##// END OF EJS Templates
Fixed: no :author method error on projects atom feed (#1623)....
Jean-Philippe Lang -
r1639:b5444b5fcd84
parent child
Show More
@@ -1,259 +1,260
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, :include => :user, :conditions => "#{User.table_name}.status=#{User::STATUS_ACTIVE}"
24 24 has_many :users, :through => :members
25 25 has_many :enabled_modules, :dependent => :delete_all
26 26 has_and_belongs_to_many :trackers, :order => "#{Tracker.table_name}.position"
27 27 has_many :issues, :dependent => :destroy, :order => "#{Issue.table_name}.created_on DESC", :include => [:status, :tracker]
28 28 has_many :issue_changes, :through => :issues, :source => :journals
29 29 has_many :versions, :dependent => :destroy, :order => "#{Version.table_name}.effective_date DESC, #{Version.table_name}.name DESC"
30 30 has_many :time_entries, :dependent => :delete_all
31 31 has_many :queries, :dependent => :delete_all
32 32 has_many :documents, :dependent => :destroy
33 33 has_many :news, :dependent => :delete_all, :include => :author
34 34 has_many :issue_categories, :dependent => :delete_all, :order => "#{IssueCategory.table_name}.name"
35 35 has_many :boards, :dependent => :destroy, :order => "position ASC"
36 36 has_one :repository, :dependent => :destroy
37 37 has_many :changesets, :through => :repository
38 38 has_one :wiki, :dependent => :destroy
39 39 # Custom field for the project issues
40 40 has_and_belongs_to_many :issue_custom_fields,
41 41 :class_name => 'IssueCustomField',
42 42 :order => "#{CustomField.table_name}.position",
43 43 :join_table => "#{table_name_prefix}custom_fields_projects#{table_name_suffix}",
44 44 :association_foreign_key => 'custom_field_id'
45 45
46 46 acts_as_tree :order => "name", :counter_cache => true
47 47
48 48 acts_as_customizable
49 49 acts_as_searchable :columns => ['name', 'description'], :project_key => 'id', :permission => nil
50 50 acts_as_event :title => Proc.new {|o| "#{l(:label_project)}: #{o.name}"},
51 :url => Proc.new {|o| {:controller => 'projects', :action => 'show', :id => o.id}}
51 :url => Proc.new {|o| {:controller => 'projects', :action => 'show', :id => o.id}},
52 :author => nil
52 53
53 54 attr_protected :status, :enabled_module_names
54 55
55 56 validates_presence_of :name, :identifier
56 57 validates_uniqueness_of :name, :identifier
57 58 validates_associated :repository, :wiki
58 59 validates_length_of :name, :maximum => 30
59 60 validates_length_of :homepage, :maximum => 255
60 61 validates_length_of :identifier, :in => 3..20
61 62 validates_format_of :identifier, :with => /^[a-z0-9\-]*$/
62 63
63 64 before_destroy :delete_all_members
64 65
65 66 def identifier=(identifier)
66 67 super unless identifier_frozen?
67 68 end
68 69
69 70 def identifier_frozen?
70 71 errors[:identifier].nil? && !(new_record? || identifier.blank?)
71 72 end
72 73
73 74 def issues_with_subprojects(include_subprojects=false)
74 75 conditions = nil
75 76 if include_subprojects
76 77 ids = [id] + child_ids
77 78 conditions = ["#{Project.table_name}.id IN (#{ids.join(',')}) AND #{Project.visible_by}"]
78 79 end
79 80 conditions ||= ["#{Project.table_name}.id = ?", id]
80 81 # Quick and dirty fix for Rails 2 compatibility
81 82 Issue.send(:with_scope, :find => { :conditions => conditions }) do
82 83 Version.send(:with_scope, :find => { :conditions => conditions }) do
83 84 yield
84 85 end
85 86 end
86 87 end
87 88
88 89 # returns latest created projects
89 90 # non public projects will be returned only if user is a member of those
90 91 def self.latest(user=nil, count=5)
91 92 find(:all, :limit => count, :conditions => visible_by(user), :order => "created_on DESC")
92 93 end
93 94
94 95 def self.visible_by(user=nil)
95 96 user ||= User.current
96 97 if user && user.admin?
97 98 return "#{Project.table_name}.status=#{Project::STATUS_ACTIVE}"
98 99 elsif user && user.memberships.any?
99 100 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(',')}))"
100 101 else
101 102 return "#{Project.table_name}.status=#{Project::STATUS_ACTIVE} AND #{Project.table_name}.is_public = #{connection.quoted_true}"
102 103 end
103 104 end
104 105
105 106 def self.allowed_to_condition(user, permission, options={})
106 107 statements = []
107 108 base_statement = "#{Project.table_name}.status=#{Project::STATUS_ACTIVE}"
108 109 if options[:project]
109 110 project_statement = "#{Project.table_name}.id = #{options[:project].id}"
110 111 project_statement << " OR #{Project.table_name}.parent_id = #{options[:project].id}" if options[:with_subprojects]
111 112 base_statement = "(#{project_statement}) AND (#{base_statement})"
112 113 end
113 114 if user.admin?
114 115 # no restriction
115 116 else
116 117 statements << "1=0"
117 118 if user.logged?
118 119 statements << "#{Project.table_name}.is_public = #{connection.quoted_true}" if Role.non_member.allowed_to?(permission)
119 120 allowed_project_ids = user.memberships.select {|m| m.role.allowed_to?(permission)}.collect {|m| m.project_id}
120 121 statements << "#{Project.table_name}.id IN (#{allowed_project_ids.join(',')})" if allowed_project_ids.any?
121 122 elsif Role.anonymous.allowed_to?(permission)
122 123 # anonymous user allowed on public project
123 124 statements << "#{Project.table_name}.is_public = #{connection.quoted_true}"
124 125 else
125 126 # anonymous user is not authorized
126 127 end
127 128 end
128 129 statements.empty? ? base_statement : "((#{base_statement}) AND (#{statements.join(' OR ')}))"
129 130 end
130 131
131 132 def project_condition(with_subprojects)
132 133 cond = "#{Project.table_name}.id = #{id}"
133 134 cond = "(#{cond} OR #{Project.table_name}.parent_id = #{id})" if with_subprojects
134 135 cond
135 136 end
136 137
137 138 def self.find(*args)
138 139 if args.first && args.first.is_a?(String) && !args.first.match(/^\d*$/)
139 140 project = find_by_identifier(*args)
140 141 raise ActiveRecord::RecordNotFound, "Couldn't find Project with identifier=#{args.first}" if project.nil?
141 142 project
142 143 else
143 144 super
144 145 end
145 146 end
146 147
147 148 def to_param
148 149 # id is used for projects with a numeric identifier (compatibility)
149 150 @to_param ||= (identifier.to_s =~ %r{^\d*$} ? id : identifier)
150 151 end
151 152
152 153 def active?
153 154 self.status == STATUS_ACTIVE
154 155 end
155 156
156 157 def archive
157 158 # Archive subprojects if any
158 159 children.each do |subproject|
159 160 subproject.archive
160 161 end
161 162 update_attribute :status, STATUS_ARCHIVED
162 163 end
163 164
164 165 def unarchive
165 166 return false if parent && !parent.active?
166 167 update_attribute :status, STATUS_ACTIVE
167 168 end
168 169
169 170 def active_children
170 171 children.select {|child| child.active?}
171 172 end
172 173
173 174 # Returns an array of the trackers used by the project and its sub projects
174 175 def rolled_up_trackers
175 176 @rolled_up_trackers ||=
176 177 Tracker.find(:all, :include => :projects,
177 178 :select => "DISTINCT #{Tracker.table_name}.*",
178 179 :conditions => ["#{Project.table_name}.id = ? OR #{Project.table_name}.parent_id = ?", id, id],
179 180 :order => "#{Tracker.table_name}.position")
180 181 end
181 182
182 183 # Deletes all project's members
183 184 def delete_all_members
184 185 Member.delete_all(['project_id = ?', id])
185 186 end
186 187
187 188 # Users issues can be assigned to
188 189 def assignable_users
189 190 members.select {|m| m.role.assignable?}.collect {|m| m.user}.sort
190 191 end
191 192
192 193 # Returns the mail adresses of users that should be always notified on project events
193 194 def recipients
194 195 members.select {|m| m.mail_notification? || m.user.mail_notification?}.collect {|m| m.user.mail}
195 196 end
196 197
197 198 # Returns an array of all custom fields enabled for project issues
198 199 # (explictly associated custom fields and custom fields enabled for all projects)
199 200 def all_issue_custom_fields
200 201 @all_issue_custom_fields ||= (IssueCustomField.for_all + issue_custom_fields).uniq
201 202 end
202 203
203 204 def project
204 205 self
205 206 end
206 207
207 208 def <=>(project)
208 209 name.downcase <=> project.name.downcase
209 210 end
210 211
211 212 def to_s
212 213 name
213 214 end
214 215
215 216 # Returns a short description of the projects (first lines)
216 217 def short_description(length = 255)
217 218 description.gsub(/^(.{#{length}}[^\n]*).*$/m, '\1').strip if description
218 219 end
219 220
220 221 def allows_to?(action)
221 222 if action.is_a? Hash
222 223 allowed_actions.include? "#{action[:controller]}/#{action[:action]}"
223 224 else
224 225 allowed_permissions.include? action
225 226 end
226 227 end
227 228
228 229 def module_enabled?(module_name)
229 230 module_name = module_name.to_s
230 231 enabled_modules.detect {|m| m.name == module_name}
231 232 end
232 233
233 234 def enabled_module_names=(module_names)
234 235 enabled_modules.clear
235 236 module_names = [] unless module_names && module_names.is_a?(Array)
236 237 module_names.each do |name|
237 238 enabled_modules << EnabledModule.new(:name => name.to_s)
238 239 end
239 240 end
240 241
241 242 protected
242 243 def validate
243 244 errors.add(parent_id, " must be a root project") if parent and parent.parent
244 245 errors.add_to_base("A project with subprojects can't be a subproject") if parent and children.size > 0
245 246 errors.add(:identifier, :activerecord_error_invalid) if !identifier.blank? && identifier.match(/^\d*$/)
246 247 end
247 248
248 249 private
249 250 def allowed_permissions
250 251 @allowed_permissions ||= begin
251 252 module_names = enabled_modules.collect {|m| m.name}
252 253 Redmine::AccessControl.modules_permissions(module_names).collect {|p| p.name}
253 254 end
254 255 end
255 256
256 257 def allowed_actions
257 258 @actions_allowed ||= allowed_permissions.inject([]) { |actions, permission| actions += Redmine::AccessControl.allowed_actions(permission) }.flatten
258 259 end
259 260 end
@@ -1,307 +1,315
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 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 require File.dirname(__FILE__) + '/../test_helper'
19 19 require 'projects_controller'
20 20
21 21 # Re-raise errors caught by the controller.
22 22 class ProjectsController; def rescue_action(e) raise e end; end
23 23
24 24 class ProjectsControllerTest < Test::Unit::TestCase
25 25 fixtures :projects, :versions, :users, :roles, :members, :issues, :journals, :journal_details,
26 26 :trackers, :projects_trackers, :issue_statuses, :enabled_modules, :enumerations, :boards, :messages
27 27
28 28 def setup
29 29 @controller = ProjectsController.new
30 30 @request = ActionController::TestRequest.new
31 31 @response = ActionController::TestResponse.new
32 32 @request.session[:user_id] = nil
33 33 end
34 34
35 35 def test_index
36 36 get :index
37 37 assert_response :success
38 38 assert_template 'index'
39 39 assert_not_nil assigns(:project_tree)
40 40 # Root project as hash key
41 41 assert assigns(:project_tree).keys.include?(Project.find(1))
42 42 # Subproject in corresponding value
43 43 assert assigns(:project_tree)[Project.find(1)].include?(Project.find(3))
44 44 end
45
46 def test_index_atom
47 get :index, :format => 'atom'
48 assert_response :success
49 assert_template 'common/feed.atom.rxml'
50 assert_select 'feed>title', :text => 'Redmine: Latest projects'
51 assert_select 'feed>entry', :count => Project.count(:conditions => Project.visible_by(User.current))
52 end
45 53
46 54 def test_show_by_id
47 55 get :show, :id => 1
48 56 assert_response :success
49 57 assert_template 'show'
50 58 assert_not_nil assigns(:project)
51 59 end
52 60
53 61 def test_show_by_identifier
54 62 get :show, :id => 'ecookbook'
55 63 assert_response :success
56 64 assert_template 'show'
57 65 assert_not_nil assigns(:project)
58 66 assert_equal Project.find_by_identifier('ecookbook'), assigns(:project)
59 67 end
60 68
61 69 def test_private_subprojects_hidden
62 70 get :show, :id => 'ecookbook'
63 71 assert_response :success
64 72 assert_template 'show'
65 73 assert_no_tag :tag => 'a', :content => /Private child/
66 74 end
67 75
68 76 def test_private_subprojects_visible
69 77 @request.session[:user_id] = 2 # manager who is a member of the private subproject
70 78 get :show, :id => 'ecookbook'
71 79 assert_response :success
72 80 assert_template 'show'
73 81 assert_tag :tag => 'a', :content => /Private child/
74 82 end
75 83
76 84 def test_settings
77 85 @request.session[:user_id] = 2 # manager
78 86 get :settings, :id => 1
79 87 assert_response :success
80 88 assert_template 'settings'
81 89 end
82 90
83 91 def test_edit
84 92 @request.session[:user_id] = 2 # manager
85 93 post :edit, :id => 1, :project => {:name => 'Test changed name',
86 94 :issue_custom_field_ids => ['']}
87 95 assert_redirected_to 'projects/settings/ecookbook'
88 96 project = Project.find(1)
89 97 assert_equal 'Test changed name', project.name
90 98 end
91 99
92 100 def test_get_destroy
93 101 @request.session[:user_id] = 1 # admin
94 102 get :destroy, :id => 1
95 103 assert_response :success
96 104 assert_template 'destroy'
97 105 assert_not_nil Project.find_by_id(1)
98 106 end
99 107
100 108 def test_post_destroy
101 109 @request.session[:user_id] = 1 # admin
102 110 post :destroy, :id => 1, :confirm => 1
103 111 assert_redirected_to 'admin/projects'
104 112 assert_nil Project.find_by_id(1)
105 113 end
106 114
107 115 def test_list_files
108 116 get :list_files, :id => 1
109 117 assert_response :success
110 118 assert_template 'list_files'
111 119 assert_not_nil assigns(:versions)
112 120 end
113 121
114 122 def test_changelog
115 123 get :changelog, :id => 1
116 124 assert_response :success
117 125 assert_template 'changelog'
118 126 assert_not_nil assigns(:versions)
119 127 end
120 128
121 129 def test_roadmap
122 130 get :roadmap, :id => 1
123 131 assert_response :success
124 132 assert_template 'roadmap'
125 133 assert_not_nil assigns(:versions)
126 134 # Version with no date set appears
127 135 assert assigns(:versions).include?(Version.find(3))
128 136 # Completed version doesn't appear
129 137 assert !assigns(:versions).include?(Version.find(1))
130 138 end
131 139
132 140 def test_roadmap_with_completed_versions
133 141 get :roadmap, :id => 1, :completed => 1
134 142 assert_response :success
135 143 assert_template 'roadmap'
136 144 assert_not_nil assigns(:versions)
137 145 # Version with no date set appears
138 146 assert assigns(:versions).include?(Version.find(3))
139 147 # Completed version appears
140 148 assert assigns(:versions).include?(Version.find(1))
141 149 end
142 150
143 151 def test_project_activity
144 152 get :activity, :id => 1, :with_subprojects => 0
145 153 assert_response :success
146 154 assert_template 'activity'
147 155 assert_not_nil assigns(:events_by_day)
148 156 assert_not_nil assigns(:events)
149 157
150 158 # subproject issue not included by default
151 159 assert !assigns(:events).include?(Issue.find(5))
152 160
153 161 assert_tag :tag => "h3",
154 162 :content => /#{2.days.ago.to_date.day}/,
155 163 :sibling => { :tag => "dl",
156 164 :child => { :tag => "dt",
157 165 :attributes => { :class => /issue-edit/ },
158 166 :child => { :tag => "a",
159 167 :content => /(#{IssueStatus.find(2).name})/,
160 168 }
161 169 }
162 170 }
163 171
164 172 get :activity, :id => 1, :from => 3.days.ago.to_date
165 173 assert_response :success
166 174 assert_template 'activity'
167 175 assert_not_nil assigns(:events_by_day)
168 176
169 177 assert_tag :tag => "h3",
170 178 :content => /#{3.day.ago.to_date.day}/,
171 179 :sibling => { :tag => "dl",
172 180 :child => { :tag => "dt",
173 181 :attributes => { :class => /issue/ },
174 182 :child => { :tag => "a",
175 183 :content => /#{Issue.find(1).subject}/,
176 184 }
177 185 }
178 186 }
179 187 end
180 188
181 189 def test_activity_with_subprojects
182 190 get :activity, :id => 1, :with_subprojects => 1
183 191 assert_response :success
184 192 assert_template 'activity'
185 193 assert_not_nil assigns(:events)
186 194
187 195 assert assigns(:events).include?(Issue.find(1))
188 196 assert !assigns(:events).include?(Issue.find(4))
189 197 # subproject issue
190 198 assert assigns(:events).include?(Issue.find(5))
191 199 end
192 200
193 201 def test_global_activity_anonymous
194 202 get :activity
195 203 assert_response :success
196 204 assert_template 'activity'
197 205 assert_not_nil assigns(:events)
198 206
199 207 assert assigns(:events).include?(Issue.find(1))
200 208 # Issue of a private project
201 209 assert !assigns(:events).include?(Issue.find(4))
202 210 end
203 211
204 212 def test_global_activity_logged_user
205 213 @request.session[:user_id] = 2 # manager
206 214 get :activity
207 215 assert_response :success
208 216 assert_template 'activity'
209 217 assert_not_nil assigns(:events)
210 218
211 219 assert assigns(:events).include?(Issue.find(1))
212 220 # Issue of a private project the user belongs to
213 221 assert assigns(:events).include?(Issue.find(4))
214 222 end
215 223
216 224
217 225 def test_global_activity_with_all_types
218 226 get :activity, :show_issues => 1, :show_news => 1, :show_files => 1, :show_documents => 1, :show_changesets => 1, :show_wiki_pages => 1, :show_messages => 1
219 227 assert_response :success
220 228 assert_template 'activity'
221 229 assert_not_nil assigns(:events)
222 230
223 231 assert assigns(:events).include?(Issue.find(1))
224 232 assert !assigns(:events).include?(Issue.find(4))
225 233 assert assigns(:events).include?(Message.find(5))
226 234 end
227 235
228 236 def test_calendar
229 237 get :calendar, :id => 1
230 238 assert_response :success
231 239 assert_template 'calendar'
232 240 assert_not_nil assigns(:calendar)
233 241 end
234 242
235 243 def test_calendar_with_subprojects_should_not_show_private_subprojects
236 244 get :calendar, :id => 1, :with_subprojects => 1, :tracker_ids => [1, 2]
237 245 assert_response :success
238 246 assert_template 'calendar'
239 247 assert_not_nil assigns(:calendar)
240 248 assert_no_tag :tag => 'a', :content => /#6/
241 249 end
242 250
243 251 def test_calendar_with_subprojects_should_show_private_subprojects
244 252 @request.session[:user_id] = 2
245 253 get :calendar, :id => 1, :with_subprojects => 1, :tracker_ids => [1, 2]
246 254 assert_response :success
247 255 assert_template 'calendar'
248 256 assert_not_nil assigns(:calendar)
249 257 assert_tag :tag => 'a', :content => /#6/
250 258 end
251 259
252 260 def test_gantt
253 261 get :gantt, :id => 1
254 262 assert_response :success
255 263 assert_template 'gantt.rhtml'
256 264 events = assigns(:events)
257 265 assert_not_nil events
258 266 # Issue with start and due dates
259 267 i = Issue.find(1)
260 268 assert_not_nil i.due_date
261 269 assert events.include?(Issue.find(1))
262 270 # Issue with without due date but targeted to a version with date
263 271 i = Issue.find(2)
264 272 assert_nil i.due_date
265 273 assert events.include?(i)
266 274 end
267 275
268 276 def test_gantt_with_subprojects_should_not_show_private_subprojects
269 277 get :gantt, :id => 1, :with_subprojects => 1, :tracker_ids => [1, 2]
270 278 assert_response :success
271 279 assert_template 'gantt.rhtml'
272 280 assert_not_nil assigns(:events)
273 281 assert_no_tag :tag => 'a', :content => /#6/
274 282 end
275 283
276 284 def test_gantt_with_subprojects_should_show_private_subprojects
277 285 @request.session[:user_id] = 2
278 286 get :gantt, :id => 1, :with_subprojects => 1, :tracker_ids => [1, 2]
279 287 assert_response :success
280 288 assert_template 'gantt.rhtml'
281 289 assert_not_nil assigns(:events)
282 290 assert_tag :tag => 'a', :content => /#6/
283 291 end
284 292
285 293 def test_gantt_export_to_pdf
286 294 get :gantt, :id => 1, :format => 'pdf'
287 295 assert_response :success
288 296 assert_template 'gantt.rfpdf'
289 297 assert_equal 'application/pdf', @response.content_type
290 298 assert_not_nil assigns(:events)
291 299 end
292 300
293 301 def test_archive
294 302 @request.session[:user_id] = 1 # admin
295 303 post :archive, :id => 1
296 304 assert_redirected_to 'admin/projects'
297 305 assert !Project.find(1).active?
298 306 end
299 307
300 308 def test_unarchive
301 309 @request.session[:user_id] = 1 # admin
302 310 Project.find(1).archive
303 311 post :unarchive, :id => 1
304 312 assert_redirected_to 'admin/projects'
305 313 assert Project.find(1).active?
306 314 end
307 315 end
@@ -1,75 +1,76
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 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 module Redmine
19 19 module Acts
20 20 module Event
21 21 def self.included(base)
22 22 base.extend ClassMethods
23 23 end
24 24
25 25 module ClassMethods
26 26 def acts_as_event(options = {})
27 27 return if self.included_modules.include?(Redmine::Acts::Event::InstanceMethods)
28 options[:datetime] ||= :created_on
29 options[:title] ||= :title
30 options[:description] ||= :description
31 options[:author] ||= :author
32 options[:url] ||= {:controller => 'welcome'}
33 options[:type] ||= self.name.underscore.dasherize
28 default_options = { :datetime => :created_on,
29 :title => :title,
30 :description => :description,
31 :author => :author,
32 :url => {:controller => 'welcome'},
33 :type => self.name.underscore.dasherize }
34
34 35 cattr_accessor :event_options
35 self.event_options = options
36 self.event_options = default_options.merge(options)
36 37 send :include, Redmine::Acts::Event::InstanceMethods
37 38 end
38 39 end
39 40
40 41 module InstanceMethods
41 42 def self.included(base)
42 43 base.extend ClassMethods
43 44 end
44 45
45 46 %w(datetime title description author type).each do |attr|
46 47 src = <<-END_SRC
47 48 def event_#{attr}
48 49 option = event_options[:#{attr}]
49 50 if option.is_a?(Proc)
50 51 option.call(self)
51 52 elsif option.is_a?(Symbol)
52 53 send(option)
53 54 else
54 55 option
55 56 end
56 57 end
57 58 END_SRC
58 59 class_eval src, __FILE__, __LINE__
59 60 end
60 61
61 62 def event_date
62 63 event_datetime.to_date
63 64 end
64 65
65 66 def event_url(options = {})
66 67 option = event_options[:url]
67 68 (option.is_a?(Proc) ? option.call(self) : send(option)).merge(options)
68 69 end
69 70
70 71 module ClassMethods
71 72 end
72 73 end
73 74 end
74 75 end
75 76 end
General Comments 0
You need to be logged in to leave comments. Login now