private_mail 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,37 @@
1
+ require 'rails/generators/migration'
2
+
3
+ module PrivateMail
4
+
5
+ module Generators
6
+ class InstallGenerator < ::Rails::Generators::Base
7
+ include Rails::Generators::Migration
8
+
9
+ source_root File.expand_path('../templates', __FILE__)
10
+ desc "add the migrations"
11
+
12
+ def self.next_migration_number(path)
13
+ unless @prev_migration_nr
14
+ @prev_migration_nr = Time.now.utc.strftime("%Y%m%d%H%M%S").to_i
15
+ else
16
+ @prev_migration_nr += 1
17
+ end
18
+ @prev_migration_nr.to_s
19
+ end
20
+
21
+ def copy_migrations
22
+ migration_template "create_conversations.rb", "db/migrate/create_conversations.rb"
23
+ migration_template "create_messages.rb", "db/migrate/create_messages.rb"
24
+ migration_template "create_messages_recipients.rb", "db/migrate/create_messages_recipients.rb"
25
+ migration_template "create_mail.rb", "db/migrate/create_mail.rb"
26
+ end
27
+
28
+ def copy_models
29
+ copy_file "../models/mail.rb", "app/models/mail.rb"
30
+ copy_file "../models/message.rb", "app/models/message.rb"
31
+ copy_file "../models/conversation.rb", "app/models/conversation.rb"
32
+ end
33
+
34
+ end
35
+ end
36
+
37
+ end
@@ -0,0 +1,56 @@
1
+ class Conversation < ActiveRecord::Base
2
+ attr_reader :originator, :original_message, :last_sender, :last_message, :users
3
+ has_many :messages
4
+ has_many :mails
5
+ before_create :clean
6
+ #looks like shit but isnt too bad
7
+ #has_many :users, :through :messages, :source => :recipients, :uniq => true doesnt work due to recipients being a habtm association
8
+ has_many :recipients, :class_name => 'User', :finder_sql =>
9
+ 'SELECT users.* FROM conversations
10
+ INNER JOIN messages ON conversations.id = messages.conversation_id
11
+ INNER JOIN messages_recipients ON messages_recipients.message_id = messages.id
12
+ INNER JOIN users ON messages_recipients.recipient_id = users.id
13
+ WHERE conversations.id = #{self.id} GROUP BY users.id;'
14
+
15
+ #originator of the conversation.
16
+ def originator()
17
+ @orignator = self.original_message.sender if @originator.nil?
18
+ return @orignator
19
+ end
20
+
21
+ #first message of the conversation.
22
+ def original_message()
23
+ @original_message = self.messages.find(:first, :order => 'created_at') if @original_message.nil?
24
+ return @original_message
25
+ end
26
+
27
+ #sender of the last message.
28
+ def last_sender()
29
+ @last_sender = self.last_message.sender if @last_sender.nil?
30
+ return @last_sender
31
+ end
32
+
33
+ #last message in the conversation.
34
+ def last_message()
35
+ @last_message = self.messages.find(:first, :order => 'created_at DESC') if @last_message.nil?
36
+ return @last_message
37
+ end
38
+
39
+ #all users involved in the conversation.
40
+ def users()
41
+ if(@users.nil?)
42
+ @users = self.recipients.clone
43
+ @users << self.originator unless @users.include?(self.originator)
44
+ end
45
+ return @users
46
+ end
47
+
48
+ protected
49
+ #[empty method]
50
+ #
51
+ #this gets called before_create. Implement this if you wish to clean out illegal content such as scripts or anything that will break layout. This is left empty because what is considered illegal content varies.
52
+ def clean()
53
+ return if(subject.nil?)
54
+ #strip all illegal content here. (scripts, shit that will break layout, etc.)
55
+ end
56
+ end
@@ -0,0 +1,16 @@
1
+ class Mail < ActiveRecord::Base
2
+ self.table_name = "mail"
3
+ belongs_to :message
4
+ belongs_to :user
5
+ belongs_to :conversation
6
+
7
+ #sets the read attribute of the mail message to true.
8
+ def mark_as_read()
9
+ update_attribute('read', true)
10
+ end
11
+
12
+ #sets the read attribute of the mail message to false.
13
+ def mark_as_unread()
14
+ update_attribute('read', false)
15
+ end
16
+ end
@@ -0,0 +1,39 @@
1
+ class Message < ActiveRecord::Base
2
+ #any additional info that needs to be sent in a message (ex. I use these to determine request types)
3
+ serialize :headers
4
+
5
+ class_inheritable_accessor :on_deliver_callback
6
+ protected :on_deliver_callback
7
+
8
+ belongs_to :sender, :class_name => 'User', :foreign_key => 'sender_id'
9
+ belongs_to :conversation
10
+ has_and_belongs_to_many :recipients, :class_name => 'User', :join_table => 'messages_recipients', :association_foreign_key => 'recipient_id'
11
+
12
+ #delivers a message to the the given mailbox of all recipients, calls the on_deliver_callback if initialized.
13
+ #
14
+ #====params:
15
+ #mailbox_type:: the mailbox to send the message to
16
+ #clean:: calls the clean method if this is set (must be implemented)
17
+ #
18
+ def deliver(mailbox_type, clean = true)
19
+ clean() if clean
20
+ self.save()
21
+ self.recipients.each do |r|
22
+ r.mailbox[mailbox_type] << self
23
+ end
24
+ self.on_deliver_callback.call(self, mailbox_type) unless self.on_deliver_callback.nil?
25
+ end
26
+
27
+ #sets the on_deliver_callback to the passed method. The method call should expect 2 params (message, mailbox_type).
28
+ def Message.on_deliver(method)
29
+ self.on_deliver_callback = method
30
+ end
31
+
32
+ protected
33
+ #[empty method]
34
+ #
35
+ #this gets called when a message is delivered and the clean param is set (default). Implement this if you wish to clean out illegal content such as scripts or anything that will break layout. This is left empty because what is considered illegal content varies.
36
+ def clean()
37
+ #strip all illegal content here. (scripts, shit that will break layout, etc.)
38
+ end
39
+ end
@@ -0,0 +1,12 @@
1
+ class CreateConversations < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :conversations do |t|
4
+ t.column :subject, :string, :default => ""
5
+ t.column :created_at, :datetime, :null => false
6
+ end
7
+ end
8
+
9
+ def self.down
10
+ drop_table :conversations
11
+ end
12
+ end
@@ -0,0 +1,18 @@
1
+ class CreateMail < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :mail do |t|
4
+ t.column :user_id, :integer, :null => false
5
+ t.column :message_id, :integer, :null => false
6
+ t.column :conversation_id, :integer
7
+ t.column :read, :boolean, :default => false
8
+ t.column :trashed, :boolean, :default => false
9
+ t.column :mailbox, :string, :limit => 25
10
+ t.column :created_at, :datetime, :null => false
11
+ end
12
+ #i use foreign keys but its a custom method, so i'm leaving it up to you want them.
13
+ end
14
+
15
+ def self.down
16
+ drop_table :mail
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ class CreateMessages < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :messages do |t|
4
+ t.column :body, :text
5
+ t.column :subject, :string, :default => ""
6
+ t.column :headers, :text
7
+ t.column :sender_id, :integer, :null => false
8
+ t.column :conversation_id, :integer
9
+ t.column :sent, :boolean, :default => false
10
+ t.column :created_at, :datetime, :null => false
11
+ end
12
+ #i use foreign keys but its a custom method, so i'm leaving it up to you if you want them.
13
+ end
14
+
15
+ def self.down
16
+ drop_table :messages
17
+ end
18
+ end
@@ -0,0 +1,13 @@
1
+ class CreateMessagesRecipients < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :messages_recipients, :id => false do |t|
4
+ t.column :message_id, :integer, :null => false
5
+ t.column :recipient_id, :integer, :null => false
6
+ end
7
+ #i use foreign keys but its a custom method, so i'm leaving it up to you want them.
8
+ end
9
+
10
+ def self.down
11
+ drop_table :messages_recipients
12
+ end
13
+ end
data/lib/mailbox.rb ADDED
@@ -0,0 +1,343 @@
1
+ module PrivateMail
2
+
3
+ class Mailbox
4
+ #this is used to filter mail by mailbox type, use the [] method rather than setting this directly.
5
+ attr_accessor :type
6
+ #the user/owner of this mailbox, set when initialized.
7
+ attr_reader :user
8
+
9
+ #creates a new Mailbox instance with the given user and optional type.
10
+ def initialize(user, type = :all)
11
+ @user = user
12
+ @type = type
13
+ end
14
+
15
+ #sets the mailbox type to the symbol corresponding to the given val.
16
+ def type=(val)
17
+ @type = val.to_sym
18
+ end
19
+
20
+ #returns a count of mail messages filtered by type and filter, if set.
21
+ #
22
+ #*this performs an actual sql count rather than selecting all mail and then gettin a length on the array... not a big deal but this could be something that is checked often to notify the user when they receive a new mail.
23
+ #
24
+ #====params:
25
+ #filter:: filters the count by the 'read' attribute.
26
+ #* :all - count of both read and unread mail.
27
+ #* :read - count of read mail.
28
+ #* :unread - count of unread mail.
29
+ #options:: see mail for acceptable options.
30
+ #
31
+ #====returns:
32
+ #number of mail messages
33
+ #
34
+ #====example:
35
+ # phil = User.find(3123)
36
+ #
37
+ # #get number of unread mail messages in phil's inbox
38
+ # phil.mailbox[:inbox].mail_count(:unread)
39
+ #
40
+ def mail_count(filter = :all, options = {})
41
+ default_options = {:conditions => ["mail.user_id = ?", @user.id]}
42
+ add_mailbox_condition!(default_options, @type)
43
+ add_conditions!(default_options, "mail.read = ?", filter == :read) unless filter == :all
44
+ return count_mail(default_options, options)
45
+ end
46
+
47
+ #returns an array of all Mail for the user filtered by type, if set.
48
+ #
49
+ #====params:
50
+ #options:: all valid find options are accepted as well as an additional conversation option.
51
+ #* :conversation - Conversation object to filter mail only belonging to this conversation.
52
+ #* :conditions - same as find conditions however the array version of conditions will not work, i.e., :conditions => ['mail.read = ?', false] will not work here.
53
+ #* all other find options will work as expected.
54
+ #
55
+ #====returns:
56
+ #array of Mail.
57
+ #
58
+ #====example:
59
+ # phil = User.find(3123)
60
+ #
61
+ # #get all mail messages belonging to phil
62
+ # phil.mailbox.mail
63
+ #
64
+ # #get all mail messages in phil's inbox associated with conversation 23
65
+ # phil.mailbox[:inbox].mail(:conversation => Conversation.find(23))
66
+ #
67
+ # #get all unread mail messages belonging to phil associated with conversation 23
68
+ # phil.mailbox.mail(:conversation => Conversation.find(23), :conditions => 'mail.read = false')
69
+ #
70
+ def mail(options = {})
71
+ default_options = {}
72
+ add_mailbox_condition!(default_options, @type)
73
+ return get_mail(default_options, options)
74
+ end
75
+
76
+ #returns an array of unread Mail for the user filtered by type, if set.
77
+ #
78
+ #====params:
79
+ #options:: see mail for acceptable options.
80
+ #
81
+ #====returns:
82
+ #array of Mail.
83
+ #
84
+ #====example:
85
+ # phil = User.find(3123)
86
+ #
87
+ # #get all unread mail in phil's inbox
88
+ # phil.mailbox[:inbox].unread_mail
89
+ #
90
+ def unread_mail(options = {})
91
+ default_options = {:conditions => ["mail.read = ?", false]}
92
+ add_mailbox_condition!(default_options, @type)
93
+ return get_mail(default_options, options)
94
+ end
95
+
96
+ #returns an array of read Mail for the user filtered by type, if set.
97
+ #
98
+ #====params:
99
+ #options:: see mail for acceptable options.
100
+ #
101
+ #====returns:
102
+ #array of Mail.
103
+ #
104
+ #====example:
105
+ # phil = User.find(3123)
106
+ #
107
+ # #get all read mail in phil's inbox
108
+ # phil.mailbox[:inbox].read_mail
109
+ #
110
+ def read_mail(options = {})
111
+ default_options = {:conditions => ["mail.read = ?", true]}
112
+ add_mailbox_condition!(default_options, @type)
113
+ return get_mail(default_options, options)
114
+ end
115
+
116
+ #returns an array of the latest Mail message for each conversation the user is involved in filtered by type, if set.
117
+ #
118
+ #*possible use for this would be an inbox view of your mail so you can easily see the status of all the convos your involved in.
119
+ #
120
+ #====params:
121
+ #options:: see mail for acceptable options.
122
+ #
123
+ #====returns:
124
+ #array of Mail.
125
+ #
126
+ #====example:
127
+ # phil = User.find(3123)
128
+ #
129
+ # #get a list of the latest received mail for each conversation
130
+ # phil.mailbox[:inbox].latest_mail
131
+ #
132
+ def latest_mail(options = {})
133
+ return only_latest(mail(options))
134
+ end
135
+
136
+ #adds a mail message to the user's mailbox specified by type.
137
+ #
138
+ #*this is used when sending a new message, all the recipients get a mail added to their inbox and the sender gets a mail in their sentbox.
139
+ #
140
+ #====params:
141
+ #msg::
142
+ # Message object from which a new mail is created from.
143
+ #
144
+ #====returns:
145
+ #new Mail.
146
+ #
147
+ def add(msg)
148
+ attributes = {:message => msg, :conversation => msg.conversation}
149
+ attributes[:mailbox] = @type.to_s unless @type == :all
150
+ attributes[:read] = msg.sender.id == @user.id
151
+ mail_msg = Mail.new(attributes)
152
+ @user.mail << mail_msg
153
+ return mail_msg
154
+ end
155
+
156
+ #marks all the mail messages matched by the options and type as read.
157
+ #
158
+ #====params:
159
+ #options:: see mail for acceptable options.
160
+ #
161
+ #====returns:
162
+ #array of Mail.
163
+ #
164
+ #====example:
165
+ # phil = User.find(3123)
166
+ #
167
+ # #mark all inbox messages as read
168
+ # phil.mailbox[:inbox].mark_as_read()
169
+ #
170
+ def mark_as_read(options = {})
171
+ default_options = {}
172
+ add_mailbox_condition!(default_options, @type)
173
+ return update_mail("mail.read = true", default_options, options)
174
+ end
175
+
176
+ #marks all the mail messages matched by the options and type as unread, except for sent messages.
177
+ #
178
+ #====params:
179
+ #options:: see mail for acceptable options.
180
+ #
181
+ #====returns:
182
+ #array of Mail.
183
+ #
184
+ #====example:
185
+ # phil = User.find(3123)
186
+ #
187
+ # #mark all inbox messages as unread
188
+ # phil.mailbox[:inbox].mark_as_unread()
189
+ #
190
+ def mark_as_unread(options = {})
191
+ default_options = {:conditions => ["mail.mailbox != ?",@user.mailbox_types[:sent].to_s]}
192
+ add_mailbox_condition!(default_options, @type)
193
+ return update_mail("mail.read = false", default_options, options)
194
+ end
195
+
196
+ #moves all mail matched by the options to the given mailbox. sent messages stay in the sentbox.
197
+ #
198
+ #====params:
199
+ #mailbox:: the mailbox_type to move the mail messages to. (ex. :inbox, :trash)
200
+ #options:: see mail for acceptable options.
201
+ #
202
+ def move_to(mailbox, options = {})
203
+ mailbox = mailbox.to_sym
204
+ trash = mailbox == @user.mailbox_types[:deleted].to_sym
205
+ default_options = {}
206
+ add_mailbox_condition!(default_options, @type)
207
+ if(!trash)
208
+ #conditional update because sentmail is always sentmail - I believe case if the most widely supported conditional, mysql also has an if which would work as well but i think mysql is the only one to support it
209
+ return update_mail("mail.trashed = false, mail.mailbox =
210
+ CASE mail.mailbox
211
+ WHEN '#{@user.mailbox_types[:sent].to_s}' THEN mail.mailbox
212
+ ELSE '#{mailbox.to_s}'
213
+ END", default_options, options)
214
+ end
215
+ return update_mail("mail.trashed = true", default_options, options)
216
+ end
217
+
218
+ #permanantly deletes all the mail messages matched by the options. Use move_to(:trash) instead if you want to send to user's trash without deleting.
219
+ #
220
+ #====params:
221
+ #options:: see mail for acceptable options.
222
+ #
223
+ def delete(options = {})
224
+ default_options = {:conditions => ["mail.user_id = ?", @user.id]}
225
+ add_mailbox_condition!(default_options, @type)
226
+ return delete_mail(default_options, options)
227
+ end
228
+
229
+ #alias for add
230
+ def <<(msg)
231
+ return self.add(msg)
232
+ end
233
+
234
+ #deletes all messages that have been trashed and match the options if passed.
235
+ #
236
+ #====params:
237
+ #options:: see mail for acceptable options.
238
+ #
239
+ def empty_trash(options = {})
240
+ default_options = {:conditions => ["mail.user_id = ? AND mail.trashed = ?", @user.id, true]}
241
+ add_mailbox_condition!(default_options, @type)
242
+ return delete_mail(default_options, options)
243
+ end
244
+
245
+ #return true if the user is involved in the given conversation.
246
+ def has_conversation?(conversation)
247
+ return mail_count(:all, :conversation => conversation) != 0
248
+ end
249
+
250
+ #sets the mailbox type and returns itself.
251
+ #
252
+ #====params:
253
+ #mailbox_type::
254
+ # type of mailbox to filter mail by, this can be anything, but the three most likely values for this will be the received, sent, and trash values set within the acts_as_messageable method.
255
+ #
256
+ #====returns:
257
+ #self
258
+ #
259
+ #====example:
260
+ # phil = User.find(3123)
261
+ #
262
+ # #all mails in the user's inbox
263
+ # phil.mailbox[:inbox].mail
264
+ #
265
+ def [](mailbox_type)
266
+ self.type = mailbox_type
267
+ return self
268
+ end
269
+
270
+ private
271
+
272
+ def get_mail(default_options, options)
273
+ build_options(default_options, options) unless options.empty?
274
+ return @user.mail.find(:all, default_options)
275
+ end
276
+
277
+ def update_mail(updates, default_options, options)
278
+ build_options(default_options, options) unless options.empty?
279
+ return @user.mail.update_all(updates, default_options[:conditions])
280
+ end
281
+
282
+ def delete_mail(default_options, options)
283
+ build_options(default_options, options) unless options.empty?
284
+ return Mail.delete_all(default_options[:conditions])
285
+ end
286
+
287
+ def count_mail(default_options, options)
288
+ build_options(default_options, options) unless options.empty?
289
+ return Mail.count(:all, default_options)
290
+ end
291
+
292
+ def build_options(default_options, options)
293
+ add_conversation_condition!(default_options, options[:conversation]) unless options[:conversation].nil?
294
+ options.delete(:conversation)
295
+ add_conditions!(default_options, options[:conditions]) unless options[:conditions].nil?
296
+ options.delete(:conditions)
297
+ default_options.merge!(options)
298
+ end
299
+
300
+ def only_latest(mail)
301
+ convos = []
302
+ latest = []
303
+ mail.each do |m|
304
+ next if(convos.include?(m.conversation_id))
305
+ convos << m.conversation_id
306
+ latest << m
307
+ end
308
+ return latest
309
+ end
310
+
311
+ def add_mailbox_condition!(options, mailbox_type)
312
+ return if mailbox_type == :all
313
+ return add_conditions!(options, "mail.mailbox = ? AND mail.trashed = ?", mailbox_type.to_s, false) unless mailbox_type == @user.mailbox_types[:deleted]
314
+ return add_conditions!(options, "mail.trashed = ?", true)
315
+ end
316
+
317
+ def add_conversation_condition!(options, conversation)
318
+ options.merge!({:order => 'created_at ASC'})
319
+ if(conversation.is_a?(Array))
320
+ conversation.map! {|c| c.is_a?(Integer) ? c : c.id}
321
+ else
322
+ conversation = conversation.is_a?(Integer) ? [conversation] : [conversation.id]
323
+ end
324
+ return add_conditions!(options, "conversation_id IN (?)", conversation)
325
+ end
326
+
327
+ def add_conditions!(options, conditions, *values)
328
+ return nil unless options.is_a?(Hash)
329
+ if(options[:conditions].nil?)
330
+ options[:conditions] = values.length == 0 ? conditions : [conditions]
331
+ elsif(options[:conditions].is_a?(Array))
332
+ options[:conditions][0] = "(#{options[:conditions][0]}) AND (#{conditions})"
333
+ else
334
+ options[:conditions] = "(#{options[:conditions]}) AND (#{conditions})"
335
+ end
336
+ values.each do |val|
337
+ options[:conditions].push(val)
338
+ end
339
+ return options
340
+ end
341
+
342
+ end
343
+ end
@@ -0,0 +1,3 @@
1
+ module PrivateMail
2
+ VERSION = "0.0.1" unless defined? PrivateMail::VERSION
3
+ end
@@ -0,0 +1,204 @@
1
+ # Private Mail implements simple private messaging between users in a Rails application.
2
+
3
+ require 'logger'
4
+ require 'active_record'
5
+ require 'mailbox'
6
+
7
+ module PrivateMail
8
+ def self.included(mod)
9
+ mod.extend(ClassMethods)
10
+ end
11
+
12
+ # declare the class level helper methods which
13
+ # will load the relevant instance methods
14
+ # defined below when invoked
15
+ module ClassMethods
16
+ #enables a class to send and receive messages to members of the same class - currently assumes the model is of class type 'User',
17
+ #some modifications to the migrations and model classes will need to be made to use a model of different type.
18
+ #
19
+ #====options:
20
+ #* :received - the mailbox type to store received messages (defaults to :inbox)
21
+ #
22
+ #* :sent - the mailbox type to store sent messages (defaults to :sentbox)
23
+ #
24
+ #* :deleted - the mailbox type to store deleted messages (defaults to :trash)
25
+ #
26
+ #====example:
27
+ # acts_as_messageable :received => :in, :sent => :sent, :deleted => :garbage
28
+ def acts_as_messageable(options = {})
29
+ cattr_accessor :mailbox_types
30
+ has_many :mail, :order => 'created_at DESC', :dependent => :delete_all
31
+
32
+ self.mailbox_types = {
33
+ :received => :inbox,
34
+ :sent => :sentbox,
35
+ :deleted => :trash
36
+ }.merge(options)
37
+
38
+ include PrivateMail::InstanceMethods
39
+ end
40
+ end
41
+
42
+ # Adds instance methods.
43
+ module InstanceMethods
44
+ #returns an instance of class type Mailbox - this object essentially wraps the user's mail messages and provides a clean interface for accessing them.
45
+ #see Mailbox for more details.
46
+ #
47
+ #====example:
48
+ # phil = User.find(3123)
49
+ # phil.mailbox[:inbox].unread_mail #returns all unread mail in your inbox
50
+ # phil.mailbox[:sentbox].mail #returns all sent mail messages
51
+ #
52
+ def mailbox()
53
+ @mailbox = Mailbox.new(self) if @mailbox.nil?
54
+ @mailbox.type = :all
55
+ return @mailbox
56
+ end
57
+ #creates new Message and Conversation objects from the given parameters and delivers Mail to each of the recipients' inbox.
58
+ #
59
+ #====params:
60
+ #recipients::
61
+ # a single user object or array of users to deliver the message to.
62
+ #msg_body::
63
+ # the body of the message.
64
+ #subject::
65
+ # the subject of the message, defaults to empty string if not provided.
66
+ #====returns:
67
+ #the sent Mail.
68
+ #
69
+ #====example:
70
+ # phil = User.find(3123)
71
+ # todd = User.find(4141)
72
+ # phil.send_message(todd, 'whats up for tonight?', 'hey guy') #sends a Mail message to todd's inbox, and a Mail message to phil's sentbox
73
+ #
74
+ def send_message(recipients, msg_body, subject = '')
75
+ convo = Conversation.create({:subject => subject})
76
+ message = Message.create({:sender => self, :conversation => convo, :body => msg_body, :subject => subject})
77
+ message.recipients = recipients.is_a?(Array) ? recipients : [recipients]
78
+ message.deliver(self.mailbox_types[:received])
79
+ return mailbox[:sentbox] << message
80
+ end
81
+ #creates a new Message associated with the given conversation and delivers the reply to each of the given recipients.
82
+ #
83
+ #*explicitly calling this method is rare unless you are replying to a subset of the users involved in the conversation or
84
+ #if you are including someone that is not currently in the conversation.
85
+ #reply_to_sender, reply_to_all, and reply_to_conversation will suffice in most cases.
86
+ #
87
+ #====params:
88
+ #conversation::
89
+ # the Conversation object that the mail you are responding to belongs.
90
+ #recipients::
91
+ # a single User object or array of Users to deliver the reply message to.
92
+ #reply_body::
93
+ # the body of the reply message.
94
+ #subject::
95
+ # the subject of the message, defaults to 'RE: [original subject]' if one isnt given.
96
+ #====returns:
97
+ #the sent Mail.
98
+ #
99
+ def reply(conversation, recipients, reply_body, subject = nil)
100
+ return nil if(reply_body.blank?)
101
+ subject = subject || "RE: #{conversation.subject}"
102
+ response = Message.create({:sender => self, :conversation => conversation, :body => reply_body, :subject => subject})
103
+ response.recipients = recipients.is_a?(Array) ? recipients : [recipients]
104
+ response.deliver(self.mailbox_types[:received])
105
+ return mailbox[self.mailbox_types[:sent]] << response
106
+ end
107
+ #sends a Mail to the sender of the given mail message.
108
+ #
109
+ #====params:
110
+ #mail::
111
+ # the Mail object that you are replying to.
112
+ #reply_body::
113
+ # the body of the reply message.
114
+ #subject::
115
+ # the subject of the message, defaults to 'RE: [original subject]' if one isnt given.
116
+ #====returns:
117
+ #the sent Mail.
118
+ #
119
+ def reply_to_sender(mail, reply_body, subject = nil)
120
+ return reply(mail.conversation, mail.message.sender, reply_body, subject)
121
+ end
122
+ #sends a Mail to all of the recipients of the given mail message (excluding yourself).
123
+ #
124
+ #====params:
125
+ #mail::
126
+ # the Mail object that you are replying to.
127
+ #reply_body::
128
+ # the body of the reply message.
129
+ #subject::
130
+ # the subject of the message, defaults to 'RE: [original subject]' if one isnt given.
131
+ #====returns:
132
+ #the sent Mail.
133
+ #
134
+ def reply_to_all(mail, reply_body, subject = nil)
135
+ msg = mail.message
136
+ recipients = msg.recipients.clone()
137
+ if(msg.sender != self)
138
+ recipients.delete(self)
139
+ if(!recipients.include?(msg.sender))
140
+ recipients << msg.sender
141
+ end
142
+ end
143
+ return reply(mail.conversation, recipients, reply_body, subject)
144
+ end
145
+ #sends a Mail to all users involved in the given conversation (excluding yourself).
146
+ #
147
+ #*this may have undesired effects if users have been added to the conversation after it has begun.
148
+ #
149
+ #====params:
150
+ #conversation::
151
+ # the Conversation object that the mail you are responding to belongs.
152
+ #reply_body::
153
+ # the body of the reply message.
154
+ #subject::
155
+ # the subject of the message, defaults to 'RE: [original subject]' if one isnt given.
156
+ #====returns:
157
+ #the sent Mail.
158
+ #
159
+ def reply_to_conversation(conversation, reply_body, subject = nil)
160
+ #move conversation to inbox if it is currently in the trash - doesnt make much sense replying to a trashed convo.
161
+ if((mailbox[self.mailbox_types[:deleted]].has_conversation?(conversation)))
162
+ mailbox.move_to(self.mailbox_types[:received], :conversation => conversation)
163
+ end
164
+ #remove self from recipients unless you are the originator of the convo
165
+ recipients = conversation.original_message.recipients.clone()
166
+ if(conversation.originator != self)
167
+ recipients.delete(self)
168
+ if(!recipients.include?(conversation.originator))
169
+ recipients << conversation.originator
170
+ end
171
+ end
172
+ return reply(conversation,recipients, reply_body, subject)
173
+ end
174
+ #returns the mail given as the parameter, marked as read.
175
+ def read_mail(mail)
176
+ return mail.mark_as_read()
177
+ end
178
+ #returns an array of the user's Mail associated with the given conversation.
179
+ #All mail is marked as read but the returning array is built before this so you can see which messages were unread when viewing the conversation.
180
+ #
181
+ #This returns deleted/trashed messages as well for the purpose of reading trashed convos, to disable this send the option ':conditions => "mail.trashed != true"'
182
+ #
183
+ #====params:
184
+ #conversation::
185
+ # the Conversation object that you want to read.
186
+ #options::
187
+ # any options to filter the conversation, these are used as find options so all valid options for find will work.
188
+ #
189
+ #====returns:
190
+ #array of Mail belonging to the given conversation.
191
+ #
192
+ def read_conversation(conversation, options = {})
193
+ convo_mail = self.mailbox.mail(options.merge(:conversation => conversation))
194
+ self.mailbox.mark_as_read(:conversation => conversation)
195
+ return convo_mail
196
+ end
197
+ end
198
+ end
199
+
200
+ # reopen ActiveRecord and include all the above to make
201
+ # them available to all our models if they want it
202
+ ActiveRecord::Base.class_eval do
203
+ include PrivateMail
204
+ end
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: private_mail
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - John McDowall
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-10-31 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rails
16
+ requirement: &2170459640 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *2170459640
25
+ - !ruby/object:Gem::Dependency
26
+ name: rspec
27
+ requirement: &2170459220 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *2170459220
36
+ description: ''
37
+ email: john@mcdowall.info
38
+ executables: []
39
+ extensions: []
40
+ extra_rdoc_files: []
41
+ files:
42
+ - lib/generators/private_mail/install/install_generator.rb
43
+ - lib/generators/private_mail/install/models/conversation.rb
44
+ - lib/generators/private_mail/install/models/mail.rb
45
+ - lib/generators/private_mail/install/models/message.rb
46
+ - lib/generators/private_mail/install/templates/create_conversations.rb
47
+ - lib/generators/private_mail/install/templates/create_mail.rb
48
+ - lib/generators/private_mail/install/templates/create_messages.rb
49
+ - lib/generators/private_mail/install/templates/create_messages_recipients.rb
50
+ - lib/mailbox.rb
51
+ - lib/private_mail/version.rb
52
+ - lib/private_mail.rb
53
+ homepage: ''
54
+ licenses: []
55
+ post_install_message:
56
+ rdoc_options: []
57
+ require_paths:
58
+ - lib
59
+ required_ruby_version: !ruby/object:Gem::Requirement
60
+ none: false
61
+ requirements:
62
+ - - ! '>='
63
+ - !ruby/object:Gem::Version
64
+ version: '0'
65
+ segments:
66
+ - 0
67
+ hash: -1834868077319262402
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ! '>='
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ requirements: []
75
+ rubyforge_project:
76
+ rubygems_version: 1.8.11
77
+ signing_key:
78
+ specification_version: 3
79
+ summary: ''
80
+ test_files: []