Initialization
This commit is contained in:
commit
94bb6e67f6
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Hugo Zilliox
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
4
README.md
Normal file
4
README.md
Normal file
@ -0,0 +1,4 @@
|
||||
# redmine_issue_dynamic_edit
|
||||
|
||||
Add new dropdowns elements on detailed issue page to dynamically update issue's status, assignee and priority directly in the details block of the issue
|
||||
You have to enable Redmine REST API
|
||||
4
README.rdoc
Normal file
4
README.rdoc
Normal file
@ -0,0 +1,4 @@
|
||||
= redmine_issue_dynamic_edit
|
||||
|
||||
Add new dropdowns elements on detailed issue page to dynamically update issue's status, assignee and priority directly in the details block of the issue
|
||||
You have to enable Redmine REST API
|
||||
84
assets/javascripts/issue_dynamic_edit.js
Normal file
84
assets/javascripts/issue_dynamic_edit.js
Normal file
@ -0,0 +1,84 @@
|
||||
var cssId = 'fontAwesome';
|
||||
if (!document.getElementById(cssId))
|
||||
{
|
||||
var head = document.getElementsByTagName('head')[0];
|
||||
var link = document.createElement('link');
|
||||
link.id = cssId;
|
||||
link.rel = 'stylesheet';
|
||||
link.type = 'text/css';
|
||||
link.href = 'https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css';
|
||||
link.media = 'all';
|
||||
head.appendChild(link);
|
||||
}
|
||||
|
||||
if($('#statusListDropdown').length > 0) {
|
||||
var htmlCopy = $('#statusListDropdown').get(0).outerHTML;
|
||||
$('#statusListDropdown').remove();
|
||||
$('.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(htmlCopy);
|
||||
}
|
||||
|
||||
if($('#prioritiesListDropdown').length > 0) {
|
||||
var htmlCopy = $('#prioritiesListDropdown').get(0).outerHTML;
|
||||
$('#prioritiesListDropdown').remove();
|
||||
$('.details .attributes .priority.attribute .value').html(htmlCopy);
|
||||
}
|
||||
|
||||
function updateDataIssue(field_name, field_value, cssClass) {
|
||||
$('.details .attributes .' + cssClass + '.attribute .value').append(' <i class="fa fa-refresh fa-spin fa-fw"></i>');
|
||||
ticketData = '<?xml version="1.0" encoding="UTF-8"?>';
|
||||
ticketData += '<issue>';
|
||||
ticketData += '<id>' + _ISSUE_ID + '</id>';
|
||||
ticketData += '<' + field_name + '>'+ field_value +'</' + field_name + '>';
|
||||
ticketData += '</issue>';
|
||||
jQuery.ajax({
|
||||
type: 'PUT',
|
||||
url: '/issues/' + _ISSUE_ID + '.xml',
|
||||
crossDomain: true,
|
||||
async: false,
|
||||
contentType: "application/xml",
|
||||
data: ticketData,
|
||||
beforeSend: function(xhr) {
|
||||
xhr.setRequestHeader("X-Redmine-API-Key", _USER_API_KEY);
|
||||
},
|
||||
success: function(msg) {
|
||||
setTimeout(function(){
|
||||
$('.details .attributes .' + cssClass + '.attribute .value i.fa-spin').remove();
|
||||
$('.details .attributes .' + cssClass + '.attribute .value').append(' <i class="fa fa-check"></i>');
|
||||
setTimeout(function(){
|
||||
$('.details .attributes .' + cssClass + '.attribute .value i.fa-check').remove();
|
||||
}, 2000);
|
||||
}, 500);
|
||||
},
|
||||
error: function(xhr, msg, error) {}
|
||||
});
|
||||
} /* end function updateDataIssue */
|
||||
|
||||
var domSelectStatus = $('body').find('#statusListDropdown select');
|
||||
domSelectStatus.on('change', function(e){
|
||||
updateDataIssue('status_id', domSelectStatus.val(), 'status');
|
||||
}); /* end on change domSelectStatus */
|
||||
|
||||
var domSelectPriorities = $('body').find('#prioritiesListDropdown select');
|
||||
domSelectPriorities.on('change', function(e){
|
||||
updateDataIssue('priority_id', domSelectPriorities.val(), 'priority');
|
||||
}); /* end on change domSelectPriorities */
|
||||
|
||||
var domSelectUsers = $('body').find('#usersListDropdown select');
|
||||
domSelectUsers.on('change', function(e){
|
||||
updateDataIssue('assigned_to_id', domSelectUsers.val(), 'assigned-to');
|
||||
}); /* end on change domSelectUsers */
|
||||
|
||||
$('.details .attributes .attribute .value').on({
|
||||
mouseenter: function () {
|
||||
$(this).find('.fa-pencil').removeClass('fa-pencil').addClass('fa-angle-down');
|
||||
},
|
||||
mouseleave: function () {
|
||||
$(this).find('.fa-angle-down').removeClass('fa-angle-down').addClass('fa-pencil');
|
||||
}
|
||||
});
|
||||
31
assets/stylesheets/issue_dynamic_edit.css
Normal file
31
assets/stylesheets/issue_dynamic_edit.css
Normal file
@ -0,0 +1,31 @@
|
||||
.value .dynamicEditSelect select {
|
||||
border-color: transparent;
|
||||
border: 0;
|
||||
border-bottom: 1px solid transparent;
|
||||
border-radius: 0;
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
background: none;
|
||||
font-size: inherit;
|
||||
color: inherit;
|
||||
font-family: inherit;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.value:hover .dynamicEditSelect select {
|
||||
border-color: inherit;
|
||||
}
|
||||
|
||||
.value .dynamicEditSelect {
|
||||
position:relative;
|
||||
}
|
||||
|
||||
.value .dynamicEditSelect i.dropdown {
|
||||
position:absolute;
|
||||
right: 5px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
pointer-events: none;
|
||||
}
|
||||
12
init.rb
Normal file
12
init.rb
Normal file
@ -0,0 +1,12 @@
|
||||
require 'redmine'
|
||||
|
||||
require 'details_issue_hooks'
|
||||
|
||||
Redmine::Plugin.register :redmine_issue_dynamic_edit do
|
||||
name 'Redmine Dynamic edit Issue plugin'
|
||||
author 'Hugo Zilliox'
|
||||
description 'Allows users to dynamically update issue\'s status, assignee and priority in detailed view using REST API'
|
||||
version '0.1.0'
|
||||
url 'https://github.com/ilogeek/redmine_issue_dynamic_edit'
|
||||
author_url 'https://hzilliox.fr'
|
||||
end
|
||||
76
lib/details_issue_hooks.rb
Normal file
76
lib/details_issue_hooks.rb
Normal file
@ -0,0 +1,76 @@
|
||||
class DetailsIssueHooks < Redmine::Hook::ViewListener
|
||||
|
||||
def protect_against_forgery?
|
||||
false
|
||||
end
|
||||
|
||||
def view_layouts_base_html_head(context)
|
||||
stylesheet_link_tag('issue_dynamic_edit.css', :plugin => :redmine_issue_dynamic_edit)
|
||||
end
|
||||
|
||||
def view_layouts_base_body_bottom(context)
|
||||
javascript_include_tag('issue_dynamic_edit.js', :plugin => :redmine_issue_dynamic_edit)
|
||||
end
|
||||
|
||||
def view_issues_show_details_bottom(context = { })
|
||||
project = context[:project]
|
||||
request = context[:request]
|
||||
issue_id = request.path_parameters[:id]
|
||||
back = request.env['HTTP_REFERER']
|
||||
|
||||
if (issue_id)
|
||||
issue = Issue.find(issue_id)
|
||||
if (issue)
|
||||
if (User.current.allowed_to?(:edit_issues, project))
|
||||
o = ''
|
||||
statuses = issue.new_statuses_allowed_to(User.current)
|
||||
if (!statuses.empty?)
|
||||
o << "<span class='dynamicEditSelect' id='statusListDropdown'><select data-issue='#{issue_id}'>"
|
||||
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><i class=\"fa fa-pencil fa-fw dropdown\" aria-hidden=\"true\"></i></span>"
|
||||
end
|
||||
assignables = project.assignable_users
|
||||
if (!assignables.empty?)
|
||||
o << "<span class='dynamicEditSelect' id='usersListDropdown'><select data-issue='#{issue_id}'>"
|
||||
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><i class=\"fa fa-pencil fa-fw dropdown\" aria-hidden=\"true\"></i></span>"
|
||||
end
|
||||
|
||||
priorities = IssuePriority.all
|
||||
if(!priorities.empty?)
|
||||
o << "<span class='dynamicEditSelect' id='prioritiesListDropdown'><select data-issue='#{issue_id}'>"
|
||||
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><i class=\"fa fa-pencil fa-fw dropdown\" aria-hidden=\"true\"></i></span>"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
o << "<script>"
|
||||
|
||||
o << " var _ISSUE_ID = \"#{issue_id}\";"
|
||||
o << " var _USER_API_KEY = \"#{User.current.api_key}\";"
|
||||
|
||||
o << "</script>"
|
||||
return o
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
Loading…
x
Reference in New Issue
Block a user