pluginaweek-has_messages 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG.rdoc +59 -0
- data/LICENSE +20 -0
- data/README.rdoc +101 -0
- data/Rakefile +97 -0
- data/app/models/message.rb +145 -0
- data/app/models/message_recipient.rb +120 -0
- data/db/migrate/001_create_messages.rb +16 -0
- data/db/migrate/002_create_message_recipients.rb +17 -0
- data/init.rb +1 -0
- data/lib/has_messages.rb +72 -0
- data/test/app_root/app/models/user.rb +3 -0
- data/test/app_root/config/environment.rb +9 -0
- data/test/app_root/db/migrate/001_create_users.rb +11 -0
- data/test/app_root/db/migrate/002_migrate_has_messages_to_version_2.rb +13 -0
- data/test/factory.rb +57 -0
- data/test/functional/has_messages_test.rb +139 -0
- data/test/test_helper.rb +13 -0
- data/test/unit/message_recipient_test.rb +449 -0
- data/test/unit/message_test.rb +465 -0
- metadata +92 -0
data/CHANGELOG.rdoc
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
== master
|
2
|
+
|
3
|
+
== 0.4.0 / 2009-04-19
|
4
|
+
|
5
|
+
* Add dependency on Rails 2.3
|
6
|
+
* Add dependency on plugin_test_helper 0.3.0
|
7
|
+
* Add dependency on state_machine 0.7.0
|
8
|
+
|
9
|
+
== 0.3.1 / 2009-01-11
|
10
|
+
|
11
|
+
* Use a state machine for the hidden_at field on Message/MessageRecipient
|
12
|
+
* Add dependency on state_machine 0.5.0
|
13
|
+
|
14
|
+
== 0.3.0 / 2008-12-14
|
15
|
+
|
16
|
+
* Remove the PluginAWeek namespace
|
17
|
+
* Rename Message#hide!/unhide! to Message#hide/unhide and MessageRecipient#hide!/unhide! to MessageRecipient#hide/unhide
|
18
|
+
|
19
|
+
== 0.2.0 / 2008-10-26
|
20
|
+
|
21
|
+
* Add mass-assignment protection in the Message/MessageRecipient models
|
22
|
+
* Change how the base module is included to prevent namespacing conflicts
|
23
|
+
|
24
|
+
== 0.1.3 / 2008-09-07
|
25
|
+
|
26
|
+
* Add dependency on state_machine 0.3.0
|
27
|
+
|
28
|
+
== 0.1.2 / 2008-06-29
|
29
|
+
|
30
|
+
* Add compatibility with state_machine 0.2.0
|
31
|
+
|
32
|
+
== 0.1.1 / 2008-06-22
|
33
|
+
|
34
|
+
* Remove log files from gems
|
35
|
+
|
36
|
+
== 0.1.0 / 2008-05-05
|
37
|
+
|
38
|
+
* Remove dependency on acts_as_list
|
39
|
+
* Update to reflect changes from has_states to state_machine
|
40
|
+
* Introduce an ActionMailer-type api for adding the various types of recipients
|
41
|
+
* Update migrations to support Rails 2.0 syntax
|
42
|
+
* Replace has_finder dependency with named_scope
|
43
|
+
* Remove has_messages helper
|
44
|
+
* Update documentation
|
45
|
+
|
46
|
+
== 0.0.1 / 2007-09-26
|
47
|
+
|
48
|
+
* Add state_changes fixtures for tests
|
49
|
+
* Use has_finder for tracking the deletion status of Messages/MessageRecipients
|
50
|
+
* Message#forward and Message#reply should create new instances of the current class, not always Message
|
51
|
+
* Add type field to MessageRecipient table
|
52
|
+
* Allow message senders to not be model recipients in third-party plugins
|
53
|
+
* Remove old references to Message::StateExtension
|
54
|
+
* Update with dependency on custom_callbacks
|
55
|
+
* Add documentation
|
56
|
+
* Convert dos newlines to unix newlines
|
57
|
+
* Move ReceiverMessage state into the MessageRecipient class
|
58
|
+
* Moved Build extensions out of the MessageRecipient class
|
59
|
+
* Fix determining whether classes exist in has_messages
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2006-2009 Aaron Pfeifer
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
= has_messages
|
2
|
+
|
3
|
+
+has_messages+ demonstrates a reference implementation for sending messages between users.
|
4
|
+
|
5
|
+
== Resources
|
6
|
+
|
7
|
+
API
|
8
|
+
|
9
|
+
* http://api.pluginaweek.org/has_messages
|
10
|
+
|
11
|
+
Bugs
|
12
|
+
|
13
|
+
* http://pluginaweek.lighthouseapp.com/projects/13274-has_messages
|
14
|
+
|
15
|
+
Development
|
16
|
+
|
17
|
+
* http://github.com/pluginaweek/has_messages
|
18
|
+
|
19
|
+
Source
|
20
|
+
|
21
|
+
* git://github.com/pluginaweek/has_messages.git
|
22
|
+
|
23
|
+
== Description
|
24
|
+
|
25
|
+
Messaging between users is fairly common in web applications, especially those
|
26
|
+
that support social networking. Messaging doesn't necessarily need to be
|
27
|
+
between users, but can also act as a way for the web application to send notices
|
28
|
+
and other notifications to users.
|
29
|
+
|
30
|
+
Designing and building a framework that supports this can be complex and takes
|
31
|
+
away from the business focus. This plugin can help ease that process by
|
32
|
+
demonstrating a reference implementation of these features.
|
33
|
+
|
34
|
+
== Usage
|
35
|
+
|
36
|
+
=== Adding message support
|
37
|
+
|
38
|
+
class User < ActiveRecord::Base
|
39
|
+
has_messages
|
40
|
+
end
|
41
|
+
|
42
|
+
This will build the following associations:
|
43
|
+
* +messages+
|
44
|
+
* +unsent_messages+
|
45
|
+
* +sent_messages+
|
46
|
+
* +received_messages+
|
47
|
+
|
48
|
+
If you have more specific needs, you can create the same associations manually
|
49
|
+
that +has_messages+ builds. See HasMessages::MacroMethods#has_messages
|
50
|
+
for more information about the asssociations that are generated from this macro.
|
51
|
+
|
52
|
+
=== Creating new messages
|
53
|
+
|
54
|
+
message = user.messages.build
|
55
|
+
message.to user1, user2
|
56
|
+
message.subject = 'Hey!'
|
57
|
+
message.body = 'Does anyone want to go out tonight?'
|
58
|
+
message.deliver
|
59
|
+
|
60
|
+
=== Replying to messages
|
61
|
+
|
62
|
+
reply = message.reply_to_all
|
63
|
+
reply.body = "I'd love to go out!"
|
64
|
+
reply.deliver
|
65
|
+
|
66
|
+
=== Forwarding messages
|
67
|
+
|
68
|
+
forward = message.forward
|
69
|
+
forward.body = 'Interested?'
|
70
|
+
forward.deliver
|
71
|
+
|
72
|
+
=== Processing messages asynchronously
|
73
|
+
|
74
|
+
In addition to delivering messages immediately, you can also *queue* messages so
|
75
|
+
that an external application processes and delivers them. This is especially
|
76
|
+
useful for messages that need to be sent outside of the confines of the application.
|
77
|
+
|
78
|
+
To queue messages for external processing, you can use the +queue+ event,
|
79
|
+
rather than +deliver+. This will indicate to any external processes that
|
80
|
+
the message is ready to be sent.
|
81
|
+
|
82
|
+
To process queued emails, you need an external cron job that checks and sends
|
83
|
+
them like so:
|
84
|
+
|
85
|
+
Message.with_state('queued').each do |message|
|
86
|
+
message.deliver
|
87
|
+
end
|
88
|
+
|
89
|
+
== Testing
|
90
|
+
|
91
|
+
Before you can run any tests, the following gem must be installed:
|
92
|
+
* plugin_test_helper[http://github.com/pluginaweek/plugin_test_helper]
|
93
|
+
|
94
|
+
To run against a specific version of Rails:
|
95
|
+
|
96
|
+
rake test RAILS_FRAMEWORK_ROOT=/path/to/rails
|
97
|
+
|
98
|
+
== Dependencies
|
99
|
+
|
100
|
+
* Rails 2.3 or later
|
101
|
+
* state_machine[http://github.com/pluginaweek/state_machine]
|
data/Rakefile
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'rake/testtask'
|
2
|
+
require 'rake/rdoctask'
|
3
|
+
require 'rake/gempackagetask'
|
4
|
+
require 'rake/contrib/sshpublisher'
|
5
|
+
|
6
|
+
spec = Gem::Specification.new do |s|
|
7
|
+
s.name = 'has_messages'
|
8
|
+
s.version = '0.4.0'
|
9
|
+
s.platform = Gem::Platform::RUBY
|
10
|
+
s.summary = 'Demonstrates a reference implementation for sending messages between users in ActiveRecord'
|
11
|
+
s.description = s.summary
|
12
|
+
|
13
|
+
s.files = FileList['{app,db,lib,test}/**/*'] + %w(CHANGELOG.rdoc init.rb LICENSE Rakefile README.rdoc) - FileList['test/app_root/{log,log/*,script,script/*}']
|
14
|
+
s.require_path = 'lib'
|
15
|
+
s.has_rdoc = true
|
16
|
+
s.test_files = Dir['test/**/*_test.rb']
|
17
|
+
s.add_dependency 'state_machine', '>= 0.7.0'
|
18
|
+
|
19
|
+
s.author = 'Aaron Pfeifer'
|
20
|
+
s.email = 'aaron@pluginaweek.org'
|
21
|
+
s.homepage = 'http://www.pluginaweek.org'
|
22
|
+
s.rubyforge_project = 'pluginaweek'
|
23
|
+
end
|
24
|
+
|
25
|
+
desc 'Default: run all tests.'
|
26
|
+
task :default => :test
|
27
|
+
|
28
|
+
desc "Test the #{spec.name} plugin."
|
29
|
+
Rake::TestTask.new(:test) do |t|
|
30
|
+
t.libs << 'lib'
|
31
|
+
t.test_files = spec.test_files
|
32
|
+
t.verbose = true
|
33
|
+
end
|
34
|
+
|
35
|
+
begin
|
36
|
+
require 'rcov/rcovtask'
|
37
|
+
namespace :test do
|
38
|
+
desc "Test the #{spec.name} plugin with Rcov."
|
39
|
+
Rcov::RcovTask.new(:rcov) do |t|
|
40
|
+
t.libs << 'lib'
|
41
|
+
t.test_files = spec.test_files
|
42
|
+
t.rcov_opts << '--exclude="^(?!lib/|app/)"'
|
43
|
+
t.verbose = true
|
44
|
+
end
|
45
|
+
end
|
46
|
+
rescue LoadError
|
47
|
+
end
|
48
|
+
|
49
|
+
desc "Generate documentation for the #{spec.name} plugin."
|
50
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
51
|
+
rdoc.rdoc_dir = 'rdoc'
|
52
|
+
rdoc.title = spec.name
|
53
|
+
rdoc.template = '../rdoc_template.rb'
|
54
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
55
|
+
rdoc.rdoc_files.include('README.rdoc', 'CHANGELOG.rdoc', 'LICENSE', 'lib/**/*.rb', 'app/**/*.rb')
|
56
|
+
end
|
57
|
+
|
58
|
+
desc 'Generate a gemspec file.'
|
59
|
+
task :gemspec do
|
60
|
+
File.open("#{spec.name}.gemspec", 'w') do |f|
|
61
|
+
f.write spec.to_ruby
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
Rake::GemPackageTask.new(spec) do |p|
|
66
|
+
p.gem_spec = spec
|
67
|
+
p.need_tar = true
|
68
|
+
p.need_zip = true
|
69
|
+
end
|
70
|
+
|
71
|
+
desc 'Publish the beta gem.'
|
72
|
+
task :pgem => [:package] do
|
73
|
+
Rake::SshFilePublisher.new('aaron@pluginaweek.org', '/home/aaron/gems.pluginaweek.org/public/gems', 'pkg', "#{spec.name}-#{spec.version}.gem").upload
|
74
|
+
end
|
75
|
+
|
76
|
+
desc 'Publish the API documentation.'
|
77
|
+
task :pdoc => [:rdoc] do
|
78
|
+
Rake::SshDirPublisher.new('aaron@pluginaweek.org', "/home/aaron/api.pluginaweek.org/public/#{spec.name}", 'rdoc').upload
|
79
|
+
end
|
80
|
+
|
81
|
+
desc 'Publish the API docs and gem'
|
82
|
+
task :publish => [:pgem, :pdoc, :release]
|
83
|
+
|
84
|
+
desc 'Publish the release files to RubyForge.'
|
85
|
+
task :release => [:gem, :package] do
|
86
|
+
require 'rubyforge'
|
87
|
+
|
88
|
+
ruby_forge = RubyForge.new.configure
|
89
|
+
ruby_forge.login
|
90
|
+
|
91
|
+
%w(gem tgz zip).each do |ext|
|
92
|
+
file = "pkg/#{spec.name}-#{spec.version}.#{ext}"
|
93
|
+
puts "Releasing #{File.basename(file)}..."
|
94
|
+
|
95
|
+
ruby_forge.add_release(spec.rubyforge_project, spec.name, spec.version, file)
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
# Represents a message sent from one user to one or more others.
|
2
|
+
#
|
3
|
+
# == States
|
4
|
+
#
|
5
|
+
# Messages can be in 1 of 3 states:
|
6
|
+
# * +unsent+ - The message has not yet been sent. This is the *initial* state.
|
7
|
+
# * +queued+ - The message has been queued for future delivery.
|
8
|
+
# * +sent+ - The message has been sent.
|
9
|
+
#
|
10
|
+
# == Interacting with the message
|
11
|
+
#
|
12
|
+
# In order to perform actions on the message, such as queueing or delivering,
|
13
|
+
# you should always use the associated event action:
|
14
|
+
# * +queue+ - Queues the message so that you can send it in a separate process
|
15
|
+
# * +deliver+ - Sends the message to all of the recipients
|
16
|
+
#
|
17
|
+
# == Message visibility
|
18
|
+
#
|
19
|
+
# Although you can delete a message, it will also delete it from the inbox of all
|
20
|
+
# the message's recipients. Instead, you can change the *visibility* of the
|
21
|
+
# message. Messages have 1 of 2 states that define its visibility:
|
22
|
+
# * +visible+ - The message is visible to the sender
|
23
|
+
# * +hidden+ - The message is hidden from the sender
|
24
|
+
#
|
25
|
+
# The visibility of a message can be changed by running the associated action:
|
26
|
+
# * +hide+ -Hides the message from the sender
|
27
|
+
# * +unhide+ - Makes the message visible again
|
28
|
+
class Message < ActiveRecord::Base
|
29
|
+
belongs_to :sender, :polymorphic => true
|
30
|
+
has_many :recipients, :class_name => 'MessageRecipient', :order => 'kind DESC, position ASC', :dependent => :destroy
|
31
|
+
|
32
|
+
validates_presence_of :state, :sender_id, :sender_type
|
33
|
+
|
34
|
+
attr_accessible :subject, :body, :to, :cc, :bcc
|
35
|
+
|
36
|
+
after_save :update_recipients
|
37
|
+
|
38
|
+
named_scope :visible, :conditions => {:hidden_at => nil}
|
39
|
+
|
40
|
+
# Define actions for the message
|
41
|
+
state_machine :state, :initial => :unsent do
|
42
|
+
# Queues the message so that it's sent in a separate process
|
43
|
+
event :queue do
|
44
|
+
transition :unsent => :queued, :if => :has_recipients?
|
45
|
+
end
|
46
|
+
|
47
|
+
# Sends the message to all of the recipients as long as at least one
|
48
|
+
# recipient has been added
|
49
|
+
event :deliver do
|
50
|
+
transition [:unsent, :queued] => :sent, :if => :has_recipients?
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Defines actions for the visibility of the message
|
55
|
+
state_machine :hidden_at, :initial => :visible do
|
56
|
+
# Hides the message from the recipient's inbox
|
57
|
+
event :hide do
|
58
|
+
transition all => :hidden
|
59
|
+
end
|
60
|
+
|
61
|
+
# Makes the message visible in the recipient's inbox
|
62
|
+
event :unhide do
|
63
|
+
transition all => :visible
|
64
|
+
end
|
65
|
+
|
66
|
+
state :visible, :value => nil
|
67
|
+
state :hidden, :value => lambda {Time.now}, :if => lambda {|value| value}
|
68
|
+
end
|
69
|
+
|
70
|
+
# Directly adds the receivers on the message (i.e. they are visible to all recipients)
|
71
|
+
def to(*receivers)
|
72
|
+
receivers(receivers, 'to')
|
73
|
+
end
|
74
|
+
alias_method :to=, :to
|
75
|
+
|
76
|
+
# Carbon copies the receivers on the message
|
77
|
+
def cc(*receivers)
|
78
|
+
receivers(receivers, 'cc')
|
79
|
+
end
|
80
|
+
alias_method :cc=, :cc
|
81
|
+
|
82
|
+
# Blind carbon copies the receivers on the message
|
83
|
+
def bcc(*receivers)
|
84
|
+
receivers(receivers, 'bcc')
|
85
|
+
end
|
86
|
+
alias_method :bcc=, :bcc
|
87
|
+
|
88
|
+
# Forwards this message, including the original subject and body in the new
|
89
|
+
# message
|
90
|
+
def forward
|
91
|
+
message = self.class.new(:subject => subject, :body => body)
|
92
|
+
message.sender = sender
|
93
|
+
message
|
94
|
+
end
|
95
|
+
|
96
|
+
# Replies to this message, including the original subject and body in the new
|
97
|
+
# message. Only the original direct receivers are added to the reply.
|
98
|
+
def reply
|
99
|
+
message = self.class.new(:subject => subject, :body => body)
|
100
|
+
message.sender = sender
|
101
|
+
message.to(to)
|
102
|
+
message
|
103
|
+
end
|
104
|
+
|
105
|
+
# Replies to all recipients on this message, including the original subject
|
106
|
+
# and body in the new message. All receivers (direct, cc, and bcc) are added
|
107
|
+
# to the reply.
|
108
|
+
def reply_to_all
|
109
|
+
message = reply
|
110
|
+
message.cc(cc)
|
111
|
+
message.bcc(bcc)
|
112
|
+
message
|
113
|
+
end
|
114
|
+
|
115
|
+
private
|
116
|
+
# Create/destroy any receivers that were added/removed
|
117
|
+
def update_recipients
|
118
|
+
if @receivers
|
119
|
+
@receivers.each do |kind, receivers|
|
120
|
+
kind_recipients = recipients.select {|recipient| recipient.kind == kind}
|
121
|
+
new_receivers = receivers - kind_recipients.map(&:receiver)
|
122
|
+
removed_recipients = kind_recipients.reject {|recipient| receivers.include?(recipient.receiver)}
|
123
|
+
|
124
|
+
recipients.delete(*removed_recipients) if removed_recipients.any?
|
125
|
+
new_receivers.each {|receiver| self.recipients.create!(:receiver => receiver, :kind => kind)}
|
126
|
+
end
|
127
|
+
|
128
|
+
@receivers = nil
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# Does this message have any recipients on it?
|
133
|
+
def has_recipients?
|
134
|
+
(to + cc + bcc).any?
|
135
|
+
end
|
136
|
+
|
137
|
+
# Creates new receivers or gets the current receivers for the given kind (to, cc, or bcc)
|
138
|
+
def receivers(receivers, kind)
|
139
|
+
if receivers.any?
|
140
|
+
(@receivers ||= {})[kind] = receivers.flatten.compact
|
141
|
+
else
|
142
|
+
@receivers && @receivers[kind] || recipients.select {|recipient| recipient.kind == kind}.map(&:receiver)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
# Represents a recipient on a message. The kind of recipient (to, cc, or bcc) is
|
2
|
+
# determined by the +kind+ attribute.
|
3
|
+
#
|
4
|
+
# == States
|
5
|
+
#
|
6
|
+
# Recipients can be in 1 of 2 states:
|
7
|
+
# * +unread+ - The message has been sent, but not yet read by the recipient. This is the *initial* state.
|
8
|
+
# * +read+ - The message has been read by the recipient
|
9
|
+
#
|
10
|
+
# == Interacting with the message
|
11
|
+
#
|
12
|
+
# In order to perform actions on the message, such as viewing, you should always
|
13
|
+
# use the associated event action:
|
14
|
+
# * +view+ - Marks the message as read by the recipient
|
15
|
+
#
|
16
|
+
# == Hiding messages
|
17
|
+
#
|
18
|
+
# Although you can delete a recipient, it will also delete it from everyone else's
|
19
|
+
# message, meaning that no one will know that person was ever a recipient of the
|
20
|
+
# message. Instead, you can change the *visibility* of the message. Messages
|
21
|
+
# have 1 of 2 states that define its visibility:
|
22
|
+
# * +visible+ - The message is visible to the recipient
|
23
|
+
# * +hidden+ - The message is hidden from the recipient
|
24
|
+
#
|
25
|
+
# The visibility of a message can be changed by running the associated action:
|
26
|
+
# * +hide+ -Hides the message from the recipient
|
27
|
+
# * +unhide+ - Makes the message visible again
|
28
|
+
class MessageRecipient < ActiveRecord::Base
|
29
|
+
belongs_to :message
|
30
|
+
belongs_to :receiver, :polymorphic => true
|
31
|
+
|
32
|
+
validates_presence_of :message_id, :kind, :state, :receiver_id, :receiver_type
|
33
|
+
|
34
|
+
attr_protected :state, :position, :hidden_at
|
35
|
+
|
36
|
+
before_create :set_position
|
37
|
+
before_destroy :reorder_positions
|
38
|
+
|
39
|
+
# Make this class look like the actual message
|
40
|
+
delegate :sender, :subject, :body, :recipients, :to, :cc, :bcc, :created_at,
|
41
|
+
:to => :message
|
42
|
+
|
43
|
+
named_scope :visible, :conditions => {:hidden_at => nil}
|
44
|
+
|
45
|
+
# Defines actions for the recipient
|
46
|
+
state_machine :state, :initial => :unread do
|
47
|
+
# Indicates that the message has been viewed by the receiver
|
48
|
+
event :view do
|
49
|
+
transition :unread => :read, :if => :message_sent?
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Defines actions for the visibility of the message to the recipient
|
54
|
+
state_machine :hidden_at, :initial => :visible do
|
55
|
+
# Hides the message from the recipient's inbox
|
56
|
+
event :hide do
|
57
|
+
transition all => :hidden
|
58
|
+
end
|
59
|
+
|
60
|
+
# Makes the message visible in the recipient's inbox
|
61
|
+
event :unhide do
|
62
|
+
transition all => :visible
|
63
|
+
end
|
64
|
+
|
65
|
+
state :visible, :value => nil
|
66
|
+
state :hidden, :value => lambda {Time.now}, :if => lambda {|value| value}
|
67
|
+
end
|
68
|
+
|
69
|
+
# Forwards this message, including the original subject and body in the new
|
70
|
+
# message
|
71
|
+
def forward
|
72
|
+
message = self.message.class.new(:subject => subject, :body => body)
|
73
|
+
message.sender = receiver
|
74
|
+
message
|
75
|
+
end
|
76
|
+
|
77
|
+
# Replies to this message, including the original subject and body in the new
|
78
|
+
# message. Only the original direct receivers are added to the reply.
|
79
|
+
def reply
|
80
|
+
message = self.message.class.new(:subject => subject, :body => body)
|
81
|
+
message.sender = receiver
|
82
|
+
message.to(sender)
|
83
|
+
message
|
84
|
+
end
|
85
|
+
|
86
|
+
# Replies to all recipients on this message, including the original subject
|
87
|
+
# and body in the new message. All receivers (sender, direct, cc, and bcc) are
|
88
|
+
# added to the reply.
|
89
|
+
def reply_to_all
|
90
|
+
message = reply
|
91
|
+
message.to(to - [receiver] + [sender])
|
92
|
+
message.cc(cc - [receiver])
|
93
|
+
message.bcc(bcc - [receiver])
|
94
|
+
message
|
95
|
+
end
|
96
|
+
|
97
|
+
private
|
98
|
+
# Has the message this recipient is on been sent?
|
99
|
+
def message_sent?
|
100
|
+
message.sent?
|
101
|
+
end
|
102
|
+
|
103
|
+
# Sets the position of the current recipient based on existing recipients
|
104
|
+
def set_position
|
105
|
+
if last_recipient = message.recipients.find(:first, :conditions => {:kind => kind}, :order => 'position DESC')
|
106
|
+
self.position = last_recipient.position + 1
|
107
|
+
else
|
108
|
+
self.position = 1
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# Reorders the positions of the message's recipients
|
113
|
+
def reorder_positions
|
114
|
+
if position
|
115
|
+
position = self.position
|
116
|
+
update_attribute(:position, nil)
|
117
|
+
self.class.update_all('position = (position - 1)', ['message_id = ? AND kind = ? AND position > ?', message_id, kind, position])
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|