discuss 0.0.1
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.
- checksums.yaml +15 -0
- data/MIT-LICENSE +20 -0
- data/README.md +153 -0
- data/Rakefile +34 -0
- data/app/assets/javascripts/discuss/application.js +3 -0
- data/app/assets/javascripts/discuss/chosen.jquery.js +1104 -0
- data/app/assets/javascripts/discuss/compose.coffee +21 -0
- data/app/assets/javascripts/discuss/discuss.chosen.coffee +2 -0
- data/app/assets/javascripts/discuss/discuss.coffee +2 -0
- data/app/assets/javascripts/discuss/messages.coffee +0 -0
- data/app/assets/stylesheets/discuss/application.css +13 -0
- data/app/assets/stylesheets/discuss/chosen.css +439 -0
- data/app/controllers/discuss/application_controller.rb +11 -0
- data/app/controllers/discuss/mailboxes_controller.rb +30 -0
- data/app/controllers/discuss/messages_controller.rb +85 -0
- data/app/helpers/discuss/application_helper.rb +7 -0
- data/app/models/discuss/conversation.rb +29 -0
- data/app/models/discuss/mailbox.rb +33 -0
- data/app/models/discuss/message.rb +135 -0
- data/app/services/discuss/message_sender.rb +33 -0
- data/app/views/discuss/mailboxes/show.html.haml +24 -0
- data/app/views/discuss/messages/_form.html.haml +18 -0
- data/app/views/discuss/messages/edit.html.haml +1 -0
- data/app/views/discuss/messages/new.html.haml +1 -0
- data/app/views/discuss/messages/show.html.haml +28 -0
- data/app/views/discuss/shared/_nav.haml +8 -0
- data/config/routes.rb +15 -0
- data/db/migrate/20130419055252_setup_discuss.rb +22 -0
- data/lib/discuss.rb +4 -0
- data/lib/discuss/engine.rb +9 -0
- data/lib/discuss/models/discussable.rb +29 -0
- data/lib/discuss/version.rb +3 -0
- data/lib/generators/discuss/views_generator.rb +8 -0
- data/lib/tasks/discuss_tasks.rake +4 -0
- data/test/discuss_test.rb +7 -0
- data/test/dummy/README.rdoc +28 -0
- data/test/dummy/Rakefile +6 -0
- data/test/dummy/app/assets/javascripts/application.js +5 -0
- data/test/dummy/app/assets/javascripts/chosen.jquery.js +1104 -0
- data/test/dummy/app/assets/javascripts/compose.coffee +21 -0
- data/test/dummy/app/assets/javascripts/discuss.chosen.coffee +2 -0
- data/test/dummy/app/assets/javascripts/discuss.coffee +2 -0
- data/test/dummy/app/assets/javascripts/messages.coffee +0 -0
- data/test/dummy/app/assets/stylesheets/application.css +13 -0
- data/test/dummy/app/assets/stylesheets/chosen.css +439 -0
- data/test/dummy/app/assets/stylesheets/discuss.css +41 -0
- data/test/dummy/app/controllers/application_controller.rb +5 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/models/user.rb +22 -0
- data/test/dummy/app/views/layouts/application.html.erb +17 -0
- data/test/dummy/bin/bundle +3 -0
- data/test/dummy/bin/rails +4 -0
- data/test/dummy/bin/rake +4 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/config/application.rb +23 -0
- data/test/dummy/config/boot.rb +9 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +27 -0
- data/test/dummy/config/environments/production.rb +80 -0
- data/test/dummy/config/environments/test.rb +36 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/mime_types.rb +5 -0
- data/test/dummy/config/initializers/secret_token.rb +12 -0
- data/test/dummy/config/initializers/session_store.rb +3 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +23 -0
- data/test/dummy/config/routes.rb +4 -0
- data/test/dummy/db/development.sqlite3 +0 -0
- data/test/dummy/db/migrate/20130428235128_create_users.rb +11 -0
- data/test/dummy/db/schema.rb +24 -0
- data/test/dummy/db/test.sqlite3 +0 -0
- data/test/dummy/log/development.log +16219 -0
- data/test/dummy/log/test.log +191354 -0
- data/test/dummy/public/404.html +27 -0
- data/test/dummy/public/422.html +26 -0
- data/test/dummy/public/500.html +26 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/0a28667732e1be8938cde061b6f996df +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/0ddd7cc29057a69bd29fde03bf0f4c00 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/0de2f69502366965bb7fae2623bc190c +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/0ee691067e6ec8f03df58bc94badd25d +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/13fe41fee1fe35b49d145bcc06610705 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/1b77abb6bee52fa8d68ee7e81077ff20 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/271bb8f72971af6089be8d1b85788bec +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/2a499e57eb2664f139ab0e74571faa77 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/2f5173deea6c795b8fdde723bb4b63af +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/32b6af14546cb11e773fb3799f041132 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/357970feca3ac29060c1e3861e2c0953 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/4728f8b9616c35f685674fbeecaa3c47 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/4e9a3e121da30d3a85273a5c283da214 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/5620ff4659033b350ea3031ecaeb5127 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/596293af7e4dcfa45880435ae753f876 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/614995c5e7b1b4f5b12ce060404706cc +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/6b9e24a8aad25948823e1a93c8be731e +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/78a00003621b318a297e05cfedb0d1fd +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/794654d117cd2ec6a9a00e485d9f4485 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/79ae3a9f48e9b9e8ef2922c844b1895f +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/9b5889bea21f2af8068646890752932c +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/9fd02d20091838d6411df2d23a5364d5 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/a02345b542298f25d3f701ee680c44c0 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/a80359c0e09ddf510ce63de09a368d4c +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/ad2a9bfe4169e0a5bc66efd35c83fe38 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/b1794788ec8ce362aceaffa31c7381e9 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/b284f477944d67f25c0672dba1a2dab4 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/b2b1f16cf54d69c151ca7f42fb2f8381 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/b4767dfdb540517d26dc8c5365736f53 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/bab78b46d2a6282e5df747dd8b6d5abf +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/c9a09977565da30179ad7fdb9f79ae0d +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/cffd775d018f68ce5dba1ee0d951a994 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/d771ace226fc8215a3572e0aa35bb0d6 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/d9c8047218dc90a46a6725543638de45 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/e5b181eb056bbd6ffad37ed146b0342e +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/f7cbd26ba1d28d48de824f0e94586655 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/0a28667732e1be8938cde061b6f996df +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/0ddd7cc29057a69bd29fde03bf0f4c00 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/0de2f69502366965bb7fae2623bc190c +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/13fe41fee1fe35b49d145bcc06610705 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/1b77abb6bee52fa8d68ee7e81077ff20 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/271bb8f72971af6089be8d1b85788bec +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/2f5173deea6c795b8fdde723bb4b63af +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/32b6af14546cb11e773fb3799f041132 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/357970feca3ac29060c1e3861e2c0953 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/4728f8b9616c35f685674fbeecaa3c47 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/5620ff4659033b350ea3031ecaeb5127 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/596293af7e4dcfa45880435ae753f876 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/78a00003621b318a297e05cfedb0d1fd +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/794654d117cd2ec6a9a00e485d9f4485 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/79ae3a9f48e9b9e8ef2922c844b1895f +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/9b5889bea21f2af8068646890752932c +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/9fd02d20091838d6411df2d23a5364d5 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/a02345b542298f25d3f701ee680c44c0 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/a80359c0e09ddf510ce63de09a368d4c +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/ad2a9bfe4169e0a5bc66efd35c83fe38 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/bab78b46d2a6282e5df747dd8b6d5abf +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/c9a09977565da30179ad7fdb9f79ae0d +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/cffd775d018f68ce5dba1ee0d951a994 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/d771ace226fc8215a3572e0aa35bb0d6 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/d9c8047218dc90a46a6725543638de45 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/f7cbd26ba1d28d48de824f0e94586655 +0 -0
- data/test/features/workflow_test.rb +182 -0
- data/test/test_helper.rb +68 -0
- data/test/unit/discuss/conversation_test.rb +79 -0
- data/test/unit/discuss/mailbox_test.rb +72 -0
- data/test/unit/discuss/message_test.rb +73 -0
- data/test/unit/discuss/recipient_test.rb +56 -0
- data/test/unit/discuss/sender_test.rb +106 -0
- data/test/unit/helpers/discuss/messages_helper_test.rb +6 -0
- metadata +422 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require_dependency 'discuss/application_controller'
|
|
2
|
+
|
|
3
|
+
module Discuss
|
|
4
|
+
class MailboxesController < ApplicationController
|
|
5
|
+
before_action :check_mailbox_params, only: :show
|
|
6
|
+
|
|
7
|
+
def show
|
|
8
|
+
@messages = Mailbox.new(discuss_current_user).send mailbox_name
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def empty_trash
|
|
12
|
+
Mailbox.new(discuss_current_user).empty_trash!
|
|
13
|
+
redirect_to mailbox_path(:inbox), notice: 'Trash has been emptied'
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
def check_mailbox_params
|
|
18
|
+
redirect_to mailbox_path(:inbox) unless valid_mailbox?
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def valid_mailbox?
|
|
22
|
+
%w{inbox outbox drafts trash}.include? params[:mailbox]
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def mailbox_name
|
|
26
|
+
params[:mailbox]
|
|
27
|
+
end
|
|
28
|
+
helper_method :mailbox_name
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
require_dependency 'discuss/application_controller'
|
|
2
|
+
|
|
3
|
+
module Discuss
|
|
4
|
+
class MessagesController < ApplicationController
|
|
5
|
+
before_action :message, only: [:show, :update, :destroy]
|
|
6
|
+
before_action :can_view_message, only: [:show, :edit, :destroy]
|
|
7
|
+
|
|
8
|
+
def new
|
|
9
|
+
@message = discuss_current_user.messages.new
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def show
|
|
13
|
+
redirect_to edit_message_path(message) unless message.received? || message.sent?
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def create
|
|
17
|
+
@message = Message.new(message_params.merge(user: discuss_current_user))
|
|
18
|
+
if @message.save
|
|
19
|
+
send_message
|
|
20
|
+
else
|
|
21
|
+
render :new
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# [e] avoiding validation exception. Should be done nicer
|
|
26
|
+
def reply
|
|
27
|
+
@message = Message.find(params[:message_id])
|
|
28
|
+
if params[:message][:body]
|
|
29
|
+
@message.reply! message_params.merge(user: discuss_current_user)
|
|
30
|
+
redirect_to mailbox_path(:inbox), notice: 'Reply sent'
|
|
31
|
+
else
|
|
32
|
+
redirect_to message, alert: "Don't you want to say something?"
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def edit
|
|
37
|
+
redirect_to message unless message.unsent?
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def update
|
|
41
|
+
message.update(message_params)
|
|
42
|
+
send_message
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def trash
|
|
46
|
+
@message = Message.find(params[:message_id])
|
|
47
|
+
@message.trash!
|
|
48
|
+
redirect_to mailbox_path(:inbox), notice: 'Message moved to trash'
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def destroy
|
|
52
|
+
message.delete!
|
|
53
|
+
redirect_to mailbox_path(:inbox), notice: 'Message deleted'
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
def message
|
|
58
|
+
@message ||= Message.find(params[:id])
|
|
59
|
+
end
|
|
60
|
+
helper_method :message
|
|
61
|
+
|
|
62
|
+
def message_params
|
|
63
|
+
params.require(:message).permit(:subject, :body, :draft, draft_recipient_ids: [])
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def send_message
|
|
67
|
+
message.send!
|
|
68
|
+
notice = message.sent? ? 'Yay! Message sent' : 'Draft saved'
|
|
69
|
+
redirect_to mailbox_path(:inbox), notice: notice
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def mailbox_name
|
|
73
|
+
params[:mailbox] || message.mailbox.to_s
|
|
74
|
+
end
|
|
75
|
+
helper_method :mailbox_name
|
|
76
|
+
|
|
77
|
+
def can_view_message
|
|
78
|
+
redirect_to mailbox_path(:inbox), notice: 'Unauthorised!' unless mine?
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def mine?
|
|
82
|
+
message.user == discuss_current_user
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# conversation should be in the context of a user,
|
|
2
|
+
# so we grab only the messages the user owns
|
|
3
|
+
# but we are defaulting to the message user just in case
|
|
4
|
+
module Discuss
|
|
5
|
+
class Conversation
|
|
6
|
+
attr_accessor :message, :user
|
|
7
|
+
|
|
8
|
+
def initialize(message, user=nil)
|
|
9
|
+
@message = message
|
|
10
|
+
@user = user || message.user
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def root
|
|
14
|
+
message.root
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def for_user
|
|
18
|
+
all.by_user(user)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def all
|
|
22
|
+
root.subtree
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def trash_conversation!
|
|
26
|
+
for_user.each { |message| message.trash! }
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# @mailbox ||= Mailbox.new(current_user)
|
|
2
|
+
|
|
3
|
+
module Discuss
|
|
4
|
+
class Mailbox
|
|
5
|
+
attr_accessor :user
|
|
6
|
+
|
|
7
|
+
def initialize(user)
|
|
8
|
+
@user = user
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def inbox
|
|
12
|
+
Message.inbox(user)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def outbox
|
|
16
|
+
Message.outbox(user)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def drafts
|
|
20
|
+
Message.drafts(user)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def trash
|
|
24
|
+
Message.trash(user)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def empty_trash!
|
|
28
|
+
trash.each { |message| message.delete! }
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
require 'thread'
|
|
2
|
+
|
|
3
|
+
module Discuss
|
|
4
|
+
class Message < ActiveRecord::Base
|
|
5
|
+
has_ancestry
|
|
6
|
+
|
|
7
|
+
attr_accessor :draft
|
|
8
|
+
serialize :draft_recipient_ids, Array
|
|
9
|
+
|
|
10
|
+
belongs_to :user
|
|
11
|
+
|
|
12
|
+
validates :body, :user_id, presence: true
|
|
13
|
+
validate :lock_down_attributes, on: :update
|
|
14
|
+
|
|
15
|
+
scope :ordered, -> { order('created_at asc') }
|
|
16
|
+
scope :active, -> { not_trashed.not_deleted }
|
|
17
|
+
|
|
18
|
+
scope :draft, -> { where('sent_at is NULL') }
|
|
19
|
+
scope :not_draft, -> { where('sent_at is not NULL') }
|
|
20
|
+
|
|
21
|
+
scope :sent, -> { where('sent_at is not NULL') }
|
|
22
|
+
scope :unsent, -> { where('sent_at is NULL') }
|
|
23
|
+
|
|
24
|
+
scope :received, -> { where('received_at is not NULL') }
|
|
25
|
+
scope :not_received, -> { where('received_at is NULL') }
|
|
26
|
+
|
|
27
|
+
scope :trashed, -> { where('trashed_at is not NULL') }
|
|
28
|
+
scope :not_trashed, -> { where('trashed_at is NULL') }
|
|
29
|
+
|
|
30
|
+
scope :deleted, -> { where('deleted_at is not NULL') }
|
|
31
|
+
scope :not_deleted, -> { where('deleted_at is NULL') }
|
|
32
|
+
|
|
33
|
+
scope :by_user, lambda { |user| where(user_id: user.id) }
|
|
34
|
+
scope :inbox, lambda { |user| by_user(user).active.received }
|
|
35
|
+
scope :outbox, lambda { |user| by_user(user).active.sent }
|
|
36
|
+
scope :drafts, lambda { |user| by_user(user).active.draft.not_received }
|
|
37
|
+
scope :trash, lambda { |user| by_user(user).trashed.not_deleted }
|
|
38
|
+
|
|
39
|
+
def active?
|
|
40
|
+
!trashed? && !deleted?
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def sender
|
|
44
|
+
sent? ? user : parent.user
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def recipients
|
|
48
|
+
sent? ? children.collect(&:user) : parent.recipients
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def recipients= users
|
|
52
|
+
users.each { |u| draft_recipient_ids << u.id }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def recipient_list
|
|
56
|
+
draft_recipient_ids.reject(&:blank?).map {|id| User.find id}
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def mailbox
|
|
60
|
+
case
|
|
61
|
+
when sent? then :outbox
|
|
62
|
+
when received? then :inbox
|
|
63
|
+
when !new_record? && unsent? then :drafts
|
|
64
|
+
when trashed? then :trash
|
|
65
|
+
else
|
|
66
|
+
:compose
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def send!
|
|
71
|
+
lock.synchronize do
|
|
72
|
+
Discuss::MessageSender.new(self).run unless draft?
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def reply! options={}
|
|
77
|
+
if parent
|
|
78
|
+
reply = children.create!(subject: options.fetch(:subject, subject),
|
|
79
|
+
body: options.fetch(:body, nil),
|
|
80
|
+
user: user,
|
|
81
|
+
recipients: [parent.user])
|
|
82
|
+
reply.send!
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def receive!
|
|
87
|
+
update(received_at: Time.zone.now)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def read!
|
|
91
|
+
update(read_at: Time.zone.now)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def trash!
|
|
95
|
+
update(trashed_at: Time.zone.now)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def delete!
|
|
99
|
+
update(deleted_at: Time.zone.now)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
%w[sent received trashed deleted read].each do |act|
|
|
103
|
+
define_method "#{act}?" do
|
|
104
|
+
self.send(:"#{act}_at").present?
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def uneditable?
|
|
109
|
+
!editable?
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def unsent?
|
|
113
|
+
!sent?
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# passed in from the compose form
|
|
117
|
+
def draft?
|
|
118
|
+
self.draft == '1'
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def sent_date
|
|
122
|
+
sent_at || received_at
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
private
|
|
126
|
+
def lock_down_attributes
|
|
127
|
+
return if editable?
|
|
128
|
+
errors.add(:base, 'Cannot edit') unless deleted_at_changed? || trashed_at_changed? || read_at_changed?
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def lock
|
|
132
|
+
@lock ||= Mutex.new
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
class Discuss::MessageSender
|
|
2
|
+
attr_reader :message, :recipients
|
|
3
|
+
|
|
4
|
+
delegate :body, to: :message
|
|
5
|
+
delegate :subject, to: :message
|
|
6
|
+
|
|
7
|
+
def initialize message
|
|
8
|
+
@message = message
|
|
9
|
+
@recipients = message.recipient_list
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def run
|
|
13
|
+
deliver!
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
def set_as_sent
|
|
18
|
+
message.update_column(:sent_at, Time.zone.now)
|
|
19
|
+
message.update_column(:editable, false)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def deliver!
|
|
23
|
+
if recipients.any? && message.unsent?
|
|
24
|
+
set_as_sent
|
|
25
|
+
recipients.each { |user| deliver_to user }
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def deliver_to user
|
|
30
|
+
attrs = {subject: subject, body: body, user: user, received_at: Time.zone.now, editable: false }
|
|
31
|
+
message.children.create(attrs)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
%section#discuss(class="mailboxes #{mailbox_name}")
|
|
2
|
+
%h2= mailbox_name.humanize
|
|
3
|
+
|
|
4
|
+
.summary= pluralize(@messages.size, 'message')
|
|
5
|
+
|
|
6
|
+
= render 'discuss/shared/nav'
|
|
7
|
+
|
|
8
|
+
%section.messages
|
|
9
|
+
- if @messages.any?
|
|
10
|
+
%table
|
|
11
|
+
%tbody
|
|
12
|
+
- @messages.each do |message|
|
|
13
|
+
%tr[message]
|
|
14
|
+
%td.user
|
|
15
|
+
= link_to message_person(mailbox_name, message), mailbox_message_path(message.mailbox, message)
|
|
16
|
+
%td.preview
|
|
17
|
+
= link_to message.try(:subject), mailbox_message_path(message.mailbox, message)
|
|
18
|
+
= link_to truncate(message.body, length: 50), mailbox_message_path(message.mailbox, message)
|
|
19
|
+
%td.sent_at
|
|
20
|
+
= message.created_at.to_date
|
|
21
|
+
|
|
22
|
+
- if mailbox_name == 'trash'
|
|
23
|
+
= form_tag empty_trash_path do
|
|
24
|
+
= submit_tag 'Empty trash'
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
%section#discuss(class = "#{mailbox_name}")
|
|
2
|
+
%h2= mailbox_name.humanize
|
|
3
|
+
= render 'discuss/shared/nav'
|
|
4
|
+
|
|
5
|
+
.compose.message
|
|
6
|
+
= simple_form_for @message do |f|
|
|
7
|
+
- if @message.errors.any?
|
|
8
|
+
%p.errors This message has #{pluralize(@message.errors.count, 'error')}
|
|
9
|
+
|
|
10
|
+
- # TODO review User.all query
|
|
11
|
+
- # TODO if no first_name and last_name, display user's email
|
|
12
|
+
= f.input :draft_recipient_ids, collection: User.all.reject{|u| u == discuss_current_user }, input_html: {class: 'chosen', multiple: true}, label: 'Recipients'
|
|
13
|
+
= f.input :subject
|
|
14
|
+
= f.input :body, required: false, label: 'Your message'
|
|
15
|
+
= f.input :draft, as: :boolean
|
|
16
|
+
|
|
17
|
+
= f.submit 'Send message'
|
|
18
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
= render 'form'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
= render 'form'
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
%section#discuss(class = "#{mailbox_name}")
|
|
2
|
+
%h2= mailbox_name.humanize
|
|
3
|
+
|
|
4
|
+
= render 'discuss/shared/nav'
|
|
5
|
+
|
|
6
|
+
.message
|
|
7
|
+
%ul.headers
|
|
8
|
+
%li.subject Subject: #{message.try(:subject)}
|
|
9
|
+
%li.sender Sender: #{message.sender}
|
|
10
|
+
%li.sent_at Date: #{message.sent_date.to_date}
|
|
11
|
+
- # Do we want recipient names to be links? or have any data attributes like the user.id?
|
|
12
|
+
%li.recipients Recipients: #{message.recipients.join(', ')}
|
|
13
|
+
|
|
14
|
+
.body= message.body
|
|
15
|
+
|
|
16
|
+
- unless message.trashed?
|
|
17
|
+
.move_to_trash
|
|
18
|
+
= simple_form_for @message, url: message_trash_path(@message), method: :post do |f|
|
|
19
|
+
= f.submit 'Move to trash'
|
|
20
|
+
|
|
21
|
+
- if message.received?
|
|
22
|
+
.compose.reply
|
|
23
|
+
%h3 Reply
|
|
24
|
+
= simple_form_for @message, url: message_reply_path(@message), method: :post do |f|
|
|
25
|
+
= f.input :subject, input_html: { value: "Re: #{@message.subject}" }
|
|
26
|
+
= f.input :body, required: false, label: 'Your message', input_html: { value: '' }
|
|
27
|
+
= f.input :draft, as: :boolean
|
|
28
|
+
= f.submit 'Reply'
|