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.
@@ -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; }
@@ -0,0 +1,5 @@
1
+ en:
2
+ version_priorities_title: "Version Priorities"
3
+ version_priorities_text_prioritized_versions: "Prioritized Versions"
4
+ version_priorities_text_unprioritized_versions: "Unprioritized Versions"
5
+ field_fixed_version_priority: "Version priority"
data/config/routes.rb ADDED
@@ -0,0 +1,3 @@
1
+ ActionController::Routing::Routes.draw do |map|
2
+ map.resource :version_priorities
3
+ end
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,2 @@
1
+ # English strings go here
2
+ my_label: "My label"
@@ -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,4 @@
1
+ require File.dirname(__FILE__) + '/../test_helper'
2
+
3
+ class VersionPrioritiesController < ActionController::TestCase
4
+ 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
@@ -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