@@ -0,0 +1,38 | |||||
|
1 | <% back_to = url_for(:controller => 'projects', :action => 'list_issues', :id => @project) %> | |||
|
2 | <ul> | |||
|
3 | <li><%= context_menu_link l(:button_edit), {:controller => 'issues', :action => 'edit', :id => @issue}, | |||
|
4 | :class => 'icon-edit', :disabled => !@can[:edit] %></li> | |||
|
5 | <li class="folder"> | |||
|
6 | <a href="#" class="submenu" onclick="return false;"><%= l(:field_status) %></a> | |||
|
7 | <ul> | |||
|
8 | <% @statuses.each do |s| %> | |||
|
9 | <li><%= context_menu_link s.name, {:controller => 'issues', :action => 'change_status', :id => @issue, :new_status_id => s}, | |||
|
10 | :selected => (s == @issue.status), :disabled => !(@can[:change_status] && @allowed_statuses.include?(s)) %></li> | |||
|
11 | <% end %> | |||
|
12 | </ul> | |||
|
13 | </li> | |||
|
14 | <li class="folder"> | |||
|
15 | <a href="#" class="submenu"><%= l(:field_priority) %></a> | |||
|
16 | <ul> | |||
|
17 | <% @priorities.each do |p| %> | |||
|
18 | <li><%= context_menu_link p.name, {:controller => 'issues', :action => 'edit', :id => @issue, 'issue[priority_id]' => p, :back_to => back_to}, :method => :post, | |||
|
19 | :selected => (p == @issue.priority), :disabled => !@can[:edit] %></li> | |||
|
20 | <% end %> | |||
|
21 | </ul> | |||
|
22 | </li> | |||
|
23 | <li class="folder"> | |||
|
24 | <a href="#" class="submenu"><%= l(:field_assigned_to) %></a> | |||
|
25 | <ul> | |||
|
26 | <% @assignables.each do |u| %> | |||
|
27 | <li><%= context_menu_link u.name, {:controller => 'issues', :action => 'edit', :id => @issue, 'issue[assigned_to_id]' => u, :back_to => back_to}, :method => :post, | |||
|
28 | :selected => (u == @issue.assigned_to), :disabled => !(@can[:edit] || @can[:change_status]) %></li> | |||
|
29 | <% end %> | |||
|
30 | <li><%= context_menu_link l(:label_nobody), {:controller => 'issues', :action => 'edit', :id => @issue, 'issue[assigned_to_id]' => '', :back_to => back_to}, :method => :post, | |||
|
31 | :selected => @issue.assigned_to.nil?, :disabled => !(@can[:edit] || @can[:change_status]) %></li> | |||
|
32 | </ul> | |||
|
33 | </li> | |||
|
34 | <li><%= context_menu_link l(:button_move), {:controller => 'projects', :action => 'move_issues', :id => @project, "issue_ids[]" => @issue.id }, | |||
|
35 | :class => 'icon-move', :disabled => !@can[:move] %> | |||
|
36 | <li><%= context_menu_link l(:button_delete), {:controller => 'issues', :action => 'destroy', :id => @issue}, | |||
|
37 | :method => :post, :confirm => l(:text_are_you_sure), :class => 'icon-del', :disabled => !@can[:delete] %></li> | |||
|
38 | </ul> |
1 | NO CONTENT: new file 100644, binary diff hidden |
|
NO CONTENT: new file 100644, binary diff hidden |
@@ -0,0 +1,44 | |||||
|
1 | ContextMenu = Class.create(); | |||
|
2 | ContextMenu.prototype = { | |||
|
3 | initialize: function (options) { | |||
|
4 | this.options = Object.extend({selector: '.hascontextmenu'}, options || { }); | |||
|
5 | ||||
|
6 | Event.observe(document, 'click', function(e){ | |||
|
7 | var t = Event.findElement(e, 'a'); | |||
|
8 | if ((t != document) && (Element.hasClassName(t, 'disabled') || Element.hasClassName(t, 'submenu'))) { | |||
|
9 | Event.stop(e); | |||
|
10 | } else { | |||
|
11 | $('context-menu').hide(); | |||
|
12 | if (this.selection) { | |||
|
13 | this.selection.removeClassName('context-menu-selection'); | |||
|
14 | } | |||
|
15 | } | |||
|
16 | ||||
|
17 | }.bind(this)); | |||
|
18 | ||||
|
19 | $$(this.options.selector).invoke('observe', (window.opera ? 'click' : 'contextmenu'), function(e){ | |||
|
20 | if (window.opera && !e.ctrlKey) { | |||
|
21 | return; | |||
|
22 | } | |||
|
23 | this.show(e); | |||
|
24 | }.bind(this)); | |||
|
25 | ||||
|
26 | }, | |||
|
27 | show: function(e) { | |||
|
28 | Event.stop(e); | |||
|
29 | Element.hide('context-menu'); | |||
|
30 | if (this.selection) { | |||
|
31 | this.selection.removeClassName('context-menu-selection'); | |||
|
32 | } | |||
|
33 | $('context-menu').style['left'] = (Event.pointerX(e) + 'px'); | |||
|
34 | $('context-menu').style['top'] = (Event.pointerY(e) + 'px'); | |||
|
35 | Element.update('context-menu', ''); | |||
|
36 | ||||
|
37 | var tr = Event.findElement(e, 'tr'); | |||
|
38 | tr.addClassName('context-menu-selection'); | |||
|
39 | this.selection = tr; | |||
|
40 | var id = tr.id.substring(6, tr.id.length); | |||
|
41 | /* TODO: do not hard code path */ | |||
|
42 | new Ajax.Updater('context-menu', '../../issues/context_menu/' + id, {asynchronous:true, evalScripts:true, onComplete:function(request){Effect.Appear('context-menu', {duration: 0.20})}}) | |||
|
43 | } | |||
|
44 | } |
@@ -0,0 +1,52 | |||||
|
1 | #context-menu { position: absolute; } | |||
|
2 | ||||
|
3 | #context-menu ul, #context-menu li, #context-menu a { | |||
|
4 | display:block; | |||
|
5 | margin:0; | |||
|
6 | padding:0; | |||
|
7 | border:0; | |||
|
8 | } | |||
|
9 | ||||
|
10 | #context-menu ul { | |||
|
11 | width:150px; | |||
|
12 | border-top:1px solid #ddd; | |||
|
13 | border-left:1px solid #ddd; | |||
|
14 | border-bottom:1px solid #777; | |||
|
15 | border-right:1px solid #777; | |||
|
16 | background:white; | |||
|
17 | list-style:none; | |||
|
18 | } | |||
|
19 | ||||
|
20 | #context-menu li { | |||
|
21 | position:relative; | |||
|
22 | padding:1px; | |||
|
23 | z-index:9; | |||
|
24 | } | |||
|
25 | #context-menu li.folder ul { | |||
|
26 | position:absolute; | |||
|
27 | left:128px; /* IE */ | |||
|
28 | top:-2px; | |||
|
29 | } | |||
|
30 | #context-menu li.folder>ul { left:148px; } | |||
|
31 | ||||
|
32 | #context-menu a { | |||
|
33 | border:1px solid white; | |||
|
34 | text-decoration:none; | |||
|
35 | background-repeat: no-repeat; | |||
|
36 | background-position: 1px 50%; | |||
|
37 | padding: 2px 0px 2px 20px; | |||
|
38 | width:100%; /* IE */ | |||
|
39 | } | |||
|
40 | #context-menu li>a { width:auto; } /* others */ | |||
|
41 | #context-menu a.disabled, #context-menu a.disabled:hover {color: #ccc;} | |||
|
42 | #context-menu li a.submenu { background:url("../images/sub.gif") right no-repeat; } | |||
|
43 | #context-menu a:hover { border-color:gray; background-color:#eee; color:#2A5685; } | |||
|
44 | #context-menu li.folder a:hover { background-color:#eee; } | |||
|
45 | #context-menu li.folder:hover { z-index:10; } | |||
|
46 | #context-menu ul ul, #context-menu li:hover ul ul { display:none; } | |||
|
47 | #context-menu li:hover ul, #context-menu li:hover li:hover ul { display:block; } | |||
|
48 | ||||
|
49 | /* selected element */ | |||
|
50 | .context-menu-selection { background-color:#507AAA !important; color:#f8f8f8 !important; } | |||
|
51 | .context-menu-selection a, .context-menu-selection a:hover { color:#f8f8f8 !important; } | |||
|
52 | .context-menu-selection:hover { background-color:#507AAA !important; color:#f8f8f8 !important; } |
@@ -79,12 +79,14 class IssuesController < ApplicationController | |||||
79 | begin |
|
79 | begin | |
80 | @issue.init_journal(self.logged_in_user) |
|
80 | @issue.init_journal(self.logged_in_user) | |
81 | # Retrieve custom fields and values |
|
81 | # Retrieve custom fields and values | |
82 | @custom_values = @project.custom_fields_for_issues(@issue.tracker).collect { |x| CustomValue.new(:custom_field => x, :customized => @issue, :value => params["custom_fields"][x.id.to_s]) } |
|
82 | if params["custom_fields"] | |
83 | @issue.custom_values = @custom_values |
|
83 | @custom_values = @project.custom_fields_for_issues(@issue.tracker).collect { |x| CustomValue.new(:custom_field => x, :customized => @issue, :value => params["custom_fields"][x.id.to_s]) } | |
|
84 | @issue.custom_values = @custom_values | |||
|
85 | end | |||
84 | @issue.attributes = params[:issue] |
|
86 | @issue.attributes = params[:issue] | |
85 | if @issue.save |
|
87 | if @issue.save | |
86 | flash[:notice] = l(:notice_successful_update) |
|
88 | flash[:notice] = l(:notice_successful_update) | |
87 | redirect_to :action => 'show', :id => @issue |
|
89 | redirect_to(params[:back_to] || {:action => 'show', :id => @issue}) | |
88 | end |
|
90 | end | |
89 | rescue ActiveRecord::StaleObjectError |
|
91 | rescue ActiveRecord::StaleObjectError | |
90 | # Optimistic locking exception |
|
92 | # Optimistic locking exception | |
@@ -163,6 +165,19 class IssuesController < ApplicationController | |||||
163 | journal.save |
|
165 | journal.save | |
164 | redirect_to :action => 'show', :id => @issue |
|
166 | redirect_to :action => 'show', :id => @issue | |
165 | end |
|
167 | end | |
|
168 | ||||
|
169 | def context_menu | |||
|
170 | @priorities = Enumeration.get_values('IPRI').reverse | |||
|
171 | @statuses = IssueStatus.find(:all, :order => 'position') | |||
|
172 | @allowed_statuses = @issue.status.find_new_statuses_allowed_to(User.current.role_for_project(@project), @issue.tracker) | |||
|
173 | @assignables = @issue.assignable_users | |||
|
174 | @assignables << @issue.assigned_to if @issue.assigned_to && !@assignables.include?(@issue.assigned_to) | |||
|
175 | @can = {:edit => User.current.allowed_to?(:edit_issues, @project), | |||
|
176 | :change_status => User.current.allowed_to?(:change_issue_status, @project), | |||
|
177 | :move => User.current.allowed_to?(:move_issues, @project), | |||
|
178 | :delete => User.current.allowed_to?(:delete_issues, @project)} | |||
|
179 | render :layout => false | |||
|
180 | end | |||
166 |
|
181 | |||
167 | def preview |
|
182 | def preview | |
168 | issue = Issue.find_by_id(params[:id]) |
|
183 | issue = Issue.find_by_id(params[:id]) |
@@ -296,6 +296,22 module ApplicationHelper | |||||
296 | link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)") |
|
296 | link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)") | |
297 | end |
|
297 | end | |
298 |
|
298 | |||
|
299 | def context_menu_link(name, url, options={}) | |||
|
300 | options[:class] ||= '' | |||
|
301 | if options.delete(:selected) | |||
|
302 | options[:class] << ' icon-checked disabled' | |||
|
303 | options[:disabled] = true | |||
|
304 | end | |||
|
305 | if options.delete(:disabled) | |||
|
306 | options.delete(:method) | |||
|
307 | options.delete(:confirm) | |||
|
308 | options.delete(:onclick) | |||
|
309 | options[:class] << ' disabled' | |||
|
310 | url = '#' | |||
|
311 | end | |||
|
312 | link_to name, url, options | |||
|
313 | end | |||
|
314 | ||||
299 | def calendar_for(field_id) |
|
315 | def calendar_for(field_id) | |
300 | image_tag("calendar.png", {:id => "#{field_id}_trigger",:class => "calendar-trigger"}) + |
|
316 | image_tag("calendar.png", {:id => "#{field_id}_trigger",:class => "calendar-trigger"}) + | |
301 | javascript_tag("Calendar.setup({inputField : '#{field_id}', ifFormat : '%Y-%m-%d', button : '#{field_id}_trigger' });") |
|
317 | javascript_tag("Calendar.setup({inputField : '#{field_id}', ifFormat : '%Y-%m-%d', button : '#{field_id}_trigger' });") |
@@ -13,7 +13,7 | |||||
13 | </tr></thead> |
|
13 | </tr></thead> | |
14 | <tbody> |
|
14 | <tbody> | |
15 | <% issues.each do |issue| %> |
|
15 | <% issues.each do |issue| %> | |
16 | <tr class="issue <%= cycle('odd', 'even') %> <%= "status-#{issue.status.position} priority-#{issue.priority.position}" %>"> |
|
16 | <tr id="issue-<%= issue.id %>" class="issue hascontextmenu <%= cycle('odd', 'even') %> <%= "status-#{issue.status.position} priority-#{issue.priority.position}" %>"> | |
17 | <td class="checkbox"><%= check_box_tag("issue_ids[]", issue.id, false, :id => "issue_#{issue.id}", :disabled => (!@project || @project != issue.project)) %></td> |
|
17 | <td class="checkbox"><%= check_box_tag("issue_ids[]", issue.id, false, :id => "issue_#{issue.id}", :disabled => (!@project || @project != issue.project)) %></td> | |
18 | <td><%= link_to issue.id, :controller => 'issues', :action => 'show', :id => issue %></td> |
|
18 | <td><%= link_to issue.id, :controller => 'issues', :action => 'show', :id => issue %></td> | |
19 | <% query.columns.each do |column| %> |
|
19 | <% query.columns.each do |column| %> |
@@ -64,4 +64,9 | |||||
64 | <%= javascript_include_tag "calendar/lang/calendar-#{current_language}.js" %> |
|
64 | <%= javascript_include_tag "calendar/lang/calendar-#{current_language}.js" %> | |
65 | <%= javascript_include_tag 'calendar/calendar-setup' %> |
|
65 | <%= javascript_include_tag 'calendar/calendar-setup' %> | |
66 | <%= stylesheet_link_tag 'calendar' %> |
|
66 | <%= stylesheet_link_tag 'calendar' %> | |
|
67 | <%= javascript_include_tag 'context_menu' %> | |||
|
68 | <%= stylesheet_link_tag 'context_menu' %> | |||
67 | <% end %> |
|
69 | <% end %> | |
|
70 | ||||
|
71 | <div id="context-menu" style="display: none;"></div> | |||
|
72 | <%= javascript_tag 'new ContextMenu({})' %> |
@@ -26,7 +26,7 Redmine::AccessControl.map do |map| | |||||
26 | map.permission :manage_categories, {:projects => [:settings, :add_issue_category], :issue_categories => [:edit, :destroy]}, :require => :member |
|
26 | map.permission :manage_categories, {:projects => [:settings, :add_issue_category], :issue_categories => [:edit, :destroy]}, :require => :member | |
27 | # Issues |
|
27 | # Issues | |
28 | map.permission :view_issues, {:projects => [:list_issues, :export_issues_csv, :export_issues_pdf, :changelog, :roadmap], |
|
28 | map.permission :view_issues, {:projects => [:list_issues, :export_issues_csv, :export_issues_pdf, :changelog, :roadmap], | |
29 | :issues => :show, |
|
29 | :issues => [:show, :context_menu], | |
30 | :queries => :index, |
|
30 | :queries => :index, | |
31 | :reports => :issue_report}, :public => true |
|
31 | :reports => :issue_report}, :public => true | |
32 | map.permission :add_issues, {:projects => :add_issue}, :require => :loggedin |
|
32 | map.permission :add_issues, {:projects => :add_issue}, :require => :loggedin |
@@ -42,7 +42,7 h4, .wiki h3 {font-size: 12px;padding: 2px 10px 1px 0px;margin-bottom: 5px; bord | |||||
42 | #sidebar hr{ width: 100%; margin: 0 auto; height: 1px; background: #ccc; border: 0; } |
|
42 | #sidebar hr{ width: 100%; margin: 0 auto; height: 1px; background: #ccc; border: 0; } | |
43 | * html #sidebar hr{ width: 95%; position: relative; left: -6px; color: #ccc; } |
|
43 | * html #sidebar hr{ width: 95%; position: relative; left: -6px; color: #ccc; } | |
44 |
|
44 | |||
45 |
#content { width: 80%; background: url(../images/contentbg.png) repeat-x; background-color: #fff; margin: 0px; border-right: 1px solid #ddd; padding: 6px 10px 10px 10px; |
|
45 | #content { width: 80%; background: url(../images/contentbg.png) repeat-x; background-color: #fff; margin: 0px; border-right: 1px solid #ddd; padding: 6px 10px 10px 10px; z-index: 10; height:600px; min-height: 600px;} | |
46 | * html #content{ width: 80%; padding-left: 0; margin-top: 0px; padding: 6px 10px 10px 10px;} |
|
46 | * html #content{ width: 80%; padding-left: 0; margin-top: 0px; padding: 6px 10px 10px 10px;} | |
47 | html>body #content { |
|
47 | html>body #content { | |
48 | height: auto; |
|
48 | height: auto; | |
@@ -154,7 +154,7 width: 200px; | |||||
154 | div.attachments p { margin:4px 0 2px 0; } |
|
154 | div.attachments p { margin:4px 0 2px 0; } | |
155 |
|
155 | |||
156 | /***** Flash & error messages ****/ |
|
156 | /***** Flash & error messages ****/ | |
157 |
#errorExplanation, div.flash, |
|
157 | #errorExplanation, div.flash, .nodata { | |
158 | padding: 4px 4px 4px 30px; |
|
158 | padding: 4px 4px 4px 30px; | |
159 | margin-bottom: 12px; |
|
159 | margin-bottom: 12px; | |
160 | font-size: 1.1em; |
|
160 | font-size: 1.1em; | |
@@ -454,6 +454,7 vertical-align: middle; | |||||
454 | .icon-lock { background-image: url(../images/locked.png); } |
|
454 | .icon-lock { background-image: url(../images/locked.png); } | |
455 | .icon-unlock { background-image: url(../images/unlock.png); } |
|
455 | .icon-unlock { background-image: url(../images/unlock.png); } | |
456 | .icon-note { background-image: url(../images/note.png); } |
|
456 | .icon-note { background-image: url(../images/note.png); } | |
|
457 | .icon-checked { background-image: url(../images/true.png); } | |||
457 |
|
458 | |||
458 | .icon22-projects { background-image: url(../images/22x22/projects.png); } |
|
459 | .icon22-projects { background-image: url(../images/22x22/projects.png); } | |
459 | .icon22-users { background-image: url(../images/22x22/users.png); } |
|
460 | .icon22-users { background-image: url(../images/22x22/users.png); } |
General Comments 0
You need to be logged in to leave comments.
Login now