Initialization

This commit is contained in:
Hugo Zilliox 2017-08-02 17:27:52 +02:00
commit 94bb6e67f6
7 changed files with 232 additions and 0 deletions

21
LICENSE Normal file
View 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
View 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
View 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

View 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');
}
});

View 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
View 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

View 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