##// END OF EJS Templates
Subprojects are now grouped by projects in the 'Projects' top navigation drop-down menu....
Jean-Philippe Lang -
r692:a19a0d7b9278
parent child
Show More
@@ -0,0 +1,12
1 <div id="menuAllProjects" class="menu" onmouseover="menuMouseover(event)">
2 <%= link_to l(:label_project_all), {:controller => 'projects' }, :class => "menuItem" %>
3
4 <% user_projects_by_root = User.current.projects.find(:all, :include => :parent, :limit => 20).group_by(&:root) %>
5 <% user_projects_by_root.keys.sort.each do |root| %>
6 <%= link_to root.name, {:controller => 'projects', :action => 'show', :id => root}, :class => "menuItem" %>
7 <% user_projects_by_root[root].sort.each do |project| %>
8 <% next if project == root %>
9 <%= link_to(('&#187; ' + project.name), {:controller => 'projects', :action => 'show', :id => project}, :class => "menuItem") %>
10 <% end %>
11 <% end %>
12 </div>
@@ -1,125 +1,129
1 1 # redMine - project management software
2 2 # Copyright (C) 2006 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 class Project < ActiveRecord::Base
19 19 # Project statuses
20 20 STATUS_ACTIVE = 1
21 21 STATUS_ARCHIVED = 9
22 22
23 23 has_many :members, :dependent => :delete_all, :include => :user, :conditions => "#{User.table_name}.status=#{User::STATUS_ACTIVE}"
24 24 has_many :users, :through => :members
25 25 has_many :custom_values, :dependent => :delete_all, :as => :customized
26 26 has_many :issues, :dependent => :destroy, :order => "#{Issue.table_name}.created_on DESC", :include => [:status, :tracker]
27 27 has_many :issue_changes, :through => :issues, :source => :journals
28 28 has_many :versions, :dependent => :destroy, :order => "#{Version.table_name}.effective_date DESC, #{Version.table_name}.name DESC"
29 29 has_many :time_entries, :dependent => :delete_all
30 30 has_many :queries, :dependent => :delete_all
31 31 has_many :documents, :dependent => :destroy
32 32 has_many :news, :dependent => :delete_all, :include => :author
33 33 has_many :issue_categories, :dependent => :delete_all, :order => "#{IssueCategory.table_name}.name"
34 34 has_many :boards, :order => "position ASC"
35 35 has_one :repository, :dependent => :destroy
36 36 has_one :wiki, :dependent => :destroy
37 37 has_and_belongs_to_many :custom_fields, :class_name => 'IssueCustomField', :join_table => "#{table_name_prefix}custom_fields_projects#{table_name_suffix}", :association_foreign_key => 'custom_field_id'
38 38 acts_as_tree :order => "name", :counter_cache => true
39 39
40 40 attr_protected :status
41 41
42 42 validates_presence_of :name, :description, :identifier
43 43 validates_uniqueness_of :name, :identifier
44 44 validates_associated :custom_values, :on => :update
45 45 validates_associated :repository, :wiki
46 46 validates_length_of :name, :maximum => 30
47 47 validates_format_of :name, :with => /^[\w\s\'\-]*$/i
48 48 validates_length_of :description, :maximum => 255
49 49 validates_length_of :homepage, :maximum => 30
50 50 validates_length_of :identifier, :in => 3..12
51 51 validates_format_of :identifier, :with => /^[a-z0-9\-]*$/
52 52
53 53 def identifier=(identifier)
54 54 super unless identifier_frozen?
55 55 end
56 56
57 57 def identifier_frozen?
58 58 errors[:identifier].nil? && !(new_record? || identifier.blank?)
59 59 end
60 60
61 61 def issues_with_subprojects(include_subprojects=false)
62 62 conditions = nil
63 63 if include_subprojects && !active_children.empty?
64 64 ids = [id] + active_children.collect {|c| c.id}
65 65 conditions = ["#{Issue.table_name}.project_id IN (#{ids.join(',')})"]
66 66 end
67 67 conditions ||= ["#{Issue.table_name}.project_id = ?", id]
68 68 Issue.with_scope :find => { :conditions => conditions } do
69 69 yield
70 70 end
71 71 end
72 72
73 73 # returns latest created projects
74 74 # non public projects will be returned only if user is a member of those
75 75 def self.latest(user=nil, count=5)
76 76 find(:all, :limit => count, :conditions => visible_by(user), :order => "created_on DESC")
77 77 end
78 78
79 79 def self.visible_by(user=nil)
80 80 if user && user.admin?
81 81 return "#{Project.table_name}.status=#{Project::STATUS_ACTIVE}"
82 82 elsif user && user.memberships.any?
83 83 return "#{Project.table_name}.status=#{Project::STATUS_ACTIVE} AND (#{Project.table_name}.is_public = #{connection.quoted_true} or #{Project.table_name}.id IN (#{user.memberships.collect{|m| m.project_id}.join(',')}))"
84 84 else
85 85 return "#{Project.table_name}.status=#{Project::STATUS_ACTIVE} AND #{Project.table_name}.is_public = #{connection.quoted_true}"
86 86 end
87 87 end
88 88
89 89 def active?
90 90 self.status == STATUS_ACTIVE
91 91 end
92 92
93 93 def archive
94 94 # Archive subprojects if any
95 95 children.each do |subproject|
96 96 subproject.archive
97 97 end
98 98 update_attribute :status, STATUS_ARCHIVED
99 99 end
100 100
101 101 def unarchive
102 102 return false if parent && !parent.active?
103 103 update_attribute :status, STATUS_ACTIVE
104 104 end
105 105
106 106 def active_children
107 107 children.select {|child| child.active?}
108 108 end
109 109
110 110 # Returns an array of all custom fields enabled for project issues
111 111 # (explictly associated custom fields and custom fields enabled for all projects)
112 112 def custom_fields_for_issues(tracker)
113 113 all_custom_fields.select {|c| tracker.custom_fields.include? c }
114 114 end
115 115
116 116 def all_custom_fields
117 117 @all_custom_fields ||= (IssueCustomField.for_all + custom_fields).uniq
118 118 end
119
120 def <=>(project)
121 name <=> project.name
122 end
119 123
120 124 protected
121 125 def validate
122 126 errors.add(parent_id, " must be a root project") if parent and parent.parent
123 127 errors.add_to_base("A project with subprojects can't be a subproject") if parent and children.size > 0
124 128 end
125 129 end
@@ -1,111 +1,101
1 1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
2 2 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
3 3 <head>
4 4 <title><%= Setting.app_title + (@html_title ? ": #{@html_title}" : "") %></title>
5 5 <meta http-equiv="content-type" content="text/html; charset=utf-8" />
6 6 <meta name="description" content="<%= Redmine::Info.app_name %>" />
7 7 <meta name="keywords" content="issue,bug,tracker" />
8 8 <!--[if IE]>
9 9 <style type="text/css">
10 10 body {behavior: url(<%= stylesheet_path "csshover.htc" %>);}
11 11 </style>
12 12 <![endif]-->
13 13 <%= stylesheet_link_tag "application" %>
14 14 <%= stylesheet_link_tag "print", :media => "print" %>
15 15 <%= javascript_include_tag :defaults %>
16 16 <%= javascript_include_tag 'menu' %>
17 17 <%= stylesheet_link_tag 'jstoolbar' %>
18 18 <!-- page specific tags --><%= yield :header_tags %>
19 19 </head>
20 20
21 21 <body>
22 22 <div id="container" >
23 23
24 24 <div id="header">
25 25 <div style="float: left;">
26 26 <h1><%= Setting.app_title %></h1>
27 27 <h2><%= Setting.app_subtitle %></h2>
28 28 </div>
29 29 <div style="float: right; padding-right: 1em; padding-top: 0.2em;">
30 30 <% if User.current.logged? %><small><%=l(:label_logged_as)%> <strong><%= User.current.login %></strong> -</small><% end %>
31 31 <small><%= toggle_link l(:label_search), 'quick-search-form', :focus => 'quick-search-input' %></small>
32 32 <% form_tag({:controller => 'search', :action => 'index', :id => @project}, :method => :get, :id => 'quick-search-form', :style => "display:none;" ) do %>
33 33 <%= text_field_tag 'q', @question, :size => 15, :class => 'small', :id => 'quick-search-input' %>
34 34 <% end %>
35 35 </div>
36 36 </div>
37 37
38 38 <div id="navigation">
39 39 <ul>
40 40 <li><%= link_to l(:label_home), { :controller => 'welcome' }, :class => "icon icon-home" %></li>
41 41 <li><%= link_to l(:label_my_page), { :controller => 'my', :action => 'page'}, :class => "icon icon-mypage" %></li>
42 42
43 43 <% if User.current.memberships.any? %>
44 44 <li class="submenu"><%= link_to l(:label_project_plural), { :controller => 'projects' }, :class => "icon icon-projects", :onmouseover => "buttonMouseover(event, 'menuAllProjects');" %></li>
45 45 <% else %>
46 46 <li><%= link_to l(:label_project_plural), { :controller => 'projects' }, :class => "icon icon-projects" %></li>
47 47 <% end %>
48 48
49 49 <% if User.current.logged? %>
50 50 <li><%= link_to l(:label_my_account), { :controller => 'my', :action => 'account' }, :class => "icon icon-user" %></li>
51 51 <% end %>
52 52
53 53 <% if User.current.admin? %>
54 54 <li class="submenu"><%= link_to l(:label_administration), { :controller => 'admin' }, :class => "icon icon-admin", :onmouseover => "buttonMouseover(event, 'menuAdmin');" %></li>
55 55 <% end %>
56 56
57 57 <li class="right"><%= link_to l(:label_help), { :controller => 'help', :ctrl => params[:controller], :page => params[:action] }, :onclick => "window.open(this.href); return false;", :class => "icon icon-help" %></li>
58 58
59 59 <% if User.current.logged? %>
60 60 <li class="right"><%= link_to l(:label_logout), { :controller => 'account', :action => 'logout' }, :class => "icon icon-user" %></li>
61 61 <% else %>
62 62 <li class="right"><%= link_to l(:label_login), { :controller => 'account', :action => 'login' }, :class => "icon icon-user" %></li>
63 63 <% end %>
64 64 </ul>
65 65 </div>
66 66
67 <% if User.current.admin? %>
68 <%= render :partial => 'admin/menu' %>
69 <% end %>
70
71 <% if User.current.memberships.any? %>
72 <div id="menuAllProjects" class="menu" onmouseover="menuMouseover(event)">
73 <%= link_to l(:label_project_all), {:controller => 'projects' }, :class => "menuItem" %>
74 <% User.current.memberships.find(:all, :limit => 20).each do |membership| %>
75 <%= link_to membership.project.name, {:controller => 'projects',:action => 'show', :id => membership.project }, :class => "menuItem" %>
76 <% end %>
77 </div>
78 <% end %>
67 <%= render(:partial => 'admin/menu') if User.current.admin? %>
68 <%= render(:partial => 'layouts/projects_menu') if User.current.memberships.any? %>
79 69
80 70 <div id="subcontent">
81 71 <% if @project && !@project.new_record? %>
82 72 <h2><%= @project.name %></h2>
83 73 <ul class="menublock">
84 74 <% Redmine::MenuManager.allowed_items(:project_menu, current_role).each do |item| %>
85 75 <% unless item.condition && !item.condition.call(@project) %>
86 76 <li><%= link_to l(item.name), {item.param => @project}.merge(item.url) %></li>
87 77 <% end %>
88 78 <% end %>
89 79 </ul>
90 80 <% end %>
91 81 </div>
92 82
93 83 <div id="content">
94 84 <div id="flash">
95 85 <%= content_tag('div', flash[:error], :class => 'error') if flash[:error] %>
96 86 <%= content_tag('div', flash[:notice], :class => 'notice') if flash[:notice] %>
97 87 </div>
98 88 <%= yield %>
99 89 </div>
100 90
101 91 <div id="ajax-indicator" style="display:none;">
102 92 <span><%= l(:label_loading) %></span>
103 93 </div>
104 94
105 95 <div id="footer">
106 96 <p><%= link_to Redmine::Info.app_name, Redmine::Info.url %> <small><%= Redmine::VERSION %> &copy 2006-2007 Jean-Philippe Lang</small></p>
107 97 </div>
108 98
109 99 </div>
110 100 </body>
111 101 </html>
General Comments 0
You need to be logged in to leave comments. Login now