redmine_version_priorities 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/COPYRIGHT.txt +18 -0
- data/CREDITS.txt +4 -0
- data/GPL.txt +339 -0
- data/README.rdoc +34 -0
- data/Rakefile +39 -0
- data/VERSION +1 -0
- data/app/controllers/version_priorities_controller.rb +25 -0
- data/app/views/version_priorities/_prioritized_versions.html.erb +9 -0
- data/app/views/version_priorities/_version.html.erb +8 -0
- data/app/views/version_priorities/show.html.erb +30 -0
- data/assets/javascripts/jquery-1.4.2.min.js +154 -0
- data/assets/javascripts/jquery-ui-1.8.1.custom.min.js +241 -0
- data/assets/javascripts/jquery.ajax_queue.js +59 -0
- data/assets/javascripts/redmine_version_priorities.js +63 -0
- data/assets/stylesheets/redmine_version_priorities.css +17 -0
- data/config/locales/en.yml +5 -0
- data/config/routes.rb +3 -0
- data/init.rb +38 -0
- data/lang/en.yml +2 -0
- data/lib/redmine_version_priorities/patches/issue_patch.rb +27 -0
- data/lib/redmine_version_priorities/patches/query_patch.rb +37 -0
- data/lib/redmine_version_priorities/patches/version_patch.rb +50 -0
- data/test/functional/version_priorities_controller.rb +4 -0
- data/test/integration/edit_version_priorities_test.rb +79 -0
- data/test/integration/view_version_priorities_test.rb +90 -0
- data/test/test_helper.rb +52 -0
- data/test/unit/lib/redmine_version_priorities/patches/issue_patch_test.rb +39 -0
- data/test/unit/lib/redmine_version_priorities/patches/query_patch_test.rb +17 -0
- data/test/unit/lib/redmine_version_priorities/patches/version_patch_test.rb +17 -0
- metadata +96 -0
@@ -0,0 +1,63 @@
|
|
1
|
+
jQuery(function($) {
|
2
|
+
$("#ajax-indicator").ajaxStart(function(){ $(this).show(); });
|
3
|
+
$("#ajax-indicator").ajaxStop(function(){ $(this).hide(); });
|
4
|
+
|
5
|
+
attachSortables = function() {
|
6
|
+
if (window.admin != true) {
|
7
|
+
return false;
|
8
|
+
}
|
9
|
+
|
10
|
+
attachPrioritizedSortables();
|
11
|
+
attachUnPrioritizedSortables();
|
12
|
+
},
|
13
|
+
|
14
|
+
attachPrioritizedSortables = function() {
|
15
|
+
$('#prioritized-versions ol').sortable({
|
16
|
+
cancel: 'a',
|
17
|
+
connectWith: ["#unprioritized-versions ul"],
|
18
|
+
placeholder: 'drop-accepted',
|
19
|
+
dropOnEmpty: true,
|
20
|
+
// Allow drag and drop inside the list
|
21
|
+
update: function (event, ui) {
|
22
|
+
if (ui.sender == null && event.target == this) {
|
23
|
+
saveOrder();
|
24
|
+
}
|
25
|
+
},
|
26
|
+
receive: function (event, ui) {
|
27
|
+
saveOrder();
|
28
|
+
}
|
29
|
+
});
|
30
|
+
},
|
31
|
+
|
32
|
+
attachUnPrioritizedSortables = function() {
|
33
|
+
$('#unprioritized-versions ul').sortable({
|
34
|
+
cancel: 'a',
|
35
|
+
connectWith: ["#prioritized-versions ol"],
|
36
|
+
placeholder: 'drop-accepted',
|
37
|
+
dropOnEmpty: true
|
38
|
+
});
|
39
|
+
|
40
|
+
},
|
41
|
+
|
42
|
+
saveOrder = function() {
|
43
|
+
var data = $('#prioritized-versions ol').sortable('serialize')
|
44
|
+
|
45
|
+
$.ajaxQueue.put('version_priorities.js', {
|
46
|
+
data: addAuthenticityToken(data),
|
47
|
+
success: function(response) {
|
48
|
+
$('#prioritized-versions').html(response);
|
49
|
+
attachPrioritizedSortables();
|
50
|
+
},
|
51
|
+
error: function(response) {
|
52
|
+
$("div.error").html("Error saving lists. Please refresh the page and try again.").show();
|
53
|
+
|
54
|
+
}
|
55
|
+
});
|
56
|
+
},
|
57
|
+
|
58
|
+
addAuthenticityToken = function(data) {
|
59
|
+
return data + '&authenticity_token=' + encodeURIComponent(window._token);
|
60
|
+
},
|
61
|
+
|
62
|
+
attachSortables();
|
63
|
+
});
|
@@ -0,0 +1,17 @@
|
|
1
|
+
|
2
|
+
.version {}
|
3
|
+
li.version { clear: both; }
|
4
|
+
.ui-sortable {cursor: move;}
|
5
|
+
|
6
|
+
#prioritized-versions ol { padding-left: 35px; }
|
7
|
+
#unprioritized-versions ul { list-style: none; padding-left: 15px;}
|
8
|
+
|
9
|
+
/* item elements */
|
10
|
+
li.version span.due_date { font-size: smaller; }
|
11
|
+
li.version table.progress { display: inline; position: relative; float: right; }
|
12
|
+
li.version p.pourcent { display: inline; position: relative; float: right; }
|
13
|
+
|
14
|
+
h1.version-priority-title { margin-bottom: 15px; }
|
15
|
+
|
16
|
+
|
17
|
+
.drop-accepted { background-color:#FFFFEE; height: 16px; margin-bottom:5px; border: 1px dashed #000000; }
|
data/config/routes.rb
ADDED
data/init.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'redmine'
|
2
|
+
|
3
|
+
Redmine::Plugin.register :redmine_version_priorities do
|
4
|
+
name 'Version Priorities'
|
5
|
+
author 'Eric Davis'
|
6
|
+
url 'https://projects.littlestreamsoftware.com/projects/version-priorities'
|
7
|
+
author_url 'http://www.littlestreamsoftware.com'
|
8
|
+
description 'Allows versions to be prioritized'
|
9
|
+
version '0.1.0'
|
10
|
+
|
11
|
+
requires_redmine :version_or_higher => '0.9.0'
|
12
|
+
|
13
|
+
menu(:top_menu,
|
14
|
+
:version_priorities,
|
15
|
+
{:controller => 'version_priorities', :action => 'show'},
|
16
|
+
:caption => :version_priorities_title,
|
17
|
+
:if => Proc.new {
|
18
|
+
User.current.logged?
|
19
|
+
})
|
20
|
+
|
21
|
+
permission(:view_version_priorities, {:version_priorities => [:show]}, :public => true)
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
require 'dispatcher'
|
26
|
+
Dispatcher.to_prepare :redmine_version_priorities do
|
27
|
+
|
28
|
+
require_dependency 'issue'
|
29
|
+
Issue.send(:include, RedmineVersionPriorities::Patches::IssuePatch)
|
30
|
+
|
31
|
+
require_dependency 'query'
|
32
|
+
unless Query.included_modules.include?(RedmineVersionPriorities::Patches::QueryPatch)
|
33
|
+
Query.send(:include, RedmineVersionPriorities::Patches::QueryPatch)
|
34
|
+
end
|
35
|
+
|
36
|
+
require_dependency 'version'
|
37
|
+
Version.send(:include, RedmineVersionPriorities::Patches::VersionPatch)
|
38
|
+
end
|
data/lang/en.yml
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
module RedmineVersionPriorities
|
2
|
+
module Patches
|
3
|
+
module IssuePatch
|
4
|
+
def self.included(base)
|
5
|
+
base.extend(ClassMethods)
|
6
|
+
|
7
|
+
base.send(:include, InstanceMethods)
|
8
|
+
base.class_eval do
|
9
|
+
unloadable
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
end
|
15
|
+
|
16
|
+
module InstanceMethods
|
17
|
+
def fixed_version_priority
|
18
|
+
if fixed_version && fixed_version.priority.present?
|
19
|
+
fixed_version
|
20
|
+
else
|
21
|
+
l(:label_none)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module RedmineVersionPriorities
|
2
|
+
module Patches
|
3
|
+
module QueryPatch
|
4
|
+
def self.included(base)
|
5
|
+
base.extend(ClassMethods)
|
6
|
+
|
7
|
+
base.send(:include, InstanceMethods)
|
8
|
+
base.class_eval do
|
9
|
+
unloadable
|
10
|
+
|
11
|
+
add_available_column(QueryColumn.new(:fixed_version_priority,
|
12
|
+
:sortable => ["#{Version.table_name}.priority IS NULL",
|
13
|
+
"#{Version.table_name}.priority",
|
14
|
+
"#{Version.table_name}.effective_date",
|
15
|
+
"#{Version.table_name}.name"],
|
16
|
+
:default_order => 'asc'))
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
module ClassMethods
|
22
|
+
# Setter for +available_columns+ that isn't provided by the core.
|
23
|
+
def available_columns=(v)
|
24
|
+
self.available_columns = (v)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Method to add a column to the +available_columns+ that isn't provided by the core.
|
28
|
+
def add_available_column(column)
|
29
|
+
self.available_columns << (column)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
module InstanceMethods
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module RedmineVersionPriorities
|
2
|
+
module Patches
|
3
|
+
module VersionPatch
|
4
|
+
def self.included(base)
|
5
|
+
base.extend(ClassMethods)
|
6
|
+
|
7
|
+
base.send(:include, InstanceMethods)
|
8
|
+
base.class_eval do
|
9
|
+
unloadable
|
10
|
+
acts_as_list :column => 'priority'
|
11
|
+
|
12
|
+
named_scope :prioritized, :conditions => ["priority != 0 AND priority IS NOT NULL"], :order => 'priority ASC'
|
13
|
+
named_scope :unprioritized, :conditions => ["priority = 0 OR priority IS NULL"]
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
# Override acts_as_list to allow nil priorities
|
18
|
+
def add_to_list_bottom
|
19
|
+
self
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
module ClassMethods
|
25
|
+
def reprioritize(order)
|
26
|
+
ordered_ids = order.collect(&:to_i) if order.present?
|
27
|
+
ordered_ids ||= []
|
28
|
+
|
29
|
+
# Removed versions
|
30
|
+
Version.visible.prioritized.each do |version|
|
31
|
+
unless ordered_ids.include?(version.id)
|
32
|
+
version.remove_from_list
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Sort
|
37
|
+
ordered_ids.each_with_index do |version_id, index|
|
38
|
+
version = Version.find_by_id(version_id)
|
39
|
+
version.insert_at( index + 1) if version
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
module InstanceMethods
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class EditVersionPrioritiesTest < ActionController::IntegrationTest
|
4
|
+
context "as a logged in user" do
|
5
|
+
setup do
|
6
|
+
@user = login_as_user
|
7
|
+
end
|
8
|
+
|
9
|
+
should_be_unauthorized("as a logged in user") {
|
10
|
+
put "/version_priorities.js"
|
11
|
+
}
|
12
|
+
end
|
13
|
+
|
14
|
+
context "as an administrator" do
|
15
|
+
setup do
|
16
|
+
@user = login_as_user
|
17
|
+
@user.update_attribute(:admin, true)
|
18
|
+
|
19
|
+
@project1 = Project.generate!
|
20
|
+
@project2 = Project.generate!
|
21
|
+
@version1 = Version.generate!(:project => @project1)
|
22
|
+
@version2 = Version.generate!(:project => @project2)
|
23
|
+
end
|
24
|
+
|
25
|
+
should "reprioritize the versions with a :html request" do
|
26
|
+
put "/version_priorities", :version => [@version2.id.to_s, @version1.id.to_s]
|
27
|
+
|
28
|
+
assert_response :redirect
|
29
|
+
follow_redirect!
|
30
|
+
assert_template 'version_priorities/show'
|
31
|
+
|
32
|
+
assert_equal 2, @version1.reload.priority
|
33
|
+
assert_equal 1, @version2.reload.priority
|
34
|
+
end
|
35
|
+
|
36
|
+
should "reprioritize the versions with a :js request" do
|
37
|
+
put "/version_priorities.js", :version => [@version2.id.to_s, @version1.id.to_s]
|
38
|
+
assert_response :success
|
39
|
+
|
40
|
+
assert_equal 2, @version1.reload.priority
|
41
|
+
assert_equal 1, @version2.reload.priority
|
42
|
+
end
|
43
|
+
|
44
|
+
should "remove versions not in the list anymore" do
|
45
|
+
@version2.insert_at(1)
|
46
|
+
assert_equal 1, @version2.reload.priority
|
47
|
+
|
48
|
+
put "/version_priorities.js", :version => [@version1.id.to_s]
|
49
|
+
assert_response :success
|
50
|
+
|
51
|
+
assert_equal nil, @version2.reload.priority
|
52
|
+
end
|
53
|
+
|
54
|
+
should "remove all versions when the list is empty" do
|
55
|
+
@version2.insert_at(1)
|
56
|
+
assert_equal 1, @version2.reload.priority
|
57
|
+
@version1.insert_at(2)
|
58
|
+
assert_equal 2, @version1.reload.priority
|
59
|
+
|
60
|
+
put "/version_priorities.js"
|
61
|
+
assert_response :success
|
62
|
+
|
63
|
+
assert_equal nil, @version1.reload.priority
|
64
|
+
assert_equal nil, @version2.reload.priority
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def login_as_user
|
69
|
+
user = User.generate!(:login => 'existing', :password => 'existing', :password_confirmation => 'existing', :admin => false)
|
70
|
+
visit "/login"
|
71
|
+
fill_in 'Login', :with => 'existing'
|
72
|
+
fill_in 'Password', :with => 'existing'
|
73
|
+
click_button 'login'
|
74
|
+
assert_response :success
|
75
|
+
assert User.current.logged?
|
76
|
+
|
77
|
+
user
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class ViewVersionPrioritiesTest < ActionController::IntegrationTest
|
4
|
+
should "not show the menu item to users not logged in" do
|
5
|
+
visit "/"
|
6
|
+
assert_response :success
|
7
|
+
assert_select "#top-menu a.version-priorities", :count => 0
|
8
|
+
end
|
9
|
+
|
10
|
+
should "show the menu item to logged in users" do
|
11
|
+
login_as_user
|
12
|
+
|
13
|
+
visit "/"
|
14
|
+
assert_response :success
|
15
|
+
assert_select "#top-menu a.version-priorities", :count => 1
|
16
|
+
end
|
17
|
+
|
18
|
+
should "allow all logged in users to visit the prioritized versions panel" do
|
19
|
+
login_as_user
|
20
|
+
|
21
|
+
visit '/'
|
22
|
+
click_link "Version Priorities"
|
23
|
+
assert_response :success
|
24
|
+
assert_equal "/version_priorities", current_url
|
25
|
+
end
|
26
|
+
|
27
|
+
should "allow all logged in users to see the unprioritized versions" do
|
28
|
+
@user = login_as_user
|
29
|
+
@project1 = Project.generate!
|
30
|
+
@project2 = Project.generate!
|
31
|
+
@version1 = Version.generate!(:project => @project1)
|
32
|
+
@version2 = Version.generate!(:project => @project2)
|
33
|
+
@role = Role.generate!(:permissions => [:view_issues])
|
34
|
+
Member.generate!(:principal => @user, :roles => [@role], :project => @project1)
|
35
|
+
Member.generate!(:principal => @user, :roles => [@role], :project => @project2)
|
36
|
+
|
37
|
+
visit "/version_priorities"
|
38
|
+
|
39
|
+
assert_select "#unprioritized-versions" do
|
40
|
+
assert_select "li" do
|
41
|
+
assert_select "a", :text => /#{@version1.name}/
|
42
|
+
assert_select "span", :text => /#{@version1.description}/
|
43
|
+
end
|
44
|
+
|
45
|
+
assert_select "li" do
|
46
|
+
assert_select "a", :text => /#{@version2.name}/
|
47
|
+
assert_select "span", :text => /#{@version2.description}/
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
should "allow all logged in users to see the prioritized versions" do
|
54
|
+
@user = login_as_user
|
55
|
+
@project1 = Project.generate!
|
56
|
+
@project2 = Project.generate!
|
57
|
+
@version1 = Version.generate!(:project => @project1, :priority => 1)
|
58
|
+
@version2 = Version.generate!(:project => @project2, :priority => 2)
|
59
|
+
@role = Role.generate!(:permissions => [:view_issues])
|
60
|
+
Member.generate!(:principal => @user, :roles => [@role], :project => @project1)
|
61
|
+
Member.generate!(:principal => @user, :roles => [@role], :project => @project2)
|
62
|
+
|
63
|
+
visit "/version_priorities"
|
64
|
+
|
65
|
+
assert_select "#prioritized-versions" do
|
66
|
+
assert_select "li" do
|
67
|
+
assert_select "a", :text => /#{@version1.name}/
|
68
|
+
assert_select "span", :text => /#{@version1.description}/
|
69
|
+
end
|
70
|
+
|
71
|
+
assert_select "li" do
|
72
|
+
assert_select "a", :text => /#{@version2.name}/
|
73
|
+
assert_select "span", :text => /#{@version2.description}/
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
def login_as_user
|
80
|
+
user = User.generate!(:login => 'existing', :password => 'existing', :password_confirmation => 'existing', :admin => false)
|
81
|
+
visit "/login"
|
82
|
+
fill_in 'Login', :with => 'existing'
|
83
|
+
fill_in 'Password', :with => 'existing'
|
84
|
+
click_button 'login'
|
85
|
+
assert_response :success
|
86
|
+
assert User.current.logged?
|
87
|
+
|
88
|
+
user
|
89
|
+
end
|
90
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# Load the normal Rails helper
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../../../test/test_helper')
|
3
|
+
|
4
|
+
# Ensure that we are using the temporary fixture path
|
5
|
+
Engines::Testing.set_fixture_path
|
6
|
+
|
7
|
+
require "webrat"
|
8
|
+
|
9
|
+
Webrat.configure do |config|
|
10
|
+
config.mode = :rails
|
11
|
+
end
|
12
|
+
|
13
|
+
class Test::Unit::TestCase
|
14
|
+
def setup
|
15
|
+
setup_anonymous_role
|
16
|
+
setup_non_member_role
|
17
|
+
end
|
18
|
+
|
19
|
+
def setup_anonymous_role
|
20
|
+
begin
|
21
|
+
@anon_role = Role.anonymous
|
22
|
+
rescue
|
23
|
+
@anon_role = Role.generate!
|
24
|
+
@anon_role.update_attribute(:builtin, Role::BUILTIN_ANONYMOUS)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def setup_non_member_role
|
29
|
+
begin
|
30
|
+
@anon_role = Role.non_member
|
31
|
+
rescue
|
32
|
+
@non_member_role = Role.generate!
|
33
|
+
@non_member_role.update_attribute(:builtin, Role::BUILTIN_NON_MEMBER)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.should_be_unauthorized(description, &block)
|
38
|
+
context description do
|
39
|
+
should "render the unauthorized template" do
|
40
|
+
instance_eval &block
|
41
|
+
assert_template 'common/403'
|
42
|
+
end
|
43
|
+
|
44
|
+
should "respond with unauthorized" do
|
45
|
+
instance_eval &block
|
46
|
+
assert_response 403
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|