##// END OF EJS Templates
Adds a css class on menu items in order to apply item specific styles (eg. icons)....
Jean-Philippe Lang -
r2057:546b98a1186d
parent child
Show More
@@ -1,163 +1,163
1 require 'redmine/access_control'
1 require 'redmine/access_control'
2 require 'redmine/menu_manager'
2 require 'redmine/menu_manager'
3 require 'redmine/activity'
3 require 'redmine/activity'
4 require 'redmine/mime_type'
4 require 'redmine/mime_type'
5 require 'redmine/core_ext'
5 require 'redmine/core_ext'
6 require 'redmine/themes'
6 require 'redmine/themes'
7 require 'redmine/hook'
7 require 'redmine/hook'
8 require 'redmine/plugin'
8 require 'redmine/plugin'
9 require 'redmine/wiki_formatting'
9 require 'redmine/wiki_formatting'
10
10
11 begin
11 begin
12 require_library_or_gem 'RMagick' unless Object.const_defined?(:Magick)
12 require_library_or_gem 'RMagick' unless Object.const_defined?(:Magick)
13 rescue LoadError
13 rescue LoadError
14 # RMagick is not available
14 # RMagick is not available
15 end
15 end
16
16
17 REDMINE_SUPPORTED_SCM = %w( Subversion Darcs Mercurial Cvs Bazaar Git Filesystem )
17 REDMINE_SUPPORTED_SCM = %w( Subversion Darcs Mercurial Cvs Bazaar Git Filesystem )
18
18
19 # Permissions
19 # Permissions
20 Redmine::AccessControl.map do |map|
20 Redmine::AccessControl.map do |map|
21 map.permission :view_project, {:projects => [:show, :activity]}, :public => true
21 map.permission :view_project, {:projects => [:show, :activity]}, :public => true
22 map.permission :search_project, {:search => :index}, :public => true
22 map.permission :search_project, {:search => :index}, :public => true
23 map.permission :edit_project, {:projects => [:settings, :edit]}, :require => :member
23 map.permission :edit_project, {:projects => [:settings, :edit]}, :require => :member
24 map.permission :select_project_modules, {:projects => :modules}, :require => :member
24 map.permission :select_project_modules, {:projects => :modules}, :require => :member
25 map.permission :manage_members, {:projects => :settings, :members => [:new, :edit, :destroy]}, :require => :member
25 map.permission :manage_members, {:projects => :settings, :members => [:new, :edit, :destroy]}, :require => :member
26 map.permission :manage_versions, {:projects => [:settings, :add_version], :versions => [:edit, :destroy]}, :require => :member
26 map.permission :manage_versions, {:projects => [:settings, :add_version], :versions => [:edit, :destroy]}, :require => :member
27
27
28 map.project_module :issue_tracking do |map|
28 map.project_module :issue_tracking do |map|
29 # Issue categories
29 # Issue categories
30 map.permission :manage_categories, {:projects => [:settings, :add_issue_category], :issue_categories => [:edit, :destroy]}, :require => :member
30 map.permission :manage_categories, {:projects => [:settings, :add_issue_category], :issue_categories => [:edit, :destroy]}, :require => :member
31 # Issues
31 # Issues
32 map.permission :view_issues, {:projects => [:changelog, :roadmap],
32 map.permission :view_issues, {:projects => [:changelog, :roadmap],
33 :issues => [:index, :changes, :show, :context_menu],
33 :issues => [:index, :changes, :show, :context_menu],
34 :versions => [:show, :status_by],
34 :versions => [:show, :status_by],
35 :queries => :index,
35 :queries => :index,
36 :reports => :issue_report}, :public => true
36 :reports => :issue_report}, :public => true
37 map.permission :add_issues, {:issues => :new}
37 map.permission :add_issues, {:issues => :new}
38 map.permission :edit_issues, {:issues => [:edit, :reply, :bulk_edit, :destroy_attachment]}
38 map.permission :edit_issues, {:issues => [:edit, :reply, :bulk_edit, :destroy_attachment]}
39 map.permission :manage_issue_relations, {:issue_relations => [:new, :destroy]}
39 map.permission :manage_issue_relations, {:issue_relations => [:new, :destroy]}
40 map.permission :add_issue_notes, {:issues => [:edit, :reply]}
40 map.permission :add_issue_notes, {:issues => [:edit, :reply]}
41 map.permission :edit_issue_notes, {:journals => :edit}, :require => :loggedin
41 map.permission :edit_issue_notes, {:journals => :edit}, :require => :loggedin
42 map.permission :edit_own_issue_notes, {:journals => :edit}, :require => :loggedin
42 map.permission :edit_own_issue_notes, {:journals => :edit}, :require => :loggedin
43 map.permission :move_issues, {:issues => :move}, :require => :loggedin
43 map.permission :move_issues, {:issues => :move}, :require => :loggedin
44 map.permission :delete_issues, {:issues => :destroy}, :require => :member
44 map.permission :delete_issues, {:issues => :destroy}, :require => :member
45 # Queries
45 # Queries
46 map.permission :manage_public_queries, {:queries => [:new, :edit, :destroy]}, :require => :member
46 map.permission :manage_public_queries, {:queries => [:new, :edit, :destroy]}, :require => :member
47 map.permission :save_queries, {:queries => [:new, :edit, :destroy]}, :require => :loggedin
47 map.permission :save_queries, {:queries => [:new, :edit, :destroy]}, :require => :loggedin
48 # Gantt & calendar
48 # Gantt & calendar
49 map.permission :view_gantt, :issues => :gantt
49 map.permission :view_gantt, :issues => :gantt
50 map.permission :view_calendar, :issues => :calendar
50 map.permission :view_calendar, :issues => :calendar
51 # Watchers
51 # Watchers
52 map.permission :view_issue_watchers, {}
52 map.permission :view_issue_watchers, {}
53 map.permission :add_issue_watchers, {:watchers => :new}
53 map.permission :add_issue_watchers, {:watchers => :new}
54 end
54 end
55
55
56 map.project_module :time_tracking do |map|
56 map.project_module :time_tracking do |map|
57 map.permission :log_time, {:timelog => :edit}, :require => :loggedin
57 map.permission :log_time, {:timelog => :edit}, :require => :loggedin
58 map.permission :view_time_entries, :timelog => [:details, :report]
58 map.permission :view_time_entries, :timelog => [:details, :report]
59 map.permission :edit_time_entries, {:timelog => [:edit, :destroy]}, :require => :member
59 map.permission :edit_time_entries, {:timelog => [:edit, :destroy]}, :require => :member
60 map.permission :edit_own_time_entries, {:timelog => [:edit, :destroy]}, :require => :loggedin
60 map.permission :edit_own_time_entries, {:timelog => [:edit, :destroy]}, :require => :loggedin
61 end
61 end
62
62
63 map.project_module :news do |map|
63 map.project_module :news do |map|
64 map.permission :manage_news, {:news => [:new, :edit, :destroy, :destroy_comment]}, :require => :member
64 map.permission :manage_news, {:news => [:new, :edit, :destroy, :destroy_comment]}, :require => :member
65 map.permission :view_news, {:news => [:index, :show]}, :public => true
65 map.permission :view_news, {:news => [:index, :show]}, :public => true
66 map.permission :comment_news, {:news => :add_comment}
66 map.permission :comment_news, {:news => :add_comment}
67 end
67 end
68
68
69 map.project_module :documents do |map|
69 map.project_module :documents do |map|
70 map.permission :manage_documents, {:documents => [:new, :edit, :destroy, :add_attachment, :destroy_attachment]}, :require => :loggedin
70 map.permission :manage_documents, {:documents => [:new, :edit, :destroy, :add_attachment, :destroy_attachment]}, :require => :loggedin
71 map.permission :view_documents, :documents => [:index, :show, :download]
71 map.permission :view_documents, :documents => [:index, :show, :download]
72 end
72 end
73
73
74 map.project_module :files do |map|
74 map.project_module :files do |map|
75 map.permission :manage_files, {:projects => :add_file, :versions => :destroy_file}, :require => :loggedin
75 map.permission :manage_files, {:projects => :add_file, :versions => :destroy_file}, :require => :loggedin
76 map.permission :view_files, :projects => :list_files, :versions => :download
76 map.permission :view_files, :projects => :list_files, :versions => :download
77 end
77 end
78
78
79 map.project_module :wiki do |map|
79 map.project_module :wiki do |map|
80 map.permission :manage_wiki, {:wikis => [:edit, :destroy]}, :require => :member
80 map.permission :manage_wiki, {:wikis => [:edit, :destroy]}, :require => :member
81 map.permission :rename_wiki_pages, {:wiki => :rename}, :require => :member
81 map.permission :rename_wiki_pages, {:wiki => :rename}, :require => :member
82 map.permission :delete_wiki_pages, {:wiki => :destroy}, :require => :member
82 map.permission :delete_wiki_pages, {:wiki => :destroy}, :require => :member
83 map.permission :view_wiki_pages, :wiki => [:index, :special]
83 map.permission :view_wiki_pages, :wiki => [:index, :special]
84 map.permission :view_wiki_edits, :wiki => [:history, :diff, :annotate]
84 map.permission :view_wiki_edits, :wiki => [:history, :diff, :annotate]
85 map.permission :edit_wiki_pages, :wiki => [:edit, :preview, :add_attachment]
85 map.permission :edit_wiki_pages, :wiki => [:edit, :preview, :add_attachment]
86 map.permission :delete_wiki_pages_attachments, :wiki => :destroy_attachment
86 map.permission :delete_wiki_pages_attachments, :wiki => :destroy_attachment
87 map.permission :protect_wiki_pages, {:wiki => :protect}, :require => :member
87 map.permission :protect_wiki_pages, {:wiki => :protect}, :require => :member
88 end
88 end
89
89
90 map.project_module :repository do |map|
90 map.project_module :repository do |map|
91 map.permission :manage_repository, {:repositories => [:edit, :committers, :destroy]}, :require => :member
91 map.permission :manage_repository, {:repositories => [:edit, :committers, :destroy]}, :require => :member
92 map.permission :browse_repository, :repositories => [:show, :browse, :entry, :annotate, :changes, :diff, :stats, :graph]
92 map.permission :browse_repository, :repositories => [:show, :browse, :entry, :annotate, :changes, :diff, :stats, :graph]
93 map.permission :view_changesets, :repositories => [:show, :revisions, :revision]
93 map.permission :view_changesets, :repositories => [:show, :revisions, :revision]
94 map.permission :commit_access, {}
94 map.permission :commit_access, {}
95 end
95 end
96
96
97 map.project_module :boards do |map|
97 map.project_module :boards do |map|
98 map.permission :manage_boards, {:boards => [:new, :edit, :destroy]}, :require => :member
98 map.permission :manage_boards, {:boards => [:new, :edit, :destroy]}, :require => :member
99 map.permission :view_messages, {:boards => [:index, :show], :messages => [:show]}, :public => true
99 map.permission :view_messages, {:boards => [:index, :show], :messages => [:show]}, :public => true
100 map.permission :add_messages, {:messages => [:new, :reply, :quote]}
100 map.permission :add_messages, {:messages => [:new, :reply, :quote]}
101 map.permission :edit_messages, {:messages => :edit}, :require => :member
101 map.permission :edit_messages, {:messages => :edit}, :require => :member
102 map.permission :edit_own_messages, {:messages => :edit}, :require => :loggedin
102 map.permission :edit_own_messages, {:messages => :edit}, :require => :loggedin
103 map.permission :delete_messages, {:messages => :destroy}, :require => :member
103 map.permission :delete_messages, {:messages => :destroy}, :require => :member
104 map.permission :delete_own_messages, {:messages => :destroy}, :require => :loggedin
104 map.permission :delete_own_messages, {:messages => :destroy}, :require => :loggedin
105 end
105 end
106 end
106 end
107
107
108 Redmine::MenuManager.map :top_menu do |menu|
108 Redmine::MenuManager.map :top_menu do |menu|
109 menu.push :home, :home_path, :html => { :class => 'home' }
109 menu.push :home, :home_path
110 menu.push :my_page, { :controller => 'my', :action => 'page' }, :html => { :class => 'mypage' }, :if => Proc.new { User.current.logged? }
110 menu.push :my_page, { :controller => 'my', :action => 'page' }, :if => Proc.new { User.current.logged? }
111 menu.push :projects, { :controller => 'projects', :action => 'index' }, :caption => :label_project_plural, :html => { :class => 'projects' }
111 menu.push :projects, { :controller => 'projects', :action => 'index' }, :caption => :label_project_plural
112 menu.push :administration, { :controller => 'admin', :action => 'index' }, :html => { :class => 'admin' }, :if => Proc.new { User.current.admin? }, :last => true
112 menu.push :administration, { :controller => 'admin', :action => 'index' }, :if => Proc.new { User.current.admin? }, :last => true
113 menu.push :help, Redmine::Info.help_url, :html => { :class => 'help' }, :last => true
113 menu.push :help, Redmine::Info.help_url, :last => true
114 end
114 end
115
115
116 Redmine::MenuManager.map :account_menu do |menu|
116 Redmine::MenuManager.map :account_menu do |menu|
117 menu.push :login, :signin_path, :html => { :class => 'login' }, :if => Proc.new { !User.current.logged? }
117 menu.push :login, :signin_path, :if => Proc.new { !User.current.logged? }
118 menu.push :register, { :controller => 'account', :action => 'register' }, :html => { :class => 'register' }, :if => Proc.new { !User.current.logged? && Setting.self_registration? }
118 menu.push :register, { :controller => 'account', :action => 'register' }, :if => Proc.new { !User.current.logged? && Setting.self_registration? }
119 menu.push :my_account, { :controller => 'my', :action => 'account' }, :html => { :class => 'myaccount' }, :if => Proc.new { User.current.logged? }
119 menu.push :my_account, { :controller => 'my', :action => 'account' }, :if => Proc.new { User.current.logged? }
120 menu.push :logout, :signout_path, :html => { :class => 'logout' }, :if => Proc.new { User.current.logged? }
120 menu.push :logout, :signout_path, :if => Proc.new { User.current.logged? }
121 end
121 end
122
122
123 Redmine::MenuManager.map :application_menu do |menu|
123 Redmine::MenuManager.map :application_menu do |menu|
124 # Empty
124 # Empty
125 end
125 end
126
126
127 Redmine::MenuManager.map :admin_menu do |menu|
127 Redmine::MenuManager.map :admin_menu do |menu|
128 # Empty
128 # Empty
129 end
129 end
130
130
131 Redmine::MenuManager.map :project_menu do |menu|
131 Redmine::MenuManager.map :project_menu do |menu|
132 menu.push :overview, { :controller => 'projects', :action => 'show' }
132 menu.push :overview, { :controller => 'projects', :action => 'show' }
133 menu.push :activity, { :controller => 'projects', :action => 'activity' }
133 menu.push :activity, { :controller => 'projects', :action => 'activity' }
134 menu.push :roadmap, { :controller => 'projects', :action => 'roadmap' },
134 menu.push :roadmap, { :controller => 'projects', :action => 'roadmap' },
135 :if => Proc.new { |p| p.versions.any? }
135 :if => Proc.new { |p| p.versions.any? }
136 menu.push :issues, { :controller => 'issues', :action => 'index' }, :param => :project_id, :caption => :label_issue_plural
136 menu.push :issues, { :controller => 'issues', :action => 'index' }, :param => :project_id, :caption => :label_issue_plural
137 menu.push :new_issue, { :controller => 'issues', :action => 'new' }, :param => :project_id, :caption => :label_issue_new,
137 menu.push :new_issue, { :controller => 'issues', :action => 'new' }, :param => :project_id, :caption => :label_issue_new,
138 :html => { :accesskey => Redmine::AccessKeys.key_for(:new_issue) }
138 :html => { :accesskey => Redmine::AccessKeys.key_for(:new_issue) }
139 menu.push :news, { :controller => 'news', :action => 'index' }, :param => :project_id, :caption => :label_news_plural
139 menu.push :news, { :controller => 'news', :action => 'index' }, :param => :project_id, :caption => :label_news_plural
140 menu.push :documents, { :controller => 'documents', :action => 'index' }, :param => :project_id, :caption => :label_document_plural
140 menu.push :documents, { :controller => 'documents', :action => 'index' }, :param => :project_id, :caption => :label_document_plural
141 menu.push :wiki, { :controller => 'wiki', :action => 'index', :page => nil },
141 menu.push :wiki, { :controller => 'wiki', :action => 'index', :page => nil },
142 :if => Proc.new { |p| p.wiki && !p.wiki.new_record? }
142 :if => Proc.new { |p| p.wiki && !p.wiki.new_record? }
143 menu.push :boards, { :controller => 'boards', :action => 'index', :id => nil }, :param => :project_id,
143 menu.push :boards, { :controller => 'boards', :action => 'index', :id => nil }, :param => :project_id,
144 :if => Proc.new { |p| p.boards.any? }, :caption => :label_board_plural
144 :if => Proc.new { |p| p.boards.any? }, :caption => :label_board_plural
145 menu.push :files, { :controller => 'projects', :action => 'list_files' }, :caption => :label_attachment_plural
145 menu.push :files, { :controller => 'projects', :action => 'list_files' }, :caption => :label_attachment_plural
146 menu.push :repository, { :controller => 'repositories', :action => 'show' },
146 menu.push :repository, { :controller => 'repositories', :action => 'show' },
147 :if => Proc.new { |p| p.repository && !p.repository.new_record? }
147 :if => Proc.new { |p| p.repository && !p.repository.new_record? }
148 menu.push :settings, { :controller => 'projects', :action => 'settings' }, :last => true
148 menu.push :settings, { :controller => 'projects', :action => 'settings' }, :last => true
149 end
149 end
150
150
151 Redmine::Activity.map do |activity|
151 Redmine::Activity.map do |activity|
152 activity.register :issues, :class_name => %w(Issue Journal)
152 activity.register :issues, :class_name => %w(Issue Journal)
153 activity.register :changesets
153 activity.register :changesets
154 activity.register :news
154 activity.register :news
155 activity.register :documents, :class_name => %w(Document Attachment)
155 activity.register :documents, :class_name => %w(Document Attachment)
156 activity.register :files, :class_name => 'Attachment'
156 activity.register :files, :class_name => 'Attachment'
157 activity.register :wiki_edits, :class_name => 'WikiContent::Version', :default => false
157 activity.register :wiki_edits, :class_name => 'WikiContent::Version', :default => false
158 activity.register :messages, :default => false
158 activity.register :messages, :default => false
159 end
159 end
160
160
161 Redmine::WikiFormatting.map do |format|
161 Redmine::WikiFormatting.map do |format|
162 format.register :textile, Redmine::WikiFormatting::Textile::Formatter, Redmine::WikiFormatting::Textile::Helper
162 format.register :textile, Redmine::WikiFormatting::Textile::Formatter, Redmine::WikiFormatting::Textile::Helper
163 end
163 end
@@ -1,196 +1,208
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require 'gloc'
18 require 'gloc'
19
19
20 module Redmine
20 module Redmine
21 module MenuManager
21 module MenuManager
22 module MenuController
22 module MenuController
23 def self.included(base)
23 def self.included(base)
24 base.extend(ClassMethods)
24 base.extend(ClassMethods)
25 end
25 end
26
26
27 module ClassMethods
27 module ClassMethods
28 @@menu_items = Hash.new {|hash, key| hash[key] = {:default => key, :actions => {}}}
28 @@menu_items = Hash.new {|hash, key| hash[key] = {:default => key, :actions => {}}}
29 mattr_accessor :menu_items
29 mattr_accessor :menu_items
30
30
31 # Set the menu item name for a controller or specific actions
31 # Set the menu item name for a controller or specific actions
32 # Examples:
32 # Examples:
33 # * menu_item :tickets # => sets the menu name to :tickets for the whole controller
33 # * menu_item :tickets # => sets the menu name to :tickets for the whole controller
34 # * menu_item :tickets, :only => :list # => sets the menu name to :tickets for the 'list' action only
34 # * menu_item :tickets, :only => :list # => sets the menu name to :tickets for the 'list' action only
35 # * menu_item :tickets, :only => [:list, :show] # => sets the menu name to :tickets for 2 actions only
35 # * menu_item :tickets, :only => [:list, :show] # => sets the menu name to :tickets for 2 actions only
36 #
36 #
37 # The default menu item name for a controller is controller_name by default
37 # The default menu item name for a controller is controller_name by default
38 # Eg. the default menu item name for ProjectsController is :projects
38 # Eg. the default menu item name for ProjectsController is :projects
39 def menu_item(id, options = {})
39 def menu_item(id, options = {})
40 if actions = options[:only]
40 if actions = options[:only]
41 actions = [] << actions unless actions.is_a?(Array)
41 actions = [] << actions unless actions.is_a?(Array)
42 actions.each {|a| menu_items[controller_name.to_sym][:actions][a.to_sym] = id}
42 actions.each {|a| menu_items[controller_name.to_sym][:actions][a.to_sym] = id}
43 else
43 else
44 menu_items[controller_name.to_sym][:default] = id
44 menu_items[controller_name.to_sym][:default] = id
45 end
45 end
46 end
46 end
47 end
47 end
48
48
49 def menu_items
49 def menu_items
50 self.class.menu_items
50 self.class.menu_items
51 end
51 end
52
52
53 # Returns the menu item name according to the current action
53 # Returns the menu item name according to the current action
54 def current_menu_item
54 def current_menu_item
55 menu_items[controller_name.to_sym][:actions][action_name.to_sym] ||
55 menu_items[controller_name.to_sym][:actions][action_name.to_sym] ||
56 menu_items[controller_name.to_sym][:default]
56 menu_items[controller_name.to_sym][:default]
57 end
57 end
58 end
58 end
59
59
60 module MenuHelper
60 module MenuHelper
61 # Returns the current menu item name
61 # Returns the current menu item name
62 def current_menu_item
62 def current_menu_item
63 @controller.current_menu_item
63 @controller.current_menu_item
64 end
64 end
65
65
66 # Renders the application main menu
66 # Renders the application main menu
67 def render_main_menu(project)
67 def render_main_menu(project)
68 render_menu((project && !project.new_record?) ? :project_menu : :application_menu, project)
68 render_menu((project && !project.new_record?) ? :project_menu : :application_menu, project)
69 end
69 end
70
70
71 def render_menu(menu, project=nil)
71 def render_menu(menu, project=nil)
72 links = []
72 links = []
73 menu_items_for(menu, project) do |item, caption, url, selected|
73 menu_items_for(menu, project) do |item, caption, url, selected|
74 links << content_tag('li',
74 links << content_tag('li',
75 link_to(h(caption), url, (selected ? item.html_options.merge(:class => 'selected') : item.html_options)))
75 link_to(h(caption), url, item.html_options(:selected => selected)))
76 end
76 end
77 links.empty? ? nil : content_tag('ul', links.join("\n"))
77 links.empty? ? nil : content_tag('ul', links.join("\n"))
78 end
78 end
79
79
80 def menu_items_for(menu, project=nil)
80 def menu_items_for(menu, project=nil)
81 items = []
81 items = []
82 Redmine::MenuManager.allowed_items(menu, User.current, project).each do |item|
82 Redmine::MenuManager.allowed_items(menu, User.current, project).each do |item|
83 unless item.condition && !item.condition.call(project)
83 unless item.condition && !item.condition.call(project)
84 url = case item.url
84 url = case item.url
85 when Hash
85 when Hash
86 project.nil? ? item.url : {item.param => project}.merge(item.url)
86 project.nil? ? item.url : {item.param => project}.merge(item.url)
87 when Symbol
87 when Symbol
88 send(item.url)
88 send(item.url)
89 else
89 else
90 item.url
90 item.url
91 end
91 end
92 caption = item.caption(project)
92 caption = item.caption(project)
93 caption = l(caption) if caption.is_a?(Symbol)
93 caption = l(caption) if caption.is_a?(Symbol)
94 if block_given?
94 if block_given?
95 yield item, caption, url, (current_menu_item == item.name)
95 yield item, caption, url, (current_menu_item == item.name)
96 else
96 else
97 items << [item, caption, url, (current_menu_item == item.name)]
97 items << [item, caption, url, (current_menu_item == item.name)]
98 end
98 end
99 end
99 end
100 end
100 end
101 return block_given? ? nil : items
101 return block_given? ? nil : items
102 end
102 end
103 end
103 end
104
104
105 class << self
105 class << self
106 def map(menu_name)
106 def map(menu_name)
107 @items ||= {}
107 @items ||= {}
108 mapper = Mapper.new(menu_name.to_sym, @items)
108 mapper = Mapper.new(menu_name.to_sym, @items)
109 if block_given?
109 if block_given?
110 yield mapper
110 yield mapper
111 else
111 else
112 mapper
112 mapper
113 end
113 end
114 end
114 end
115
115
116 def items(menu_name)
116 def items(menu_name)
117 @items[menu_name.to_sym] || []
117 @items[menu_name.to_sym] || []
118 end
118 end
119
119
120 def allowed_items(menu_name, user, project)
120 def allowed_items(menu_name, user, project)
121 project ? items(menu_name).select {|item| user && user.allowed_to?(item.url, project)} : items(menu_name)
121 project ? items(menu_name).select {|item| user && user.allowed_to?(item.url, project)} : items(menu_name)
122 end
122 end
123 end
123 end
124
124
125 class Mapper
125 class Mapper
126 def initialize(menu, items)
126 def initialize(menu, items)
127 items[menu] ||= []
127 items[menu] ||= []
128 @menu = menu
128 @menu = menu
129 @menu_items = items[menu]
129 @menu_items = items[menu]
130 end
130 end
131
131
132 @@last_items_count = Hash.new {|h,k| h[k] = 0}
132 @@last_items_count = Hash.new {|h,k| h[k] = 0}
133
133
134 # Adds an item at the end of the menu. Available options:
134 # Adds an item at the end of the menu. Available options:
135 # * param: the parameter name that is used for the project id (default is :id)
135 # * param: the parameter name that is used for the project id (default is :id)
136 # * if: a Proc that is called before rendering the item, the item is displayed only if it returns true
136 # * if: a Proc that is called before rendering the item, the item is displayed only if it returns true
137 # * caption that can be:
137 # * caption that can be:
138 # * a localized string Symbol
138 # * a localized string Symbol
139 # * a String
139 # * a String
140 # * a Proc that can take the project as argument
140 # * a Proc that can take the project as argument
141 # * before, after: specify where the menu item should be inserted (eg. :after => :activity)
141 # * before, after: specify where the menu item should be inserted (eg. :after => :activity)
142 # * last: menu item will stay at the end (eg. :last => true)
142 # * last: menu item will stay at the end (eg. :last => true)
143 # * html_options: a hash of html options that are passed to link_to
143 # * html_options: a hash of html options that are passed to link_to
144 def push(name, url, options={})
144 def push(name, url, options={})
145 options = options.dup
145 options = options.dup
146
146
147 # menu item position
147 # menu item position
148 if before = options.delete(:before)
148 if before = options.delete(:before)
149 position = @menu_items.collect(&:name).index(before)
149 position = @menu_items.collect(&:name).index(before)
150 elsif after = options.delete(:after)
150 elsif after = options.delete(:after)
151 position = @menu_items.collect(&:name).index(after)
151 position = @menu_items.collect(&:name).index(after)
152 position += 1 unless position.nil?
152 position += 1 unless position.nil?
153 elsif options.delete(:last)
153 elsif options.delete(:last)
154 position = @menu_items.size
154 position = @menu_items.size
155 @@last_items_count[@menu] += 1
155 @@last_items_count[@menu] += 1
156 end
156 end
157 # default position
157 # default position
158 position ||= @menu_items.size - @@last_items_count[@menu]
158 position ||= @menu_items.size - @@last_items_count[@menu]
159
159
160 @menu_items.insert(position, MenuItem.new(name, url, options))
160 @menu_items.insert(position, MenuItem.new(name, url, options))
161 end
161 end
162
162
163 # Removes a menu item
163 # Removes a menu item
164 def delete(name)
164 def delete(name)
165 @menu_items.delete_if {|i| i.name == name}
165 @menu_items.delete_if {|i| i.name == name}
166 end
166 end
167 end
167 end
168
168
169 class MenuItem
169 class MenuItem
170 include GLoc
170 include GLoc
171 attr_reader :name, :url, :param, :condition, :html_options
171 attr_reader :name, :url, :param, :condition
172
172
173 def initialize(name, url, options)
173 def initialize(name, url, options)
174 raise "Invalid option :if for menu item '#{name}'" if options[:if] && !options[:if].respond_to?(:call)
174 raise "Invalid option :if for menu item '#{name}'" if options[:if] && !options[:if].respond_to?(:call)
175 raise "Invalid option :html for menu item '#{name}'" if options[:html] && !options[:html].is_a?(Hash)
175 raise "Invalid option :html for menu item '#{name}'" if options[:html] && !options[:html].is_a?(Hash)
176 @name = name
176 @name = name
177 @url = url
177 @url = url
178 @condition = options[:if]
178 @condition = options[:if]
179 @param = options[:param] || :id
179 @param = options[:param] || :id
180 @caption = options[:caption]
180 @caption = options[:caption]
181 @html_options = options[:html] || {}
181 @html_options = options[:html] || {}
182 # Adds a unique class to each menu item based on its name
183 @html_options[:class] = [@html_options[:class], @name.to_s.dasherize].compact.join(' ')
182 end
184 end
183
185
184 def caption(project=nil)
186 def caption(project=nil)
185 if @caption.is_a?(Proc)
187 if @caption.is_a?(Proc)
186 c = @caption.call(project).to_s
188 c = @caption.call(project).to_s
187 c = @name.to_s.humanize if c.blank?
189 c = @name.to_s.humanize if c.blank?
188 c
190 c
189 else
191 else
190 # check if localized string exists on first render (after GLoc strings are loaded)
192 # check if localized string exists on first render (after GLoc strings are loaded)
191 @caption_key ||= (@caption || (l_has_string?("label_#{@name}".to_sym) ? "label_#{@name}".to_sym : @name.to_s.humanize))
193 @caption_key ||= (@caption || (l_has_string?("label_#{@name}".to_sym) ? "label_#{@name}".to_sym : @name.to_s.humanize))
192 end
194 end
193 end
195 end
196
197 def html_options(options={})
198 if options[:selected]
199 o = @html_options.dup
200 o[:class] += ' selected'
201 o
202 else
203 @html_options
204 end
205 end
194 end
206 end
195 end
207 end
196 end
208 end
@@ -1,41 +1,41
1 @import url(../../../stylesheets/application.css);
1 @import url(../../../stylesheets/application.css);
2
2
3 body{ color:#303030; background:#e8eaec; }
3 body{ color:#303030; background:#e8eaec; }
4
4
5 #top-menu { font-size: 80%; height: 2em; padding-top: 0.5em; background-color: #578bb8; }
5 #top-menu { font-size: 80%; height: 2em; padding-top: 0.5em; background-color: #578bb8; }
6 #top-menu a { font-weight: bold; }
6 #top-menu a { font-weight: bold; }
7 #header { background: #467aa7; height:5.8em; padding: 10px 0 0 0; }
7 #header { background: #467aa7; height:5.8em; padding: 10px 0 0 0; }
8 #header h1 { margin-left: 6px; }
8 #header h1 { margin-left: 6px; }
9 #quick-search { margin-right: 6px; }
9 #quick-search { margin-right: 6px; }
10 #main-menu { background-color: #578bb8; left: 0; border-top: 1px solid #fff; width: 100%; }
10 #main-menu { background-color: #578bb8; left: 0; border-top: 1px solid #fff; width: 100%; }
11 #main-menu li { margin: 0; padding: 0; }
11 #main-menu li { margin: 0; padding: 0; }
12 #main-menu li a { background-color: #578bb8; border-right: 1px solid #fff; font-size: 90%; padding: 4px 8px 4px 8px; font-weight: bold; }
12 #main-menu li a { background-color: #578bb8; border-right: 1px solid #fff; font-size: 90%; padding: 4px 8px 4px 8px; font-weight: bold; }
13 #main-menu li a:hover { background-color: #80b0da; color: #ffffff; }
13 #main-menu li a:hover { background-color: #80b0da; color: #ffffff; }
14 #main-menu li a.selected, #main-menu li a.selected:hover { background-color: #80b0da; color: #ffffff; }
14 #main-menu li a.selected, #main-menu li a.selected:hover { background-color: #80b0da; color: #ffffff; }
15
15
16 #footer { background-color: #578bb8; border: 0; color: #fff;}
16 #footer { background-color: #578bb8; border: 0; color: #fff;}
17 #footer a { color: #fff; font-weight: bold; }
17 #footer a { color: #fff; font-weight: bold; }
18
18
19 #main { font:90% Verdana,Tahoma,Arial,sans-serif; background: #e8eaec; }
19 #main { font:90% Verdana,Tahoma,Arial,sans-serif; background: #e8eaec; }
20 #main a { font-weight: bold; color: #467aa7;}
20 #main a { font-weight: bold; color: #467aa7;}
21 #main a:hover { color: #2a5a8a; text-decoration: underline; }
21 #main a:hover { color: #2a5a8a; text-decoration: underline; }
22 #content { background: #fff; }
22 #content { background: #fff; }
23 #content .tabs ul { bottom:-1px; }
23 #content .tabs ul { bottom:-1px; }
24
24
25 h2, h3, h4, .wiki h1, .wiki h2, .wiki h3 { border-bottom: 0px; color:#606060; font-family: Trebuchet MS,Georgia,"Times New Roman",serif; }
25 h2, h3, h4, .wiki h1, .wiki h2, .wiki h3 { border-bottom: 0px; color:#606060; font-family: Trebuchet MS,Georgia,"Times New Roman",serif; }
26 h2, .wiki h1 { letter-spacing:-1px; }
26 h2, .wiki h1 { letter-spacing:-1px; }
27 h4 { border-bottom: dotted 1px #c0c0c0; }
27 h4 { border-bottom: dotted 1px #c0c0c0; }
28
28
29 #top-menu a.home, #top-menu a.mypage, #top-menu a.projects, #top-menu a.admin, #top-menu a.help {
29 #top-menu a.home, #top-menu a.my-page, #top-menu a.projects, #top-menu a.administration, #top-menu a.help {
30 background-position: 0% 40%;
30 background-position: 0% 40%;
31 background-repeat: no-repeat;
31 background-repeat: no-repeat;
32 padding-left: 20px;
32 padding-left: 20px;
33 padding-top: 2px;
33 padding-top: 2px;
34 padding-bottom: 3px;
34 padding-bottom: 3px;
35 }
35 }
36
36
37 #top-menu a.home { background-image: url(../../../images/home.png); }
37 #top-menu a.home { background-image: url(../../../images/home.png); }
38 #top-menu a.mypage { background-image: url(../../../images/user_page.png); }
38 #top-menu a.my-page { background-image: url(../../../images/user_page.png); }
39 #top-menu a.projects { background-image: url(../../../images/projects.png); }
39 #top-menu a.projects { background-image: url(../../../images/projects.png); }
40 #top-menu a.admin { background-image: url(../../../images/admin.png); }
40 #top-menu a.administration { background-image: url(../../../images/admin.png); }
41 #top-menu a.help { background-image: url(../../../images/help.png); }
41 #top-menu a.help { background-image: url(../../../images/help.png); }
@@ -1,274 +1,277
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2008 Jean-Philippe Lang
2 # Copyright (C) 2006-2008 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.dirname(__FILE__) + '/../test_helper'
18 require File.dirname(__FILE__) + '/../test_helper'
19 require 'projects_controller'
19 require 'projects_controller'
20
20
21 # Re-raise errors caught by the controller.
21 # Re-raise errors caught by the controller.
22 class ProjectsController; def rescue_action(e) raise e end; end
22 class ProjectsController; def rescue_action(e) raise e end; end
23
23
24 class ProjectsControllerTest < Test::Unit::TestCase
24 class ProjectsControllerTest < Test::Unit::TestCase
25 fixtures :projects, :versions, :users, :roles, :members, :issues, :journals, :journal_details,
25 fixtures :projects, :versions, :users, :roles, :members, :issues, :journals, :journal_details,
26 :trackers, :projects_trackers, :issue_statuses, :enabled_modules, :enumerations, :boards, :messages
26 :trackers, :projects_trackers, :issue_statuses, :enabled_modules, :enumerations, :boards, :messages
27
27
28 def setup
28 def setup
29 @controller = ProjectsController.new
29 @controller = ProjectsController.new
30 @request = ActionController::TestRequest.new
30 @request = ActionController::TestRequest.new
31 @response = ActionController::TestResponse.new
31 @response = ActionController::TestResponse.new
32 @request.session[:user_id] = nil
32 @request.session[:user_id] = nil
33 Setting.default_language = 'en'
33 Setting.default_language = 'en'
34 end
34 end
35
35
36 def test_index
36 def test_index
37 get :index
37 get :index
38 assert_response :success
38 assert_response :success
39 assert_template 'index'
39 assert_template 'index'
40 assert_not_nil assigns(:project_tree)
40 assert_not_nil assigns(:project_tree)
41 # Root project as hash key
41 # Root project as hash key
42 assert assigns(:project_tree).keys.include?(Project.find(1))
42 assert assigns(:project_tree).keys.include?(Project.find(1))
43 # Subproject in corresponding value
43 # Subproject in corresponding value
44 assert assigns(:project_tree)[Project.find(1)].include?(Project.find(3))
44 assert assigns(:project_tree)[Project.find(1)].include?(Project.find(3))
45 end
45 end
46
46
47 def test_index_atom
47 def test_index_atom
48 get :index, :format => 'atom'
48 get :index, :format => 'atom'
49 assert_response :success
49 assert_response :success
50 assert_template 'common/feed.atom.rxml'
50 assert_template 'common/feed.atom.rxml'
51 assert_select 'feed>title', :text => 'Redmine: Latest projects'
51 assert_select 'feed>title', :text => 'Redmine: Latest projects'
52 assert_select 'feed>entry', :count => Project.count(:conditions => Project.visible_by(User.current))
52 assert_select 'feed>entry', :count => Project.count(:conditions => Project.visible_by(User.current))
53 end
53 end
54
54
55 def test_show_by_id
55 def test_show_by_id
56 get :show, :id => 1
56 get :show, :id => 1
57 assert_response :success
57 assert_response :success
58 assert_template 'show'
58 assert_template 'show'
59 assert_not_nil assigns(:project)
59 assert_not_nil assigns(:project)
60 end
60 end
61
61
62 def test_show_by_identifier
62 def test_show_by_identifier
63 get :show, :id => 'ecookbook'
63 get :show, :id => 'ecookbook'
64 assert_response :success
64 assert_response :success
65 assert_template 'show'
65 assert_template 'show'
66 assert_not_nil assigns(:project)
66 assert_not_nil assigns(:project)
67 assert_equal Project.find_by_identifier('ecookbook'), assigns(:project)
67 assert_equal Project.find_by_identifier('ecookbook'), assigns(:project)
68 end
68 end
69
69
70 def test_private_subprojects_hidden
70 def test_private_subprojects_hidden
71 get :show, :id => 'ecookbook'
71 get :show, :id => 'ecookbook'
72 assert_response :success
72 assert_response :success
73 assert_template 'show'
73 assert_template 'show'
74 assert_no_tag :tag => 'a', :content => /Private child/
74 assert_no_tag :tag => 'a', :content => /Private child/
75 end
75 end
76
76
77 def test_private_subprojects_visible
77 def test_private_subprojects_visible
78 @request.session[:user_id] = 2 # manager who is a member of the private subproject
78 @request.session[:user_id] = 2 # manager who is a member of the private subproject
79 get :show, :id => 'ecookbook'
79 get :show, :id => 'ecookbook'
80 assert_response :success
80 assert_response :success
81 assert_template 'show'
81 assert_template 'show'
82 assert_tag :tag => 'a', :content => /Private child/
82 assert_tag :tag => 'a', :content => /Private child/
83 end
83 end
84
84
85 def test_settings
85 def test_settings
86 @request.session[:user_id] = 2 # manager
86 @request.session[:user_id] = 2 # manager
87 get :settings, :id => 1
87 get :settings, :id => 1
88 assert_response :success
88 assert_response :success
89 assert_template 'settings'
89 assert_template 'settings'
90 end
90 end
91
91
92 def test_edit
92 def test_edit
93 @request.session[:user_id] = 2 # manager
93 @request.session[:user_id] = 2 # manager
94 post :edit, :id => 1, :project => {:name => 'Test changed name',
94 post :edit, :id => 1, :project => {:name => 'Test changed name',
95 :issue_custom_field_ids => ['']}
95 :issue_custom_field_ids => ['']}
96 assert_redirected_to 'projects/settings/ecookbook'
96 assert_redirected_to 'projects/settings/ecookbook'
97 project = Project.find(1)
97 project = Project.find(1)
98 assert_equal 'Test changed name', project.name
98 assert_equal 'Test changed name', project.name
99 end
99 end
100
100
101 def test_get_destroy
101 def test_get_destroy
102 @request.session[:user_id] = 1 # admin
102 @request.session[:user_id] = 1 # admin
103 get :destroy, :id => 1
103 get :destroy, :id => 1
104 assert_response :success
104 assert_response :success
105 assert_template 'destroy'
105 assert_template 'destroy'
106 assert_not_nil Project.find_by_id(1)
106 assert_not_nil Project.find_by_id(1)
107 end
107 end
108
108
109 def test_post_destroy
109 def test_post_destroy
110 @request.session[:user_id] = 1 # admin
110 @request.session[:user_id] = 1 # admin
111 post :destroy, :id => 1, :confirm => 1
111 post :destroy, :id => 1, :confirm => 1
112 assert_redirected_to 'admin/projects'
112 assert_redirected_to 'admin/projects'
113 assert_nil Project.find_by_id(1)
113 assert_nil Project.find_by_id(1)
114 end
114 end
115
115
116 def test_list_files
116 def test_list_files
117 get :list_files, :id => 1
117 get :list_files, :id => 1
118 assert_response :success
118 assert_response :success
119 assert_template 'list_files'
119 assert_template 'list_files'
120 assert_not_nil assigns(:versions)
120 assert_not_nil assigns(:versions)
121 end
121 end
122
122
123 def test_changelog
123 def test_changelog
124 get :changelog, :id => 1
124 get :changelog, :id => 1
125 assert_response :success
125 assert_response :success
126 assert_template 'changelog'
126 assert_template 'changelog'
127 assert_not_nil assigns(:versions)
127 assert_not_nil assigns(:versions)
128 end
128 end
129
129
130 def test_roadmap
130 def test_roadmap
131 get :roadmap, :id => 1
131 get :roadmap, :id => 1
132 assert_response :success
132 assert_response :success
133 assert_template 'roadmap'
133 assert_template 'roadmap'
134 assert_not_nil assigns(:versions)
134 assert_not_nil assigns(:versions)
135 # Version with no date set appears
135 # Version with no date set appears
136 assert assigns(:versions).include?(Version.find(3))
136 assert assigns(:versions).include?(Version.find(3))
137 # Completed version doesn't appear
137 # Completed version doesn't appear
138 assert !assigns(:versions).include?(Version.find(1))
138 assert !assigns(:versions).include?(Version.find(1))
139 end
139 end
140
140
141 def test_roadmap_with_completed_versions
141 def test_roadmap_with_completed_versions
142 get :roadmap, :id => 1, :completed => 1
142 get :roadmap, :id => 1, :completed => 1
143 assert_response :success
143 assert_response :success
144 assert_template 'roadmap'
144 assert_template 'roadmap'
145 assert_not_nil assigns(:versions)
145 assert_not_nil assigns(:versions)
146 # Version with no date set appears
146 # Version with no date set appears
147 assert assigns(:versions).include?(Version.find(3))
147 assert assigns(:versions).include?(Version.find(3))
148 # Completed version appears
148 # Completed version appears
149 assert assigns(:versions).include?(Version.find(1))
149 assert assigns(:versions).include?(Version.find(1))
150 end
150 end
151
151
152 def test_project_activity
152 def test_project_activity
153 get :activity, :id => 1, :with_subprojects => 0
153 get :activity, :id => 1, :with_subprojects => 0
154 assert_response :success
154 assert_response :success
155 assert_template 'activity'
155 assert_template 'activity'
156 assert_not_nil assigns(:events_by_day)
156 assert_not_nil assigns(:events_by_day)
157
157
158 assert_tag :tag => "h3",
158 assert_tag :tag => "h3",
159 :content => /#{2.days.ago.to_date.day}/,
159 :content => /#{2.days.ago.to_date.day}/,
160 :sibling => { :tag => "dl",
160 :sibling => { :tag => "dl",
161 :child => { :tag => "dt",
161 :child => { :tag => "dt",
162 :attributes => { :class => /issue-edit/ },
162 :attributes => { :class => /issue-edit/ },
163 :child => { :tag => "a",
163 :child => { :tag => "a",
164 :content => /(#{IssueStatus.find(2).name})/,
164 :content => /(#{IssueStatus.find(2).name})/,
165 }
165 }
166 }
166 }
167 }
167 }
168 end
168 end
169
169
170 def test_previous_project_activity
170 def test_previous_project_activity
171 get :activity, :id => 1, :from => 3.days.ago.to_date
171 get :activity, :id => 1, :from => 3.days.ago.to_date
172 assert_response :success
172 assert_response :success
173 assert_template 'activity'
173 assert_template 'activity'
174 assert_not_nil assigns(:events_by_day)
174 assert_not_nil assigns(:events_by_day)
175
175
176 assert_tag :tag => "h3",
176 assert_tag :tag => "h3",
177 :content => /#{3.day.ago.to_date.day}/,
177 :content => /#{3.day.ago.to_date.day}/,
178 :sibling => { :tag => "dl",
178 :sibling => { :tag => "dl",
179 :child => { :tag => "dt",
179 :child => { :tag => "dt",
180 :attributes => { :class => /issue/ },
180 :attributes => { :class => /issue/ },
181 :child => { :tag => "a",
181 :child => { :tag => "a",
182 :content => /#{Issue.find(1).subject}/,
182 :content => /#{Issue.find(1).subject}/,
183 }
183 }
184 }
184 }
185 }
185 }
186 end
186 end
187
187
188 def test_global_activity
188 def test_global_activity
189 get :activity
189 get :activity
190 assert_response :success
190 assert_response :success
191 assert_template 'activity'
191 assert_template 'activity'
192 assert_not_nil assigns(:events_by_day)
192 assert_not_nil assigns(:events_by_day)
193
193
194 assert_tag :tag => "h3",
194 assert_tag :tag => "h3",
195 :content => /#{5.day.ago.to_date.day}/,
195 :content => /#{5.day.ago.to_date.day}/,
196 :sibling => { :tag => "dl",
196 :sibling => { :tag => "dl",
197 :child => { :tag => "dt",
197 :child => { :tag => "dt",
198 :attributes => { :class => /issue/ },
198 :attributes => { :class => /issue/ },
199 :child => { :tag => "a",
199 :child => { :tag => "a",
200 :content => /#{Issue.find(5).subject}/,
200 :content => /#{Issue.find(5).subject}/,
201 }
201 }
202 }
202 }
203 }
203 }
204 end
204 end
205
205
206 def test_activity_atom_feed
206 def test_activity_atom_feed
207 get :activity, :format => 'atom'
207 get :activity, :format => 'atom'
208 assert_response :success
208 assert_response :success
209 assert_template 'common/feed.atom.rxml'
209 assert_template 'common/feed.atom.rxml'
210 end
210 end
211
211
212 def test_archive
212 def test_archive
213 @request.session[:user_id] = 1 # admin
213 @request.session[:user_id] = 1 # admin
214 post :archive, :id => 1
214 post :archive, :id => 1
215 assert_redirected_to 'admin/projects'
215 assert_redirected_to 'admin/projects'
216 assert !Project.find(1).active?
216 assert !Project.find(1).active?
217 end
217 end
218
218
219 def test_unarchive
219 def test_unarchive
220 @request.session[:user_id] = 1 # admin
220 @request.session[:user_id] = 1 # admin
221 Project.find(1).archive
221 Project.find(1).archive
222 post :unarchive, :id => 1
222 post :unarchive, :id => 1
223 assert_redirected_to 'admin/projects'
223 assert_redirected_to 'admin/projects'
224 assert Project.find(1).active?
224 assert Project.find(1).active?
225 end
225 end
226
226
227 def test_project_menu
227 def test_project_menu
228 assert_no_difference 'Redmine::MenuManager.items(:project_menu).size' do
228 assert_no_difference 'Redmine::MenuManager.items(:project_menu).size' do
229 Redmine::MenuManager.map :project_menu do |menu|
229 Redmine::MenuManager.map :project_menu do |menu|
230 menu.push :foo, { :controller => 'projects', :action => 'show' }, :cation => 'Foo'
230 menu.push :foo, { :controller => 'projects', :action => 'show' }, :cation => 'Foo'
231 menu.push :bar, { :controller => 'projects', :action => 'show' }, :before => :activity
231 menu.push :bar, { :controller => 'projects', :action => 'show' }, :before => :activity
232 menu.push :hello, { :controller => 'projects', :action => 'show' }, :caption => Proc.new {|p| p.name.upcase }, :after => :bar
232 menu.push :hello, { :controller => 'projects', :action => 'show' }, :caption => Proc.new {|p| p.name.upcase }, :after => :bar
233 end
233 end
234
234
235 get :show, :id => 1
235 get :show, :id => 1
236 assert_tag :div, :attributes => { :id => 'main-menu' },
236 assert_tag :div, :attributes => { :id => 'main-menu' },
237 :descendant => { :tag => 'li', :child => { :tag => 'a', :content => 'Foo' } }
237 :descendant => { :tag => 'li', :child => { :tag => 'a', :content => 'Foo',
238 :attributes => { :class => 'foo' } } }
238
239
239 assert_tag :div, :attributes => { :id => 'main-menu' },
240 assert_tag :div, :attributes => { :id => 'main-menu' },
240 :descendant => { :tag => 'li', :child => { :tag => 'a', :content => 'Bar' },
241 :descendant => { :tag => 'li', :child => { :tag => 'a', :content => 'Bar',
242 :attributes => { :class => 'bar' } },
241 :before => { :tag => 'li', :child => { :tag => 'a', :content => 'ECOOKBOOK' } } }
243 :before => { :tag => 'li', :child => { :tag => 'a', :content => 'ECOOKBOOK' } } }
242
244
243 assert_tag :div, :attributes => { :id => 'main-menu' },
245 assert_tag :div, :attributes => { :id => 'main-menu' },
244 :descendant => { :tag => 'li', :child => { :tag => 'a', :content => 'ECOOKBOOK' },
246 :descendant => { :tag => 'li', :child => { :tag => 'a', :content => 'ECOOKBOOK',
247 :attributes => { :class => 'hello' } },
245 :before => { :tag => 'li', :child => { :tag => 'a', :content => 'Activity' } } }
248 :before => { :tag => 'li', :child => { :tag => 'a', :content => 'Activity' } } }
246
249
247 # Remove the menu items
250 # Remove the menu items
248 Redmine::MenuManager.map :project_menu do |menu|
251 Redmine::MenuManager.map :project_menu do |menu|
249 menu.delete :foo
252 menu.delete :foo
250 menu.delete :bar
253 menu.delete :bar
251 menu.delete :hello
254 menu.delete :hello
252 end
255 end
253 end
256 end
254 end
257 end
255
258
256 # A hook that is manually registered later
259 # A hook that is manually registered later
257 class ProjectBasedTemplate < Redmine::Hook::ViewListener
260 class ProjectBasedTemplate < Redmine::Hook::ViewListener
258 def view_layouts_base_html_head(context)
261 def view_layouts_base_html_head(context)
259 # Adds a project stylesheet
262 # Adds a project stylesheet
260 stylesheet_link_tag(context[:project].identifier) if context[:project]
263 stylesheet_link_tag(context[:project].identifier) if context[:project]
261 end
264 end
262 end
265 end
263 # Don't use this hook now
266 # Don't use this hook now
264 Redmine::Hook.clear_listeners
267 Redmine::Hook.clear_listeners
265
268
266 def test_hook_response
269 def test_hook_response
267 Redmine::Hook.add_listener(ProjectBasedTemplate)
270 Redmine::Hook.add_listener(ProjectBasedTemplate)
268 get :show, :id => 1
271 get :show, :id => 1
269 assert_tag :tag => 'link', :attributes => {:href => '/stylesheets/ecookbook.css'},
272 assert_tag :tag => 'link', :attributes => {:href => '/stylesheets/ecookbook.css'},
270 :parent => {:tag => 'head'}
273 :parent => {:tag => 'head'}
271
274
272 Redmine::Hook.clear_listeners
275 Redmine::Hook.clear_listeners
273 end
276 end
274 end
277 end
General Comments 0
You need to be logged in to leave comments. Login now