0.8.0 update - Complete rework. Compatible with last Redmine version. New settings added (#70 #88). Removed external lib (FontAwesome) (#74). Mobile style added (#87). Print style added (#84). Bug fix (#79, #85)
This commit is contained in:
parent
e968ee7499
commit
ad37b96b53
@ -29,16 +29,16 @@ You can set some settings by editing the file `assets/javascripts/issue_dynamic_
|
|||||||
* **\_CONF\_LISTENER\_TYPE\_ICON** : Choose which action will trigger the apparition of the edition block when fired from the pencil icon (by default: same as **\_CONF\_LISTENER\_TYPE\_VALUE**). Allowed value : `none`, `click`, `dblclick`
|
* **\_CONF\_LISTENER\_TYPE\_ICON** : Choose which action will trigger the apparition of the edition block when fired from the pencil icon (by default: same as **\_CONF\_LISTENER\_TYPE\_VALUE**). Allowed value : `none`, `click`, `dblclick`
|
||||||
* **\_CONF\_LISTENER\_TARGET** : Choose which area 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. Eg: `TitleInput`, `DescriptionInput`, `statusListDropdown` ...
|
* **\_CONF\_EXCLUDED\_FIELD\_ID** : Choose which fields to exclude. They won't have the edit block and pencil. Eg: `TitleInput`, `DescriptionInput`, `statusListDropdown` ...
|
||||||
|
* **\_CONF\_CHECK\_ISSUE\_UPDATE\_CONFLICT** : Choose if you allow current user to override all modifications performed by other users while editing the issue
|
||||||
|
|
||||||
### 🎨 Customization
|
### 🎨 Customization
|
||||||
|
|
||||||
Feel free to edit `assets/stylesheets/issue_dynamic_edit.css` to update the look of your fields depending on your current Redmine Theme.
|
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 4.7](https://fontawesome.com/v4.7.0/)
|
|
||||||
|
|
||||||
### 🆕 Changelog
|
### 🆕 Changelog
|
||||||
|
|
||||||
* **v 0.7.2** : New settings added into config file (`\_CONF\_DISPLAY\_EDIT\_ICON` and `\_CONF\_LISTENER\_TYPE\_ICON`) see Configuration part for more info ; new event `none` for `\_CONF\_LISTENER\_TYPE\_VALUE` disabling listener on value ; css fix
|
* **v 0.8.0** : Complete rework. Compatible with last Redmine version. New settings added : `_CONF_CHECK_ISSUE_UPDATE_CONFLICT` (#70 #88). Removed external lib (FontAwesome) (#74). Mobile style added (#87). Print style added (#84). Bug fix (#79, #85)
|
||||||
|
* **v 0.7.2** : New settings added into config file (`_CONF_DISPLAY_EDIT_ICON` and `_CONF_LISTENER_TYPE_ICON`) see Configuration part for more info ; new event `none` for `_CONF_LISTENER_TYPE_VALUE` disabling listener on value ; css fix
|
||||||
* **v 0.7.1** : Fixed incorrect DOM structure if user has read only access to the issue (#61 #64)
|
* **v 0.7.1** : Fixed incorrect DOM structure if user has read only access to the issue (#61 #64)
|
||||||
* **v 0.7.0** : Category filter by project added (#55) and prevent dialog closing when using fa-pencil selector (#59)
|
* **v 0.7.0** : Category filter by project added (#55) and prevent dialog closing when using fa-pencil selector (#59)
|
||||||
* **v 0.6.9** : Category field support (Github request #54)
|
* **v 0.6.9** : Category field support (Github request #54)
|
||||||
|
|||||||
@ -4,9 +4,21 @@
|
|||||||
var _CONF_FORCE_HTTPS = _CONF_FORCE_HTTPS || false;
|
var _CONF_FORCE_HTTPS = _CONF_FORCE_HTTPS || false;
|
||||||
var _CONF_DISPLAY_EDIT_ICON = _CONF_DISPLAY_EDIT_ICON || "single";
|
var _CONF_DISPLAY_EDIT_ICON = _CONF_DISPLAY_EDIT_ICON || "single";
|
||||||
var _CONF_LISTENER_TYPE_VALUE = _CONF_LISTENER_TYPE_VALUE || "click";
|
var _CONF_LISTENER_TYPE_VALUE = _CONF_LISTENER_TYPE_VALUE || "click";
|
||||||
var _CONF_LISTENER_TYPE_ICON = _CONF_LISTENER_TYPE_ICON || "click";
|
var _CONF_LISTENER_TYPE_ICON = _CONF_LISTENER_TYPE_ICON || "none";
|
||||||
var _CONF_LISTENER_TARGET = _CONF_LISTENER_TARGET || "value";
|
var _CONF_LISTENER_TARGET = _CONF_LISTENER_TARGET || "value";
|
||||||
var _CONF_EXCLUDED_FIELD_ID = _CONF_EXCLUDED_FIELD_ID || [];
|
var _CONF_EXCLUDED_FIELD_ID = _CONF_EXCLUDED_FIELD_ID || [];
|
||||||
|
var _CONF_CHECK_ISSUE_UPDATE_CONFLICT = _CONF_CHECK_ISSUE_UPDATE_CONFLICT || false;
|
||||||
|
|
||||||
|
_CONF_LISTENER_TARGET = _CONF_LISTENER_TARGET === "all" ? "*" : _CONF_LISTENER_TARGET;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SVG ICONS
|
||||||
|
* Source : https://www.iconfinder.com/iconsets/glyphs
|
||||||
|
*/
|
||||||
|
|
||||||
|
var SVG_EDIT = '<svg style="width: 1em; height: 1em;" version="1.1" viewBox="0 0 24 24" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g class="svg_edit"><path d="m2 20c0 1.1.9 2 2 2h2.6l-4.6-4.6z"/><path d="m21.6 5.6-3.2-3.2c-.8-.8-2-.8-2.8 0l-.2.2c-.4.4-.4 1 0 1.4l4.6 4.6c.4.4 1 .4 1.4 0l.2-.2c.8-.8.8-2 0-2.8z"/><path d="m14 5.4c-.4-.4-1-.4-1.4 0l-9.1 9.1c-.5.5-.5 1.1-.1 1.5l4.6 4.6c.4.4 1 .4 1.4 0l9.1-9.1c.4-.4.4-1 0-1.4z"/></g></svg>';
|
||||||
|
var SVG_VALID = '<svg style="width: 1em; height: 1em; fill:white;" version="1.1" viewBox="0 0 24 24" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g id="info"/><g id="icons"><path d="M10,18c-0.5,0-1-0.2-1.4-0.6l-4-4c-0.8-0.8-0.8-2,0-2.8c0.8-0.8,2.1-0.8,2.8,0l2.6,2.6l6.6-6.6 c0.8-0.8,2-0.8,2.8,0c0.8,0.8,0.8,2,0,2.8l-8,8C11,17.8,10.5,18,10,18z" class="svg_check"/></g></svg>';
|
||||||
|
var SVG_CANCEL = '<svg style="width: 1em; height: 1em;" version="1.1" viewBox="0 0 24 24" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g id="info"/><g id="icons"><path d="M14.8,12l3.6-3.6c0.8-0.8,0.8-2,0-2.8c-0.8-0.8-2-0.8-2.8,0L12,9.2L8.4,5.6c-0.8-0.8-2-0.8-2.8,0 c-0.8,0.8-0.8,2,0,2.8L9.2,12l-3.6,3.6c-0.8,0.8-0.8,2,0,2.8C6,18.8,6.5,19,7,19s1-0.2,1.4-0.6l3.6-3.6l3.6,3.6 C16,18.8,16.5,19,17,19s1-0.2,1.4-0.6c0.8-0.8,0.8-2,0-2.8L14.8,12z" class="svg_cancel"/></g></svg>';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Allow inclusion from other page
|
* Allow inclusion from other page
|
||||||
@ -22,396 +34,256 @@ if (_CONF_FORCE_HTTPS) {
|
|||||||
* or if user has to hover every element to discover if (s)he can edit it
|
* or if user has to hover every element to discover if (s)he can edit it
|
||||||
*/
|
*/
|
||||||
if (_CONF_DISPLAY_EDIT_ICON === "block"){
|
if (_CONF_DISPLAY_EDIT_ICON === "block"){
|
||||||
$('body.controller-issues.action-show .issue.details').addClass('showDynamicEdit');
|
$('body.controller-issues.action-show .issue.details').addClass('showPencils');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Generate edit block */
|
||||||
|
var getEditFormHTML = function(attribute){
|
||||||
|
var formElement = $('#issue_' + attribute + "_id");
|
||||||
|
formElement = formElement.length ? formElement : $('#issue_' + attribute);
|
||||||
|
formElement = formElement.length ? formElement : $('#' + attribute);
|
||||||
|
|
||||||
|
// Checkbox specific case
|
||||||
/* FontAwesome inclusion */
|
var is_checkboxes = false;
|
||||||
var cssId = 'fontAwesome';
|
let is_file = false;
|
||||||
|
let is_list = false;
|
||||||
if (!document.getElementById(cssId)) {
|
let CF_ID = false;
|
||||||
var head = document.getElementsByTagName('head')[0];
|
if(!formElement.length && attribute.startsWith("custom_field_values_")){
|
||||||
var link = document.createElement('link');
|
CF_ID = attribute.split("custom_field_values_")[1];
|
||||||
link.id = cssId;
|
/* Is it a checkbox block ? */
|
||||||
link.rel = 'stylesheet';
|
formElement = $('#issue_custom_field_values_' + CF_ID);
|
||||||
link.type = 'text/css';
|
if(formElement.length){
|
||||||
link.href = 'https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css';
|
formElement = formElement.parents('.check_box_group');
|
||||||
link.media = 'all';
|
is_checkboxes = CF_ID;
|
||||||
head.appendChild(link);
|
|
||||||
}
|
|
||||||
|
|
||||||
function editActionHandler(e) {
|
|
||||||
$('.issue .attributes .attribute .value').removeClass('edited');
|
|
||||||
// bind click to show edit block if click inside an edit box or on trigger, except button inside edit box
|
|
||||||
if(!$(e.target).closest('a.btn.btn-primary').length &&
|
|
||||||
($(e.target).closest('.' + _CONF_LISTENER_TARGET).length ||
|
|
||||||
$(e.target).closest('span.dynamicEdit').length)
|
|
||||||
) {
|
|
||||||
$(e.target).closest('.value').addClass('edited');
|
|
||||||
}
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Listen on events on a whole line for any field
|
|
||||||
if(_CONF_LISTENER_TYPE_VALUE !== "none") {
|
|
||||||
$(document).on(_CONF_LISTENER_TYPE_VALUE, editActionHandler);
|
|
||||||
} else {
|
} else {
|
||||||
$('body.controller-issues.action-show .issue.details').addClass('no-cursor');
|
/* Is it a file block ? */
|
||||||
|
formElement = $('#issue_custom_field_values_' + CF_ID + '_blank');
|
||||||
|
if(formElement.length){
|
||||||
|
formElement = formElement.parents('p');
|
||||||
|
formElement.find('label').remove();
|
||||||
|
is_file = CF_ID;
|
||||||
|
} else {
|
||||||
|
/* Is it a checkbox/radio group ? */
|
||||||
|
formElement = $('#issue-form .cf_' + CF_ID + '.check_box_group');
|
||||||
|
is_list = CF_ID;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a supplementary type of event is set specifically for the dynamic edit icon,
|
if(formElement.length){
|
||||||
// add another listener for it
|
var clone = formElement.clone();
|
||||||
if (_CONF_LISTENER_TYPE_VALUE !== _CONF_LISTENER_TYPE_ICON && _CONF_LISTENER_TYPE_ICON !== "none") {
|
if(clone.is('select') && !clone.prop('multiple')) clone.on('change', function(e){sendData($(this).serializeArray());});
|
||||||
$(document).on(_CONF_LISTENER_TYPE_ICON, '.fa-pencil.dynamicEditIcon' , function (e) {
|
if(is_checkboxes || is_file || is_list) {
|
||||||
editActionHandler(e);
|
clone.prop('id', "issue_custom_field_values_" + CF_ID + "_dynamic");
|
||||||
|
} else {
|
||||||
|
clone.prop('id', formElement.prop('id') + "_dynamic");
|
||||||
|
}
|
||||||
|
var wrapper = $("<div/>").addClass('dynamicEditField');
|
||||||
|
wrapper.append(clone);
|
||||||
|
if(!clone.is('select') || clone.prop('multiple')) wrapper.append("<button class='action valid'><!--✓-->" + SVG_VALID + "</button>");
|
||||||
|
wrapper.append("<button class='action refuse'><!--✕-->" + SVG_CANCEL + "</button>");
|
||||||
|
return wrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Loop over all form attribute and clone them into details part */
|
||||||
|
var cloneEditForm = function(){
|
||||||
|
$('.issue.details .subject').append('<button class="refreshData">⟳</button>');
|
||||||
|
$(".issue.details ").wrap("<form id='fakeDynamicForm'>");
|
||||||
|
|
||||||
|
$('div.issue.details .attribute').each(function(){
|
||||||
|
var classList = $(this).attr('class').split(/\s+/);
|
||||||
|
|
||||||
|
var attributes = classList.filter(function(elem) { return elem != "attribute"; });
|
||||||
|
// Specific case : all "-" are replaced by "_" into form id
|
||||||
|
attributes = attributes.map(attr => attr.replaceAll('-', '_'));
|
||||||
|
|
||||||
|
let custom_field = false;
|
||||||
|
attributes.forEach(function(part, index, arr) {
|
||||||
|
if(arr[index] === "progress") arr[index] = "done_ratio";
|
||||||
|
if(arr[index].startsWith('cf_')) {
|
||||||
|
arr[index] = arr[index].replace('cf', 'custom_field_values');
|
||||||
|
custom_field = arr[index];
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
attributes = attributes.join(" ");
|
||||||
|
|
||||||
|
let selected_elt = custom_field ? custom_field : attributes;
|
||||||
|
if(attributes && !_CONF_EXCLUDED_FIELD_ID.includes(selected_elt)){
|
||||||
|
let dynamicEditField = getEditFormHTML(selected_elt);
|
||||||
|
if(dynamicEditField) $(this).find('.value').append(" <span class='iconEdit'><!--✎-->" + SVG_EDIT + "</span>").append(dynamicEditField);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
function isExcluded(elmt_id) {
|
// Specific Case : Description field
|
||||||
return _CONF_EXCLUDED_FIELD_ID.indexOf(elmt_id) > -1;
|
if(!_CONF_EXCLUDED_FIELD_ID.includes("description") && document.querySelectorAll('div.issue.details .description').length){
|
||||||
}
|
$('div.issue.details .description > p').first().find('strong').after(" <span class='iconEdit'><!--✎-->" + SVG_EDIT + "</span>");
|
||||||
|
var formDescription = getEditFormHTML("description");
|
||||||
|
formDescription.find("#issue_description_dynamic").removeAttr('data-tribute');
|
||||||
|
$('div.issue.details .description').append(formDescription);
|
||||||
|
|
||||||
function initEditFields() {
|
|
||||||
/* Put new dropdown lists in the detailed info block */
|
|
||||||
if ($('#statusListDropdown').length > 0 && !isExcluded('statusListDropdown')) {
|
|
||||||
var htmlCopy = $('#statusListDropdown').get(0).outerHTML;
|
|
||||||
$('#statusListDropdown').remove();
|
|
||||||
$('.details .attributes .status.attribute .value').html(
|
|
||||||
htmlCopy +
|
|
||||||
'<span class="showValue">' +
|
|
||||||
$('.details .attributes .status.attribute .value').html() +
|
|
||||||
'</span> <i class="fa fa-pencil dynamicEditIcon fa-fw" aria-hidden="true"></i>'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($('#prioritiesListDropdown').length > 0 && !isExcluded('prioritiesListDropdown')) {
|
|
||||||
var htmlCopy = $('#prioritiesListDropdown').get(0).outerHTML;
|
|
||||||
$('#prioritiesListDropdown').remove();
|
|
||||||
$('.details .attributes .priority.attribute .value').html(
|
|
||||||
htmlCopy +
|
|
||||||
'<span class="showValue">' +
|
|
||||||
$('.details .attributes .priority.attribute .value').html() +
|
|
||||||
'</span> <i class="fa fa-pencil dynamicEditIcon fa-fw" aria-hidden="true"></i>'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($('#categoriesListDropdown').length > 0 && !isExcluded('categoriesListDropdown')) {
|
|
||||||
var htmlCopy = $('#categoriesListDropdown').get(0).outerHTML;
|
|
||||||
$('#categoriesListDropdown').remove();
|
|
||||||
$('.details .attributes .category.attribute .value').html(
|
|
||||||
htmlCopy +
|
|
||||||
'<span class="showValue">' +
|
|
||||||
$('.details .attributes .category.attribute .value').html() +
|
|
||||||
'</span> <i class="fa fa-pencil dynamicEditIcon fa-fw" aria-hidden="true"></i>'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($('#doneRatioListDropdown').length > 0 && !isExcluded('doneRatioListDropdown')) {
|
|
||||||
var htmlCopy = $('#doneRatioListDropdown').get(0).outerHTML;
|
|
||||||
$('#doneRatioListDropdown').remove();
|
|
||||||
$('.details .attributes .progress.attribute .value').html(
|
|
||||||
htmlCopy +
|
|
||||||
'<span class="showValue">' +
|
|
||||||
$('.details .attributes .progress.attribute .value').html() + '</span> <i class="fa fa-pencil dynamicEditIcon fa-fw" aria-hidden="true"></i>'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($('#EstimatedTimeInput').length > 0 && !isExcluded('EstimatedTimeInput')) {
|
|
||||||
var htmlCopy = $('#EstimatedTimeInput').get(0).outerHTML;
|
|
||||||
$('#EstimatedTimeInput').remove();
|
|
||||||
$('.details .attributes .estimated-hours.attribute .value').html(
|
|
||||||
htmlCopy +
|
|
||||||
'<span class="showValue">' +
|
|
||||||
$('.details .attributes .estimated-hours.attribute .value').html() +
|
|
||||||
'</span> <i class="fa fa-pencil dynamicEditIcon fa-fw" aria-hidden="true"></i>'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($('#StartDateInput').length > 0 && !isExcluded('StartDateInput')) {
|
|
||||||
var htmlCopy = $('#StartDateInput').get(0).outerHTML;
|
|
||||||
$('#StartDateInput').remove();
|
|
||||||
$('.details .attributes .start-date.attribute .value').html(
|
|
||||||
htmlCopy +
|
|
||||||
'<span class="showValue">' +
|
|
||||||
$('.details .attributes .start-date.attribute .value').html() +
|
|
||||||
'</span> <i class="fa fa-pencil dynamicEditIcon fa-fw" aria-hidden="true"></i>'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($('#DueDateInput').length > 0 && !isExcluded('DueDateInput')) {
|
|
||||||
var htmlCopy = $('#DueDateInput').get(0).outerHTML;
|
|
||||||
$('#DueDateInput').remove();
|
|
||||||
$('.details .attributes .due-date.attribute .value').html(
|
|
||||||
htmlCopy +
|
|
||||||
'<span class="showValue">' +
|
|
||||||
$('.details .attributes .due-date.attribute .value').html() +
|
|
||||||
'</span> <i class="fa fa-pencil dynamicEditIcon fa-fw" aria-hidden="true"></i>'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($('#TitleInput').length > 0 && !isExcluded('TitleInput')) {
|
|
||||||
var htmlCopy = $('#TitleInput').get(0).outerHTML;
|
|
||||||
$('#TitleInput').remove();
|
|
||||||
$('.subject h3').html(
|
|
||||||
htmlCopy +
|
|
||||||
'<span class="showValue">' +
|
|
||||||
$('.subject h3').html() +
|
|
||||||
'</span> <i class="fa fa-pencil dynamicEditIcon fa-fw" aria-hidden="true"></i>'
|
|
||||||
).addClass('value');
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($('#DescriptionInput').length > 0 && !isExcluded('DescriptionInput')) {
|
|
||||||
var htmlCopy = $('#DescriptionInput').get(0).outerHTML;
|
|
||||||
$('#DescriptionInput').remove();
|
|
||||||
$('div.description .wiki').html(
|
|
||||||
htmlCopy +
|
|
||||||
' <i class="fa fa-pencil dynamicEditIcon fa-fw" aria-hidden="true" style="float:right;"></i><span class="showValue">' +
|
|
||||||
$('div.description .wiki').html() + '</span>'
|
|
||||||
).addClass('value');
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
htmlCopy = htmlCopy.replace(/id="/g, 'id="dynamic_').replace(/id='/g, "id='dynamic_");
|
|
||||||
|
|
||||||
var editHTML = "<span class='dynamicEdit' id='dynamic_edit_assigned_to_id'>";
|
|
||||||
editHTML += htmlCopy;
|
|
||||||
editHTML += " <a href='#' class='btn btn-primary close' aria-label='" + _TXT_CANCEL_BTN + "'><i class='fa fa-times fa-fw' aria-hidden='true'></i></a>";
|
|
||||||
editHTML += "</span>";
|
|
||||||
|
|
||||||
$('.details .attributes .assigned-to.attribute .value').html(
|
|
||||||
editHTML +
|
|
||||||
'<span class="showValue">' +
|
|
||||||
$('.details .attributes .assigned-to.attribute .value').html() +
|
|
||||||
'</span> <i class="fa fa-pencil dynamicEditIcon fa-fw" aria-hidden="true"></i>'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
htmlCopy = htmlCopy.replace(/id="/g, 'id="dynamic_').replace(/id='/g, "id='dynamic_");
|
|
||||||
|
|
||||||
var editHTML = "<span class='dynamicEdit' id='dynamic_edit_fixed_version'>";
|
|
||||||
editHTML += htmlCopy;
|
|
||||||
editHTML += " <a href='#' class='btn btn-primary close' aria-label='" + _TXT_CANCEL_BTN + "'><i class='fa fa-times fa-fw' aria-hidden='true'></i></a>";
|
|
||||||
editHTML += "</span>";
|
|
||||||
|
|
||||||
$('.details .attributes .fixed-version.attribute .value').html(
|
|
||||||
editHTML +
|
|
||||||
'<span class="showValue">' +
|
|
||||||
$('.details .attributes .fixed-version.attribute .value').html() +
|
|
||||||
'</span> <i class="fa fa-pencil dynamicEditIcon fa-fw" aria-hidden="true"></i>'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var i = 0 ; i < CF_VALUE_JSON.length ; i++) {
|
|
||||||
var info = CF_VALUE_JSON[i].custom_field;
|
|
||||||
var value = CF_VALUE_JSON[i].value;
|
|
||||||
|
|
||||||
if (info.visible && info.editable && !isExcluded("issue_custom_field_values_" + info.id)) {
|
|
||||||
if (
|
if (
|
||||||
$('.details .attributes .cf_' + info.id + '.attribute .value').length &&
|
typeof(CKEDITOR) === "object" &&
|
||||||
(
|
typeof(CKEDITOR.instances['issue_description'] !== "undefined") &&
|
||||||
$('#issue_custom_field_values_' + info.id).length ||
|
typeof(CKEDITOR.instances['issue_description'].getData) === typeof(Function)
|
||||||
$('input[name=issue\\[custom_field_values\\]\\[' + info.id + '\\]\\[\\]]').length
|
|
||||||
)
|
|
||||||
) {
|
) {
|
||||||
// if single input first case, else checkboxes second case
|
var cfg = CKEDITOR.instances['issue_description'].config;
|
||||||
if($('#issue_custom_field_values_' + info.id).length) {
|
cfg.height = 100;
|
||||||
var htmlCopy = $('#issue_custom_field_values_' + info.id).get(0).outerHTML;
|
CKEDITOR.replace("issue_description_dynamic", cfg)
|
||||||
|
}else if (typeof(jsToolBar) === typeof(Function)) {
|
||||||
|
var wikiToolbar = new jsToolBar(document.getElementById('issue_description_dynamic')); wikiToolbar.draw();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Specific Case : Title field
|
||||||
|
if(!_CONF_EXCLUDED_FIELD_ID.includes("subject")){
|
||||||
|
$('div.issue.details .subject h3').append(" <span class='iconEdit'><!--✎-->" + SVG_EDIT + "</span>");
|
||||||
|
var formTitle = getEditFormHTML("issue_subject");
|
||||||
|
$('div.issue.details .subject').append(formTitle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Perform action on .value (display edit form) */
|
||||||
|
$('body').on(_CONF_LISTENER_TYPE_VALUE,
|
||||||
|
'div.issue.details .attributes .attribute .' + _CONF_LISTENER_TARGET + ', div.issue.details div.description > p, div.issue.details div.subject',
|
||||||
|
function(e){
|
||||||
|
if($(e.target).closest('.dynamicEditField').length) return; /* We're already into a dynamic field, ignore */
|
||||||
|
$('.dynamicEditField').each(function(e){ $(this).removeClass('open'); });
|
||||||
|
if(!$(e.target).closest('a').length && !$(e.target).closest('button').length){
|
||||||
|
if($(this).parent().hasClass('description')){
|
||||||
|
$(this).parent().find('.dynamicEditField').addClass('open');
|
||||||
} else {
|
} else {
|
||||||
var htmlCopy = $('input[name=issue\\[custom_field_values\\]\\[' + info.id + '\\]\\[\\]]').parents('.check_box_group').get(0).outerHTML;
|
$(this).find('.dynamicEditField').addClass('open');
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// 2 technics with simple or double quote (safety first)
|
/* Perform action on .iconEdit (display edit form) */
|
||||||
htmlCopy = htmlCopy.replace(/id="/g, 'id="dynamic_').replace(/id='/g, "id='dynamic_");
|
$('body').on(_CONF_LISTENER_TYPE_ICON,
|
||||||
htmlCopy = htmlCopy.replace(/class="/g, 'class="cf_' + info.id + ' ').replace(/class='/g, "class='cf_" + info.id + " ");
|
'div.issue.details .iconEdit', function(e){
|
||||||
|
$('.dynamicEditField').each(function(e){ $(this).removeClass('open'); });
|
||||||
|
$(this).parent().find('.dynamicEditField').addClass('open');
|
||||||
|
});
|
||||||
|
|
||||||
var editHTML = "<span class='dynamicEdit " + info.field_format + "' id='dynamic_edit_cf_" + info.id + "'>";
|
/* Perform data update when clicking on valid button from edit form */
|
||||||
|
$('body').on('click', '.dynamicEditField .action.valid', function(e){
|
||||||
editHTML += htmlCopy;
|
|
||||||
editHTML += " <a href='#' class='btn btn-primary validate' aria-label='" + _TXT_VALIDATION_BTN + "'><i class='fa fa-check fa-fw' aria-hidden='true'></i></a>";
|
|
||||||
editHTML += " <a href='#' class='btn btn-primary close' aria-label='" + _TXT_CANCEL_BTN + "'><i class='fa fa-times fa-fw' aria-hidden='true'></i></a>";
|
|
||||||
editHTML += "</span>";
|
|
||||||
|
|
||||||
$('.details .attributes .cf_' + info.id + '.attribute .value').html(
|
|
||||||
editHTML +
|
|
||||||
'<span class="showValue">' +
|
|
||||||
$('.details .attributes .cf_' + info.id + '.attribute .value').html() +
|
|
||||||
'</span> <i class="fa fa-pencil dynamicEditIcon fa-fw" aria-hidden="true"></i>'
|
|
||||||
);
|
|
||||||
|
|
||||||
if (info.field_format == "date") {
|
|
||||||
if (
|
|
||||||
$('body').find('#dynamic_issue_custom_field_values_' + info.id).length &&
|
|
||||||
$('body').find('#dynamic_issue_custom_field_values_' + info.id).datepickerFallback instanceof Function &&
|
|
||||||
typeof datepickerOptions !== 'undefined'
|
|
||||||
) {
|
|
||||||
$('body').find('#dynamic_issue_custom_field_values_' + info.id).datepickerFallback(datepickerOptions);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cf_datetime = $('body').find('#dynamic_issue_custom_field_values_' + info.id);
|
|
||||||
if (info.field_format == "datetime") {
|
|
||||||
if (
|
|
||||||
cf_datetime.length &&
|
|
||||||
typeof datetimepickerOptions !== 'undefined'
|
|
||||||
) {
|
|
||||||
cf_datetime.datetimepicker(datetimepickerOptions);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
initEditFields();
|
|
||||||
|
|
||||||
/* Add required style to attributes */
|
|
||||||
function updateRequiredFields(reqFieldsArray) {
|
|
||||||
for (var i = 0; i < reqFieldsArray.length; i++) {
|
|
||||||
var htmlLabel = reqFieldsArray[i].replace(/_/g, '-');
|
|
||||||
$('.issue.details .attribute.' + htmlLabel + ' .label').html(
|
|
||||||
'<span title=\"' + _TXT_REQUIRED_FIELD + '\" class=\"field-description\">' +
|
|
||||||
$('.issue.details .attribute.' + htmlLabel + ' .label').html() +
|
|
||||||
'</span> <span class=\"required\"> *</span>'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($('#required_field_array').length) {
|
|
||||||
updateRequiredFields(JSON.parse($('#required_field_array').html()));
|
|
||||||
}
|
|
||||||
|
|
||||||
$('body.controller-issues.action-show').on('click', '.btn.close', function(e) {
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
$(e.target).closest('.value').removeClass('edited');
|
var input = $(this).parents('.dynamicEditField').find(':input');
|
||||||
return false;
|
sendData(input.serializeArray());
|
||||||
|
$(this).parents('.dynamicEditField').removeClass('open');
|
||||||
});
|
});
|
||||||
|
|
||||||
function getLastLockVersion() {
|
/* Hide edit form when clicking on cancel button */
|
||||||
var token = $("meta[name=csrf-token]").attr('content');
|
$('body').on('click', '.dynamicEditField .action.refuse', function(e){
|
||||||
var lock_version = $('#issue_lock_version').val();
|
e.preventDefault();
|
||||||
|
$(this).parents('.dynamicEditField').removeClass('open');
|
||||||
|
});
|
||||||
|
|
||||||
|
/* Update whole .details block + history + form with global refresh button */
|
||||||
|
$('body').on('click', '.refreshData', function(e){
|
||||||
|
sendData();
|
||||||
|
});
|
||||||
|
|
||||||
|
/* Listen on esc key press to close opened dialog box */
|
||||||
|
document.onkeydown = function(evt) {
|
||||||
|
evt = evt || window.event;
|
||||||
|
var isEscape = false;
|
||||||
|
if ("key" in evt) {
|
||||||
|
isEscape = (evt.key === "Escape" || evt.key === "Esc");
|
||||||
|
} else {
|
||||||
|
isEscape = (evt.keyCode === 27);
|
||||||
|
}
|
||||||
|
if (isEscape) {
|
||||||
|
$('.dynamicEditField').each(function(e){ $(this).removeClass('open'); });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let checkVersion = function(callback){
|
||||||
jQuery.ajax({
|
jQuery.ajax({
|
||||||
type: 'GET',
|
type: 'GET',
|
||||||
url: LOCATION_HREF,
|
url: LOCATION_HREF,
|
||||||
data: { "authenticity_token" : token },
|
|
||||||
crossDomain: true,
|
crossDomain: true,
|
||||||
async: false,
|
async: false,
|
||||||
beforeSend: function(xhr) {
|
|
||||||
xhr.setRequestHeader("authenticity_token", token);
|
|
||||||
},
|
|
||||||
success: function(msg) {
|
success: function(msg) {
|
||||||
parsed = $.parseHTML(msg);
|
let parsed = $.parseHTML(msg);
|
||||||
lock_version = $(parsed).find('#issue_lock_version').val();
|
let current_version = $(parsed).find('#issue_lock_version').val();
|
||||||
|
|
||||||
|
if(current_version !== $('#issue_lock_version').val()){
|
||||||
|
if(!$('#content .conflict').length){
|
||||||
|
$('#content').prepend(`
|
||||||
|
<div class="conflict">
|
||||||
|
${_TXT_CONFLICT_TITLE}
|
||||||
|
<div class="conflict-details">
|
||||||
|
<div class="conflict-journal">
|
||||||
|
<p>${_TXT_CONFLICT_TXT}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$('#content .conflict').remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(callback) callback(current_version);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return lock_version;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function issueDynamicUpdate(field_name, field_value, type, cssClass) {
|
let checkVersionInterval = false;
|
||||||
/* hide edit field */
|
let setCheckVersionInterval = function(activate){
|
||||||
$('.details .' + cssClass + ' .value').removeClass('edited');
|
if(!_CONF_CHECK_ISSUE_UPDATE_CONFLICT) return false;
|
||||||
|
if(activate && !checkVersionInterval){
|
||||||
/* add spin notification */
|
checkVersionInterval = window.setInterval(function(){ checkVersion(); }, 5000);
|
||||||
$('.details .' + cssClass + ' .value').append(' <i class="fa fa-refresh fa-spin fa-fw"></i>');
|
} else {
|
||||||
|
clearInterval(checkVersionInterval);
|
||||||
/* update value displayed */
|
checkVersionInterval = false;
|
||||||
$('.details .' + cssClass + ' .showValue').html(function() {
|
|
||||||
if (type == "select") {
|
|
||||||
return $('.details .' + cssClass + ' .value select option:selected').html()
|
|
||||||
} else if (type == "input") {
|
|
||||||
return $('.details .' + cssClass + ' .value input').val()
|
|
||||||
} else if (type == "textarea") {
|
|
||||||
return $('.details .' + cssClass + ' .value textarea').val()
|
|
||||||
} else if (type == "date") {
|
|
||||||
return "XXXX/XX/XX";
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
/* lost focus on element */
|
|
||||||
if (type != "select") {
|
|
||||||
$('.details .' + cssClass + ' .value input').blur();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setCheckVersionInterval(true);
|
||||||
|
|
||||||
|
/* Global function to perform AJAX call */
|
||||||
|
var sendData = function(serialized_data){
|
||||||
|
|
||||||
|
let updateIssue = function(serialized_data){
|
||||||
|
setCheckVersionInterval(false);
|
||||||
var token = $("meta[name=csrf-token]").attr('content');
|
var token = $("meta[name=csrf-token]").attr('content');
|
||||||
|
var params = serialized_data || [];
|
||||||
$('#issue-form').find("#issue_" + field_name).val(field_value).css({"display": "inline-block"});
|
params.push({name: '_method', value: "patch"});
|
||||||
// avoid conflict revision
|
params.push({name: 'authenticity_token', value: token})
|
||||||
var lastLockVersion = getLastLockVersion();
|
|
||||||
$('#issue_lock_version').val(lastLockVersion);
|
|
||||||
var formData = "";
|
|
||||||
|
|
||||||
// If checkbox we have to uncheck everything in the issue-form and get data from dynamic edit
|
|
||||||
if(type == "checkbox"){
|
|
||||||
formData = field_value + "&";
|
|
||||||
var cf_id = field_name.replace(/\D/g,'');
|
|
||||||
$('input[name=issue\\[custom_field_values\\]\\[' + cf_id + '\\]\\[\\]]').each(function(){
|
|
||||||
$(this).prop('checked', false);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
formData += $('#issue-form').serialize();
|
|
||||||
|
|
||||||
|
|
||||||
jQuery.ajax({
|
jQuery.ajax({
|
||||||
type: 'POST',
|
type: 'POST',
|
||||||
url: LOCATION_HREF,
|
url: LOCATION_HREF,
|
||||||
data: formData,
|
data: $.param(params),
|
||||||
beforeSend: function(xhr) {
|
|
||||||
xhr.setRequestHeader("authenticity_token", token);
|
|
||||||
},
|
|
||||||
success: function(msg) {
|
success: function(msg) {
|
||||||
/* get result page content (updated issue detail page with new status) */
|
/* get result page content (updated issue detail page with new status) */
|
||||||
|
$('#ajax-indicator').css('display', 'none');
|
||||||
|
|
||||||
var parsed = $.parseHTML(msg);
|
var parsed = $.parseHTML(msg);
|
||||||
|
|
||||||
var error = $(parsed).find("#errorExplanation");
|
var error = $(parsed).find("#errorExplanation");
|
||||||
|
|
||||||
if (error.length) {
|
if (error.length) {
|
||||||
|
|
||||||
if ($('html').find("#errorExplanation").length == 0) {
|
if ($('html').find("#errorExplanation").length == 0) {
|
||||||
$('.issue.details').before("<div id='errorExplanation'>" + error.html() + "</div>");
|
$('.issue.details').before("<div id='errorExplanation'>" + error.html() + "</div>");
|
||||||
|
|
||||||
|
$([document.documentElement, document.body]).animate({
|
||||||
|
scrollTop: $("#errorExplanation").offset().top
|
||||||
|
}, 500);
|
||||||
} else {
|
} else {
|
||||||
$('html').find("#errorExplanation").html(error.html());
|
$('html').find("#errorExplanation").html(error.html());
|
||||||
}
|
}
|
||||||
|
|
||||||
/* data updated, remove spin and add success icon for 2sec */
|
|
||||||
setTimeout(function() {
|
|
||||||
$('.details .' + cssClass + ' i.fa-spin').removeClass('fa-refresh fa-spin').addClass('fa-times statusKo');
|
|
||||||
setTimeout(function() {
|
|
||||||
$('.details .' + cssClass + ' i.fa-times.statusKo').remove();
|
|
||||||
}, 2000);
|
|
||||||
}, 500);
|
|
||||||
|
|
||||||
|
|
||||||
jQuery.ajax({
|
jQuery.ajax({
|
||||||
type: 'GET',
|
type: 'GET',
|
||||||
url: LOCATION_HREF,
|
url: LOCATION_HREF,
|
||||||
data: { "authenticity_token" : token },
|
data: { "authenticity_token" : token },
|
||||||
crossDomain: true,
|
crossDomain: true,
|
||||||
async: false,
|
async: false,
|
||||||
beforeSend: function(xhr) {
|
|
||||||
xhr.setRequestHeader("authenticity_token", token);
|
|
||||||
},
|
|
||||||
success: function(msg) {
|
success: function(msg) {
|
||||||
parsed = $.parseHTML(msg);
|
parsed = $.parseHTML(msg);
|
||||||
}
|
}
|
||||||
@ -421,34 +293,20 @@ function issueDynamicUpdate(field_name, field_value, type, cssClass) {
|
|||||||
$('html').find("#errorExplanation").remove();
|
$('html').find("#errorExplanation").remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* we update the details block */
|
|
||||||
$('div.issue.details').html( $(parsed).find('div.issue.details').html() );
|
|
||||||
$('body').find('.details .' + cssClass + ' .value').append(' <i class="fa fa-refresh fa-spin fa-fw"></i>');
|
|
||||||
|
|
||||||
/* we update form*/
|
/* we update form*/
|
||||||
$('form#issue-form').html( $(parsed).find('form#issue-form').html() );
|
$('form#issue-form').html( $(parsed).find('form#issue-form').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() );
|
||||||
|
|
||||||
/* we init edit fields */
|
/* we update the details block */
|
||||||
initEditFields();
|
$('div.issue.details').html( $(parsed).find('div.issue.details').html() );
|
||||||
initEditFieldListeners();
|
|
||||||
|
|
||||||
if ($(parsed).find('#required_field_array').length) {
|
|
||||||
updateRequiredFields(JSON.parse($(parsed).find('#required_field_array').html()));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* we update the history list */
|
/* we update the history list */
|
||||||
$('#history').append($(parsed).find('#history .journal.has-details:last-child'));
|
$('#tab-content-history').append($(parsed).find('#history .journal.has-details:last-child'));
|
||||||
|
|
||||||
/* data updated, remove spin and add success icon for 2sec */
|
/* we init edit fields */
|
||||||
setTimeout(function(){
|
cloneEditForm();
|
||||||
$('.details .' + cssClass + ' i.fa-spin').removeClass('fa-refresh fa-spin').addClass('fa-check statusOk');
|
|
||||||
setTimeout(function(){
|
|
||||||
$('.details .' + cssClass + ' i.fa-check.statusOk').remove();
|
|
||||||
}, 2000);
|
|
||||||
}, 500);
|
|
||||||
|
|
||||||
//set datepicker fallback for input type date
|
//set datepicker fallback for input type date
|
||||||
if (
|
if (
|
||||||
@ -458,249 +316,42 @@ function issueDynamicUpdate(field_name, field_value, type, cssClass) {
|
|||||||
) {
|
) {
|
||||||
$('body').find('input[type=date]').datepickerFallback(datepickerOptions);
|
$('body').find('input[type=date]').datepickerFallback(datepickerOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setCheckVersionInterval(true);
|
||||||
},
|
},
|
||||||
error: function(xhr, msg, error) {
|
error: function(xhr, msg, error) {
|
||||||
|
setCheckVersionInterval(true);
|
||||||
|
$('#ajax-indicator').css('display', 'none');
|
||||||
|
|
||||||
/* error and no update, info logged into console */
|
/* error and no update, info logged into console */
|
||||||
console.log('%c -------- Error while updating the issue attribute dynamically -------- ', 'background: #ff0000; color: white; font-weight:900');
|
console.groupCollapsed('%c -------- Error while updating the issue attribute dynamically -------- ', 'background: #ff0000; color: white; font-weight:900');
|
||||||
|
console.log("POST " + LOCATION_HREF);
|
||||||
|
console.table(params);
|
||||||
console.log('%c xhr data: ', 'background: black; color: white;');
|
console.log('%c xhr data: ', 'background: black; color: white;');
|
||||||
console.log(xhr);
|
console.log(xhr);
|
||||||
console.log('%c msg data: ', 'background: black; color: white;');
|
console.log('%c msg data: ', 'background: black; color: white;');
|
||||||
console.log(msg);
|
console.log(msg);
|
||||||
console.log('%c error data: ', 'background: black; color: white;');;
|
console.log('%c error data: ', 'background: black; color: white;');;
|
||||||
console.log(error);
|
console.log(error);
|
||||||
console.log('%c ---------------------------------------------------------- ', 'background: #ff0000; color: white; font-weight:900');
|
console.groupEnd();
|
||||||
|
|
||||||
$('.details .' + cssClass + ' i.fa-spin').removeClass('fa-refresh fa-spin').addClass('fa-times').html(" " + _TXT_ERROR_AJAX_CALL);
|
|
||||||
setTimeout(function(){
|
|
||||||
$('.details .' + cssClass + ' i.fa-times').remove();
|
|
||||||
}, 2000);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Listeners foreach attribute */
|
if(_CONF_CHECK_ISSUE_UPDATE_CONFLICT){
|
||||||
function initEditFieldListeners() {
|
checkVersion(function(current_version){
|
||||||
var domSelectStatus = $('body').find('#statusListDropdown select');
|
if(current_version == $('#issue_lock_version').val()){
|
||||||
domSelectStatus.on('change', function(e) {
|
updateIssue(serialized_data);
|
||||||
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 priority from */
|
|
||||||
$("#content > div.issue").removeClass(function (index, className) {
|
|
||||||
return (className.match (/(^|\s)priority-\S+/g) || []).join(' ');
|
|
||||||
}).addClass('priority-' + domSelectPriorities.val());
|
|
||||||
}); /* end on change domSelectPriorities */
|
|
||||||
|
|
||||||
var domSelectCategories = $('body').find('#categoriesListDropdown select');
|
|
||||||
domSelectCategories.on('change', function(e){
|
|
||||||
issueDynamicUpdate('category_id', domSelectCategories.val(), 'select', 'category');
|
|
||||||
|
|
||||||
/* update the classes priority from */
|
|
||||||
$("#content > div.issue").removeClass(function (index, className) {
|
|
||||||
return (className.match (/(^|\s)priority-\S+/g) || []).join(' ');
|
|
||||||
}).addClass('category-' + domSelectPriorities.val());
|
|
||||||
}); /* end on change domSelectCategories */
|
|
||||||
|
|
||||||
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 {
|
} else {
|
||||||
/* estimated time must be > 0 */
|
$([document.documentElement, document.body]).animate({
|
||||||
$('.estimated-hours .value').append('<span class="error"><i class="fa fa-exclamation-circle" aria-hidden="true"></i> ' + _TXT_ERROR_POSITIVE_NUMBER + '</span>');
|
scrollTop: $("#content .conflict").offset().top
|
||||||
|
}, 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
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() || $('body').find('#DueDateInput input').val() == "")
|
|
||||||
{
|
|
||||||
issueDynamicUpdate('start_date', domInputStartDate.val(), 'date', 'start-date');
|
|
||||||
} else {
|
} else {
|
||||||
/* start date must be < due date */
|
updateIssue(serialized_data);
|
||||||
$('.start-date .value').append('<span class="error"><i class="fa fa-exclamation-circle" aria-hidden="true"></i> ' + _TXT_ERROR_START_DATE + '</span>');
|
|
||||||
}
|
|
||||||
|
|
||||||
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() || $('body').find('#StartDateInput input').val() == "" )
|
|
||||||
{
|
|
||||||
issueDynamicUpdate('due_date', domInputDueDate.val(), 'date', 'due-date');
|
|
||||||
} else {
|
|
||||||
/* start date must be < due date */
|
|
||||||
$('.due-date .value').append('<span class="error"><i class="fa fa-exclamation-circle" aria-hidden="true"></i> ' + _TXT_ERROR_DUE_DATE + '</span>');
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
domInputDueDate.on('keyup', function(e){
|
|
||||||
if (e.keyCode == 13) {
|
|
||||||
$('#DueDateInput a.btn.validate').click();
|
|
||||||
}
|
|
||||||
});/* end StartDate */
|
|
||||||
|
|
||||||
var domInputTitle = $('body').find('#TitleInput input');
|
|
||||||
$('#TitleInput a.btn.validate').on('click', function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
issueDynamicUpdate('subject', domInputTitle.val(), 'input', 'subject');
|
|
||||||
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
domInputTitle.on('keyup', function(e){
|
|
||||||
if (e.keyCode == 13) {
|
|
||||||
$('#TitleInput a.btn.validate').click();
|
|
||||||
}
|
|
||||||
});/* end Title */
|
|
||||||
|
|
||||||
var domInputDescription = $('body').find('#DescriptionInput textarea');
|
|
||||||
|
|
||||||
if(domInputDescription.length) {
|
|
||||||
|
|
||||||
if (
|
|
||||||
typeof(CKEDITOR) === "object" &&
|
|
||||||
typeof(CKEDITOR.instances['issue_description'].getData) === typeof(Function)
|
|
||||||
) {
|
|
||||||
var cfg = CKEDITOR.instances['issue_description'].config;
|
|
||||||
cfg.height = 100;
|
|
||||||
CKEDITOR.replace("description_textarea", cfg)
|
|
||||||
}else if (typeof(jsToolBar) === typeof(Function)) {
|
|
||||||
var wikiToolbar = new jsToolBar(document.getElementById('description_textarea')); wikiToolbar.draw();
|
|
||||||
}
|
|
||||||
|
|
||||||
$('#DescriptionInput a.btn.validate').on('click', function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
var new_value = domInputDescription.val();
|
|
||||||
|
|
||||||
if (
|
|
||||||
typeof(CKEDITOR) === "object" &&
|
|
||||||
typeof(CKEDITOR.instances['description_textarea'].getData) === typeof(Function)
|
|
||||||
) {
|
|
||||||
new_value = CKEDITOR.instances['description_textarea'].getData();
|
|
||||||
}
|
|
||||||
|
|
||||||
issueDynamicUpdate('description', new_value, 'textarea', 'description');
|
|
||||||
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
var dynamic_edit_assigned_to_id = $('body').find('#dynamic_edit_assigned_to_id select');
|
|
||||||
|
|
||||||
if (dynamic_edit_assigned_to_id.length) {
|
|
||||||
dynamic_edit_assigned_to_id.on('change', function(e){
|
|
||||||
issueDynamicUpdate('assigned_to_id', dynamic_edit_assigned_to_id.val(), 'select', 'assigned-to');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
var dynamic_edit_fixed_version = $('body').find('#dynamic_edit_fixed_version select');
|
|
||||||
|
|
||||||
if (dynamic_edit_fixed_version.length) {
|
|
||||||
dynamic_edit_fixed_version.on('change', function(e){
|
|
||||||
issueDynamicUpdate('fixed_version_id', dynamic_edit_fixed_version.val(), 'select', 'fixed-version');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/* end Description */
|
|
||||||
|
|
||||||
/* Custom fields */
|
|
||||||
for (var i = 0 ; i < CF_VALUE_JSON.length ; i++) {
|
|
||||||
(
|
|
||||||
function() {
|
|
||||||
var info = CF_VALUE_JSON[i].custom_field;
|
|
||||||
var value = CF_VALUE_JSON[i].value;
|
|
||||||
|
|
||||||
if (info.visible && info.editable) {
|
|
||||||
var inputType = "input";
|
|
||||||
switch (info.field_format) {
|
|
||||||
case "bool":
|
|
||||||
case "user":
|
|
||||||
case "list":
|
|
||||||
case "enumeration":
|
|
||||||
case "version":
|
|
||||||
inputType = "select";
|
|
||||||
break;
|
|
||||||
case "text":
|
|
||||||
inputType = "textarea";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(info.format_store.edit_tag_style == "check_box"){
|
|
||||||
inputType = "checkbox";
|
|
||||||
}
|
|
||||||
|
|
||||||
var domInputField = $('body').find('#dynamic_issue_custom_field_values_' + info.id);
|
|
||||||
$('body').find('#dynamic_edit_cf_' + info.id + ' a.btn.validate').on('click', function(e) {
|
|
||||||
var new_value = domInputField.val();
|
|
||||||
|
|
||||||
// Specific case with checkboxes
|
|
||||||
if(typeof new_value === 'undefined'){
|
|
||||||
var new_value = $('body').find('#dynamic_edit_cf_' + info.id + " :input").serialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
issueDynamicUpdate('custom_field_values_' + info.id , new_value, inputType, 'cf_' + info.id);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
domInputField.on('keyup', function(e) {
|
|
||||||
if (e.keyCode == 13 && inputType != "textarea") {
|
|
||||||
$('body').find('#dynamic_edit_cf_' + info.id + ' a.btn.validate').click();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
); // closure FTW
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
initEditFieldListeners();
|
// Init plugin
|
||||||
|
cloneEditForm();
|
||||||
@ -36,15 +36,25 @@ var _CONF_LISTENER_TYPE_ICON = "click";
|
|||||||
/*
|
/*
|
||||||
* _CONF_LISTENER_TARGET (string)
|
* _CONF_LISTENER_TARGET (string)
|
||||||
* Choose which area will trigger the apparition of the edition block
|
* 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
|
* "value" will target the value of the attribute (+ pencil), "iconEdit" will only target the pencil icon, "label" will trigger the label attribute
|
||||||
* Allowed values : value (default), fa-pencil
|
* "all" will target the whole line (label + value + pencil)
|
||||||
|
* Allowed values : value (default), iconEdit, label, all
|
||||||
*/
|
*/
|
||||||
var _CONF_LISTENER_TARGET = "value";
|
var _CONF_LISTENER_TARGET = "value";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* _CONF_EXCLUDED_FIELD_ID (string array)
|
* _CONF_EXCLUDED_FIELD_ID (string array)
|
||||||
* Choose which fields to exclude. They won't have the edit block and pencil
|
* 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
|
* You have to take element (input, select, textarea ...) class attribute from edit form at the bottom of the page
|
||||||
* Allowed values : array of any ID selector (css). Eg : ["statusListDropdown", "StartDateInput", "TitleInput", "issue_custom_field_values_4"]
|
* Custom fields have an unique ID and this ID must be prefixed by "custom_field_values_". Eg : "custom_field_values_4" is an allowed value
|
||||||
|
* Allowed values : array of any ID selector (css). Eg : ["status", "priority", "category", "assigned_to", "done_ratio", "start_date", "custom_field_values_4"]
|
||||||
*/
|
*/
|
||||||
var _CONF_EXCLUDED_FIELD_ID = [];
|
var _CONF_EXCLUDED_FIELD_ID = [];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* _CONF_CHECK_ISSUE_UPDATE_CONFLICT (boolean)
|
||||||
|
* Choose if you allow current user to override all modifications performed by other users while editing the issue
|
||||||
|
* true : will check issue update conflict and prevent current user to update the issue without refreshing the page
|
||||||
|
* false : user will be able to update the issue no matter other modification performed (will override modification made by other)
|
||||||
|
*/
|
||||||
|
var _CONF_CHECK_ISSUE_UPDATE_CONFLICT = true;
|
||||||
|
|||||||
@ -1,17 +1,20 @@
|
|||||||
/* prefix selector with body.controller-issues.action-show to avoid unwanted style on other page ? */
|
/* prefix selector with body.controller-issues.action-show to avoid unwanted style on other page ? */
|
||||||
|
|
||||||
body.controller-issues.action-show div.issue.details .value, body.controller-issues.action-show div.issue.details .splitcontent {
|
body.controller-issues.action-show div.issue.details .value, body.controller-issues.action-show div.issue.details .attributes .attribute .value {
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.controller-issues.action-show .issue.details div.description,
|
||||||
|
body.controller-issues.action-show .issue.details div.subject {
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.controller-issues.action-show div.issue .attribute .value .percent {
|
body.controller-issues.action-show .issue.details.no-cursor .value {
|
||||||
display: inline-block;
|
cursor: initial;
|
||||||
}
|
|
||||||
|
|
||||||
/* Progress bar fix */
|
|
||||||
body.controller-issues.action-show table.progress {
|
|
||||||
margin-right: 10px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
body.controller-issues.action-show .attribute .error {
|
body.controller-issues.action-show .attribute .error {
|
||||||
@ -19,28 +22,39 @@ body.controller-issues.action-show .attribute .error {
|
|||||||
margin-left: 3px;
|
margin-left: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.controller-issues.action-show .fa-pencil {
|
body.controller-issues.action-show div.issue.details .subject {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.controller-issues.action-show div.issue.details .subject .refreshData {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.controller-issues.action-show div.issue.details .splitcontent {
|
||||||
|
overflow: initial;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.controller-issues.action-show div.issue.details .iconEdit {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
display: inline-block;
|
||||||
|
transition: opacity .3s ease-in;
|
||||||
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.controller-issues.action-show .issue.details .showValue {
|
body.controller-issues.action-show div.issue.details.showPencil .iconEdit,
|
||||||
cursor: pointer;
|
body.controller-issues.action-show div.issue.details .attribute:hover .iconEdit,
|
||||||
}
|
body.controller-issues.action-show div.issue.details .description:hover .iconEdit,
|
||||||
|
body.controller-issues.action-show div.issue.details .subject:hover .iconEdit {
|
||||||
body.controller-issues.action-show .issue.details.no-cursor .showValue {
|
|
||||||
cursor: initial;
|
|
||||||
}
|
|
||||||
|
|
||||||
body.controller-issues.action-show .issue.details .value:hover .fa-pencil,
|
|
||||||
body.controller-issues.action-show .issue.details.showDynamicEdit:hover .value .fa-pencil {
|
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
cursor: pointer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
body.controller-issues.action-show .dynamicEdit {
|
body.controller-issues.action-show .dynamicEditField {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
position:absolute;
|
position:absolute;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
transition: opacity .3s ease-in;
|
||||||
/* left:0; correct position on top of icon */
|
/* left:0; correct position on top of icon */
|
||||||
bottom: 100%;
|
bottom: 100%;
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
@ -54,39 +68,36 @@ body.controller-issues.action-show .dynamicEdit {
|
|||||||
white-space: nowrap; /* force icons to stay on the same line */
|
white-space: nowrap; /* force icons to stay on the same line */
|
||||||
}
|
}
|
||||||
|
|
||||||
body.controller-issues.action-show #TitleInput.dynamicEdit {
|
body.controller-issues.action-show .subject .dynamicEditField {
|
||||||
font-size: 0.57em;
|
bottom: initial;
|
||||||
|
top:100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.controller-issues.action-show .dynamicEdit.version {
|
body.controller-issues.action-show .dynamicEditField.open {
|
||||||
max-width: 100%;
|
opacity: 1;
|
||||||
}
|
z-index: 999;
|
||||||
|
|
||||||
body.controller-issues.action-show .edited .dynamicEdit {
|
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.controller-issues.action-show div.issue.details .value.edited .dynamicEdit {
|
body.controller-issues.action-show #TitleInput.dynamicEditField {
|
||||||
opacity: 1;
|
font-size: 0.57em;
|
||||||
z-index: 999;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
body.controller-issues.action-show div.issue.details .btn-primary {
|
body.controller-issues.action-show .dynamicEditField button.action {
|
||||||
color: white !important;
|
margin-left: .5em;
|
||||||
border-radius: 3px;
|
cursor: pointer;
|
||||||
padding: 3px;
|
border: 0;
|
||||||
vertical-align: middle;
|
background-color: #efefef;
|
||||||
|
line-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.controller-issues.action-show div.issue.details .btn-primary.close {
|
body.controller-issues.action-show .dynamicEditField button.action.valid {
|
||||||
background: #c0392b;
|
font-weight: 700;
|
||||||
|
background-color: #27ae60;
|
||||||
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.controller-issues.action-show div.issue.details .btn-primary.validate {
|
body.controller-issues.action-show .dynamicEditField select {
|
||||||
background: #27ae60;
|
|
||||||
}
|
|
||||||
|
|
||||||
body.controller-issues.action-show .dynamicEdit select {
|
|
||||||
border:none;
|
border:none;
|
||||||
background-image:none;
|
background-image:none;
|
||||||
background-color:transparent;
|
background-color:transparent;
|
||||||
@ -99,41 +110,77 @@ body.controller-issues.action-show .dynamicEdit select {
|
|||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.controller-issues.action-show .dynamicEdit select option[disabled="disabled"]
|
body.controller-issues.action-show .dynamicEditField select option[disabled="disabled"] {
|
||||||
{
|
|
||||||
display:none;
|
display:none;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.controller-issues.action-show div.issue.details .attributes {
|
body.controller-issues.action-show div.issue div.subject h3 {
|
||||||
display: table;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
body.controller-issues.action-show div.issue.details .splitcontent {
|
|
||||||
display: table-row;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.issue div.subject h3 {
|
|
||||||
position:relative;
|
position:relative;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.controller-issues.action-show .dynamicEdit input[type="text"] {
|
body.controller-issues.action-show .dynamicEditField input[type="text"] {
|
||||||
width: auto !important;
|
width: auto !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.controller-issues.action-show .dynamicEdit textarea {
|
body.controller-issues.action-show .dynamicEditField textarea {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: block;
|
display: block;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.controller-issues.action-show .dynamicEdit input,
|
body.controller-issues.action-show .dynamicEditField input,
|
||||||
body.controller-issues.action-show .dynamicEdit select {
|
body.controller-issues.action-show .dynamicEditField select {
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.controller-issues.action-show .dynamicEdit .check_box_group {
|
body.controller-issues.action-show .dynamicEditField .check_box_group {
|
||||||
border: 0px !important;
|
border: 0px !important;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* MOBILE MEDIAQUERY */
|
||||||
|
@media screen and (max-width: 899px){
|
||||||
|
|
||||||
|
body.controller-issues.action-show div.issue.details .subject {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.controller-issues.action-show div.issue.details .subject .refreshData {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.controller-issues.action-show div.issue.details .value, body.controller-issues.action-show div.issue.details .attributes .attribute .value {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.controller-issues.action-show .dynamicEditField {
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
margin: 10px 0;
|
||||||
|
white-space: normal;
|
||||||
|
height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.controller-issues.action-show .dynamicEditField.open {
|
||||||
|
height: auto;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* PRINT MEDIAQUERY */
|
||||||
|
@media print {
|
||||||
|
body.controller-issues.action-show div.issue.details .subject .refreshData,
|
||||||
|
body.controller-issues.action-show div.issue.details .iconEdit,
|
||||||
|
body.controller-issues.action-show .dynamicEditField {
|
||||||
|
display : none !important;
|
||||||
|
height: 0;
|
||||||
|
width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
padding : 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,9 +0,0 @@
|
|||||||
# Translation by Peter Pfläging <peter@pflaeging.net>
|
|
||||||
de:
|
|
||||||
ide_txt_validation_btn : 'Änderung bestätitgen'
|
|
||||||
ide_txt_error_positive_number : 'Hier muss eine positive Zahl stehen'
|
|
||||||
ide_txt_error_start_date : 'Das Startdatum muss vor dem Enddatum liegen'
|
|
||||||
ide_txt_error_due_date : 'Das Enddatum muss nach dem Startdatum liegen'
|
|
||||||
ide_txt_error_ajax_call : 'Error (Javascript Konsole kontrollieren)'
|
|
||||||
ide_txt_cancel_btn : 'Änderungen ignorieren'
|
|
||||||
ide_txt_required_field : 'Pflichtfeld bitte ausfüllen'
|
|
||||||
@ -1,8 +1,3 @@
|
|||||||
en:
|
en:
|
||||||
ide_txt_validation_btn : 'Validate the modification'
|
ide_txt_notice_conflict_title : "This issue has been updated by someone else while you are editing it."
|
||||||
ide_txt_error_positive_number : 'It must be a positive number'
|
ide_txt_notice_conflict_text : "<a href='#' class='refreshData'>Refresh the current page</a> to get the updated data. <strong>Your current modifications will be lost</strong>"
|
||||||
ide_txt_error_start_date : 'Start date must be anterior to due date'
|
|
||||||
ide_txt_error_due_date : 'Due date must be posterior to start date'
|
|
||||||
ide_txt_error_ajax_call : 'Error (check your JS console)'
|
|
||||||
ide_txt_cancel_btn : 'Cancel the modification'
|
|
||||||
ide_txt_required_field : 'This field is required to go to next step'
|
|
||||||
@ -1,8 +1,3 @@
|
|||||||
fr:
|
fr:
|
||||||
ide_txt_validation_btn : 'Valider la modification'
|
ide_txt_notice_conflict_title : "La demande a été mise à jour pendant que vous la modifiez."
|
||||||
ide_txt_error_positive_number : 'Le nombre doit être positif'
|
ide_txt_notice_conflict_text : "<a href='#' class='refreshData'>Rafraichissez la page</a> pour voir une version à jour de la demande. <strong>Vos modifications actuelles seront perdues</strong>"
|
||||||
ide_txt_error_start_date : "La date de début doit être antérieure à la date d'échéance"
|
|
||||||
ide_txt_error_due_date : "La date d'échéance doit être postérieure à la date de début"
|
|
||||||
ide_txt_error_ajax_call : 'Erreur (logs dans la console JS)'
|
|
||||||
ide_txt_cancel_btn : 'Annuler la modification'
|
|
||||||
ide_txt_required_field : 'Ce champs est obligatoire pour passer au Statut suivant'
|
|
||||||
2
init.rb
2
init.rb
@ -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 without refreshing the page (JIRA style)'
|
description 'Allows users to dynamically update issue attributes in detailed view without refreshing the page (JIRA style)'
|
||||||
version '0.7.2'
|
version '0.8.0'
|
||||||
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
|
||||||
|
|||||||
@ -24,238 +24,14 @@ class DetailsIssueHooks < Redmine::Hook::ViewListener
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def view_issues_show_details_bottom(context = {})
|
def view_issues_show_details_bottom(context)
|
||||||
project = context[:project]
|
if User.current.allowed_to?(:edit_issues, context[:project])
|
||||||
request = context[:request]
|
content = "<script>\n"
|
||||||
issue_id = request.path_parameters[:id]
|
content << " var _TXT_CONFLICT_TITLE = \"" + l(:ide_txt_notice_conflict_title) + "\";\n"
|
||||||
back = request.env['HTTP_REFERER']
|
content << " var _TXT_CONFLICT_TXT = \"" + l(:ide_txt_notice_conflict_text) + "\";\n"
|
||||||
|
content << "</script>\n"
|
||||||
o = ''
|
return content.html_safe
|
||||||
|
|
||||||
if issue_id
|
|
||||||
issue = Issue.find(issue_id)
|
|
||||||
readOnlyAttributes = issue.read_only_attribute_names(User.current)
|
|
||||||
requiredAttributes = issue.required_attribute_names(User.current)
|
|
||||||
|
|
||||||
# o << requiredAttributes.to_json
|
|
||||||
|
|
||||||
allRequiredFieldsFilled = true
|
|
||||||
requiredAttributes.each do |attr|
|
|
||||||
if issue.read_attribute(attr).to_s.empty?
|
|
||||||
allRequiredFieldsFilled = false
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if issue
|
|
||||||
if User.current.allowed_to?(:edit_issues, project)
|
|
||||||
# if there's a JS error, we hide the generated values
|
|
||||||
o << '<div style="display:none">'
|
|
||||||
|
|
||||||
# Status dropdown
|
|
||||||
statuses = issue.new_statuses_allowed_to(User.current)
|
|
||||||
if (
|
|
||||||
!statuses.empty? &&
|
|
||||||
!readOnlyAttributes.include?('status_id') &&
|
|
||||||
allRequiredFieldsFilled
|
|
||||||
)
|
|
||||||
o << "<span class='dynamicEdit' id='statusListDropdown'>"
|
|
||||||
o << "<select data-issue='#{issue_id}'>"
|
|
||||||
o << "<option disabled='disabled' selected> </option>"
|
|
||||||
statuses.each do |s|
|
|
||||||
if (s != issue.status)
|
|
||||||
o << "<option value='#{s.id}'>#{s.name}</option>"
|
|
||||||
else
|
|
||||||
o << "<option value='#{s.id}' selected>#{s.name}</option>"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
o << "</select>"
|
|
||||||
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>"
|
|
||||||
end
|
|
||||||
|
|
||||||
# Users dropdown
|
|
||||||
assignables = project.assignable_users
|
|
||||||
if (
|
|
||||||
!assignables.empty? &&
|
|
||||||
!readOnlyAttributes.include?('assigned_to_id')
|
|
||||||
)
|
|
||||||
o << "<span class='dynamicEdit' id='usersListDropdown'>"
|
|
||||||
o << "<select data-issue='#{issue_id}'>"
|
|
||||||
o << "<option disabled='disabled' selected> </option>"
|
|
||||||
|
|
||||||
assignables.each do |u|
|
|
||||||
if (u != issue.assigned_to)
|
|
||||||
o << "<option value='#{u.id}'>#{u.name}</option>"
|
|
||||||
else
|
|
||||||
o << "<option value='#{u.id}' selected>#{u.name}</option>"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
o << "</select>"
|
|
||||||
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>"
|
|
||||||
end
|
|
||||||
|
|
||||||
# Priorities dropdown
|
|
||||||
priorities = IssuePriority.all
|
|
||||||
|
|
||||||
if !priorities.empty? && !(readOnlyAttributes.include? 'priority_id')
|
|
||||||
o << "<span class='dynamicEdit' id='prioritiesListDropdown'>"
|
|
||||||
o << "<select data-issue='#{issue_id}'>"
|
|
||||||
o << "<option disabled='disabled' selected> </option>"
|
|
||||||
|
|
||||||
priorities.each do |p|
|
|
||||||
if (p != issue.priority)
|
|
||||||
o << "<option value='#{p.id}'>#{p.name}</option>"
|
|
||||||
else
|
|
||||||
o << "<option value='#{p.id}' selected>#{p.name}</option>"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
o << "</select>"
|
|
||||||
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>"
|
|
||||||
end
|
|
||||||
|
|
||||||
# Categories dropdown
|
|
||||||
categories = project.issue_categories
|
|
||||||
|
|
||||||
if !categories.empty? && !(readOnlyAttributes.include? 'category_id')
|
|
||||||
o << "<span class='dynamicEdit' id='categoriesListDropdown'>"
|
|
||||||
o << "<select data-issue='#{issue_id}'>"
|
|
||||||
o << "<option value='' selected> </option>"
|
|
||||||
|
|
||||||
categories.each do |c|
|
|
||||||
if (c != issue.category)
|
|
||||||
o << "<option value='#{c.id}'>#{c.name}</option>"
|
|
||||||
else
|
|
||||||
o << "<option value='#{c.id}' selected>#{c.name}</option>"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
o << "</select>"
|
|
||||||
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>"
|
|
||||||
end
|
|
||||||
|
|
||||||
# %done dropdown
|
|
||||||
if ! readOnlyAttributes.include?('done_ratio')
|
|
||||||
percent = 0
|
|
||||||
o << "<span class='dynamicEdit' id='doneRatioListDropdown'>"
|
|
||||||
o << "<select data-issue='#{issue_id}'>"
|
|
||||||
o << "<option disabled='disabled' selected> </option>"
|
|
||||||
|
|
||||||
loop do
|
|
||||||
if percent == issue.done_ratio
|
|
||||||
o << "<option value='#{percent}' selected>#{percent}%</option>"
|
|
||||||
else
|
|
||||||
o << "<option value='#{percent}'>#{percent}%</option>"
|
|
||||||
end
|
|
||||||
percent += 10
|
|
||||||
|
|
||||||
break if percent == 110
|
|
||||||
end
|
|
||||||
|
|
||||||
o << "</select>"
|
|
||||||
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>"
|
|
||||||
end
|
|
||||||
|
|
||||||
# Estimated_time dropdown
|
|
||||||
if ! readOnlyAttributes.include?('estimated_hours')
|
|
||||||
o << "<span class='dynamicEdit' id='EstimatedTimeInput'>"
|
|
||||||
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 close' aria-label='" + l(:ide_txt_cancel_btn) + "'><i class='fa fa-times fa-fw' aria-hidden='true'></i></a>"
|
|
||||||
o << "</span>"
|
|
||||||
end
|
|
||||||
|
|
||||||
# Start date
|
|
||||||
if ! readOnlyAttributes.include?('start_date')
|
|
||||||
o << "<span class='dynamicEdit' id='StartDateInput'>"
|
|
||||||
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 close' aria-label='" + l(:ide_txt_cancel_btn) + "'><i class='fa fa-times fa-fw' aria-hidden='true'></i></a>"
|
|
||||||
o << "</span>"
|
|
||||||
o << "<script>"
|
|
||||||
o << "//<![CDATA[\n"
|
|
||||||
o << " if(typeof datepickerOptions !== 'undefined'){\n"
|
|
||||||
o << " $(function() { $('#StartDateInput input').addClass('date').datepickerFallback(datepickerOptions); });\n"
|
|
||||||
o << " }\n"
|
|
||||||
o << "//]]>\n"
|
|
||||||
o << "</script>"
|
|
||||||
end
|
|
||||||
|
|
||||||
# Due date
|
|
||||||
if ! readOnlyAttributes.include?('due_date')
|
|
||||||
o << "<span class='dynamicEdit' id='DueDateInput'>"
|
|
||||||
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 close' aria-label='" + l(:ide_txt_cancel_btn) + "'><i class='fa fa-times fa-fw' aria-hidden='true'></i></a>"
|
|
||||||
o << "</span>"
|
|
||||||
o << "<script>"
|
|
||||||
o << "//<![CDATA[\n"
|
|
||||||
o << " if(typeof datepickerOptions !== 'undefined'){\n"
|
|
||||||
o << " $(function() { $('#DueDateInput input').addClass('date').datepickerFallback(datepickerOptions); });\n"
|
|
||||||
o << " }\n"
|
|
||||||
o << "//]]>\n"
|
|
||||||
o << "</script>"
|
|
||||||
end
|
|
||||||
|
|
||||||
# Title
|
|
||||||
# Make quotings in subject! (PP)
|
|
||||||
clonesubject = issue.subject.gsub('"','"')
|
|
||||||
o << "<span class='dynamicEdit' id='TitleInput'>"
|
|
||||||
o << " <input size=\"50\" value=\"#{clonesubject}\" type=\"text\">"
|
|
||||||
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 << "<script>"
|
|
||||||
o << "//<![CDATA[\n"
|
|
||||||
o << " function resizeTitleInput() {\n"
|
|
||||||
o << " $('#TitleInput input')[0].size = Math.max(50, Math.trunc(window.innerWidth / 11));\n"
|
|
||||||
o << " }\n"
|
|
||||||
o << " resizeTitleInput();\n"
|
|
||||||
o << " window.addEventListener('resize', resizeTitleInput);\n"
|
|
||||||
o << "//]]>\n"
|
|
||||||
o << "</script>"
|
|
||||||
o << "</span>"
|
|
||||||
|
|
||||||
# Description
|
|
||||||
o << "<span class='dynamicEdit' id='DescriptionInput'>"
|
|
||||||
o << " <textarea name='description' id='description_textarea' cols='60' rows='10' style='width:calc(100\% - 10px)'>#{issue.description}</textarea>"
|
|
||||||
o << " <div style='display:block; text-align:right; margin-top:10px;'><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></div>"
|
|
||||||
o << "</span>"
|
|
||||||
|
|
||||||
# JS Part at the end of the edit block
|
|
||||||
o << "<script>"
|
|
||||||
|
|
||||||
o << " var CF_VALUE_JSON = " + issue.editable_custom_field_values(User.current).to_json + ";\n"
|
|
||||||
|
|
||||||
o << " var _ISSUE_ID = \"#{issue_id}\";\n"
|
|
||||||
o << " var _USER_API_KEY = \"#{User.current.api_key}\";\n"
|
|
||||||
o << " var _BASE_REDMINE_PATH = \"#{Redmine::Utils.relative_url_root}\";\n"
|
|
||||||
|
|
||||||
# Translations text
|
|
||||||
o << " var _TXT_VALIDATION_BTN = \"" + l(:ide_txt_validation_btn) + "\";\n"
|
|
||||||
o << " var _TXT_CANCEL_BTN = \"" + l(:ide_txt_cancel_btn) + "\";\n"
|
|
||||||
o << " var _TXT_ERROR_POSITIVE_NUMBER = \"" + l(:ide_txt_error_positive_number) + "\";\n"
|
|
||||||
o << " var _TXT_ERROR_START_DATE = \"" + l(:ide_txt_error_start_date) + "\";\n"
|
|
||||||
o << " var _TXT_ERROR_DUE_DATE = \"" + l(:ide_txt_error_due_date) + "\";\n"
|
|
||||||
o << " var _TXT_ERROR_AJAX_CALL = \"" + l(:ide_txt_error_ajax_call) + "\";\n"
|
|
||||||
o << " var _TXT_REQUIRED_FIELD = \"" + l(:ide_txt_required_field) + "\";\n"
|
|
||||||
o << "</script>\n"
|
|
||||||
|
|
||||||
o << "<div style='display:none' id='required_field_array'>#{requiredAttributes.to_json}</div>\n"
|
|
||||||
|
|
||||||
# closing the display none div parent
|
|
||||||
o << "</div>"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return o
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user