message_train 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.document +5 -0
- data/.rspec +1 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +7 -0
- data/Gemfile +51 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +83 -0
- data/Rakefile +71 -0
- data/VERSION +1 -0
- data/app/assets/javascripts/message_train.js +138 -0
- data/app/assets/stylesheets/message_train.scss +107 -0
- data/app/controllers/message_train/application_controller.rb +64 -0
- data/app/controllers/message_train/boxes_controller.rb +37 -0
- data/app/controllers/message_train/conversations_controller.rb +38 -0
- data/app/controllers/message_train/messages_controller.rb +74 -0
- data/app/controllers/message_train/participants_controller.rb +36 -0
- data/app/helpers/message_train/application_helper.rb +4 -0
- data/app/helpers/message_train/boxes_helper.rb +30 -0
- data/app/helpers/message_train/conversations_helper.rb +73 -0
- data/app/helpers/message_train/messages_helper.rb +58 -0
- data/app/models/message_train/attachment.rb +17 -0
- data/app/models/message_train/box.rb +268 -0
- data/app/models/message_train/conversation.rb +101 -0
- data/app/models/message_train/ignore.rb +18 -0
- data/app/models/message_train/message.rb +146 -0
- data/app/models/message_train/receipt.rb +70 -0
- data/app/views/application/404.html.haml +3 -0
- data/app/views/message_train/application/results.json.jbuilder +8 -0
- data/app/views/message_train/boxes/_dropdown_list.html.haml +5 -0
- data/app/views/message_train/boxes/_list_item.html.haml +5 -0
- data/app/views/message_train/boxes/_widget.html.haml +6 -0
- data/app/views/message_train/boxes/show.html.haml +47 -0
- data/app/views/message_train/conversations/_conversation.html.haml +9 -0
- data/app/views/message_train/conversations/_deleted_toggle.html.haml +1 -0
- data/app/views/message_train/conversations/_ignored_toggle.html.haml +4 -0
- data/app/views/message_train/conversations/_read_toggle.html.haml +4 -0
- data/app/views/message_train/conversations/_toggle.html.haml +1 -0
- data/app/views/message_train/conversations/_trashed_toggle.html.haml +4 -0
- data/app/views/message_train/conversations/show.html.haml +11 -0
- data/app/views/message_train/conversations/show.json.jbuilder +2 -0
- data/app/views/message_train/messages/_deleted_toggle.html.haml +1 -0
- data/app/views/message_train/messages/_form.html.haml +8 -0
- data/app/views/message_train/messages/_message.html.haml +35 -0
- data/app/views/message_train/messages/_read_toggle.html.haml +4 -0
- data/app/views/message_train/messages/_toggle.html.haml +1 -0
- data/app/views/message_train/messages/_trashed_toggle.html.haml +4 -0
- data/app/views/message_train/messages/edit.html.haml +2 -0
- data/app/views/message_train/messages/new.html.haml +2 -0
- data/app/views/message_train/messages/show.json.jbuilder +2 -0
- data/app/views/message_train/participants/_field.html.haml +4 -0
- data/app/views/message_train/participants/_participant.json.jbuilder +5 -0
- data/app/views/message_train/participants/index.json.jbuilder +3 -0
- data/app/views/message_train/participants/show.json.jbuilder +1 -0
- data/bin/rails +12 -0
- data/config/initializers/bootstrap_pager_config.rb +10 -0
- data/config/initializers/date_time.rb +5 -0
- data/config/locales/en.yml +50 -0
- data/config/routes.rb +11 -0
- data/db/migrate/20150721145319_create_message_train_conversations.rb +9 -0
- data/db/migrate/20150721160322_create_message_train_messages.rb +14 -0
- data/db/migrate/20150721161144_create_message_train_attachments.rb +10 -0
- data/db/migrate/20150721161940_create_message_train_receipts.rb +16 -0
- data/db/migrate/20150721163838_create_message_train_ignores.rb +11 -0
- data/lib/generators/message_train/install/install_generator.rb +30 -0
- data/lib/generators/message_train/install/templates/initializer.rb +10 -0
- data/lib/generators/message_train/utils.rb +16 -0
- data/lib/message_train/configuration.rb +35 -0
- data/lib/message_train/engine.rb +19 -0
- data/lib/message_train/localization.rb +31 -0
- data/lib/message_train/mixin.rb +83 -0
- data/lib/message_train/version.rb +7 -0
- data/lib/message_train.rb +20 -0
- data/lib/tasks/message_train_tasks.rake +0 -0
- data/message_train.gemspec +325 -0
- data/night_train.gemspec +274 -0
- data/spec/controllers/message_train/boxes_controller_spec.rb +82 -0
- data/spec/controllers/message_train/conversations_controller_spec.rb +86 -0
- data/spec/controllers/message_train/messages_controller_spec.rb +210 -0
- data/spec/controllers/message_train/participants_controller_spec.rb +35 -0
- data/spec/dummy/.rspec +2 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/assets/files/message_train/attachments/1917-Boys_Race_Above-Wiki.jpg +0 -0
- data/spec/dummy/app/assets/files/message_train/attachments/ACD_Silverbarn's_Mayumi.jpg +0 -0
- data/spec/dummy/app/assets/files/message_train/attachments/ACD_obedience.jpg +0 -0
- data/spec/dummy/app/assets/files/message_train/attachments/A_Man_and_His_Dog.jpg +0 -0
- data/spec/dummy/app/assets/files/message_train/attachments/Alaskan_Malamute_agility_a-frame.jpg +0 -0
- data/spec/dummy/app/assets/files/message_train/attachments/American_water_spaniel_02.jpg +0 -0
- data/spec/dummy/app/assets/files/message_train/attachments/Aport_konkurs_rybnik.jpg +0 -0
- data/spec/dummy/app/assets/files/message_train/attachments/Askerhytta.jpg +0 -0
- data/spec/dummy/app/assets/files/message_train/attachments/Bambisj.jpg +0 -0
- data/spec/dummy/app/assets/files/message_train/attachments/Cattle_dog_skating.jpg +0 -0
- data/spec/dummy/app/assets/files/message_train/attachments/Dog's_Olympics_training_ground_-_geograph.org.uk_-_660324.jpg +0 -0
- data/spec/dummy/app/assets/files/message_train/attachments/DogShowHierarchy.png +0 -0
- data/spec/dummy/app/assets/files/message_train/attachments/Dog_weight_pull.jpg +0 -0
- data/spec/dummy/app/assets/files/message_train/attachments/Draghuntpostcard.jpg +0 -0
- data/spec/dummy/app/assets/files/message_train/attachments/Esham_51.JPG +0 -0
- data/spec/dummy/app/assets/files/message_train/attachments/FastCourseExample.jpg +0 -0
- data/spec/dummy/app/assets/files/message_train/attachments/Fotos_joel_141.JPG +0 -0
- data/spec/dummy/app/assets/files/message_train/attachments/Galgo_Spanish_male_brindle.jpg +0 -0
- data/spec/dummy/app/assets/files/message_train/attachments/Golden-retriever-carlos-bei-der-dummyarbeit.jpg +0 -0
- data/spec/dummy/app/assets/files/message_train/attachments/Habavk.jpg +0 -0
- data/spec/dummy/app/assets/files/message_train/attachments/Haie_rci.svg +1714 -0
- data/spec/dummy/app/assets/files/message_train/attachments/Hanging_18.jpg +0 -0
- data/spec/dummy/app/assets/files/message_train/attachments/Hundewa.jpg +0 -0
- data/spec/dummy/app/assets/files/message_train/attachments/K9ProSports_AttackOnHandler.jpg +0 -0
- data/spec/dummy/app/assets/files/message_train/attachments/K9ProSports_heel.jpg +0 -0
- data/spec/dummy/app/assets/files/message_train/attachments/K9Pro_Civil_agitation.JPG +0 -0
- data/spec/dummy/app/assets/files/message_train/attachments/TrainingSchutzhundRetrieveOverWall.jpg +0 -0
- data/spec/dummy/app/assets/files/message_train/attachments/example.pdf +0 -0
- data/spec/dummy/app/assets/files/message_train/attachments/letterlegal5.doc +0 -0
- data/spec/dummy/app/assets/files/message_train/attachments/pdf-sample.pdf +0 -0
- data/spec/dummy/app/assets/files/message_train/attachments/sample.pdf +0 -0
- data/spec/dummy/app/assets/files/message_train/attachments/tips.doc +0 -0
- data/spec/dummy/app/assets/files/message_train/attachments/wd-spectools-word-sample-04.doc +0 -0
- data/spec/dummy/app/assets/images/logo.svg +140 -0
- data/spec/dummy/app/assets/javascripts/application.js +17 -0
- data/spec/dummy/app/assets/stylesheets/application.css.scss +35 -0
- data/spec/dummy/app/controllers/application_controller.rb +14 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/mailers/.keep +0 -0
- data/spec/dummy/app/models/.keep +0 -0
- data/spec/dummy/app/models/group.rb +19 -0
- data/spec/dummy/app/models/role.rb +10 -0
- data/spec/dummy/app/models/user.rb +23 -0
- data/spec/dummy/app/views/layouts/_top_navigation.html.haml +5 -0
- data/spec/dummy/app/views/layouts/application.html.haml +29 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/bin/setup +29 -0
- data/spec/dummy/config/application.rb +40 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +41 -0
- data/spec/dummy/config/environments/production.rb +79 -0
- data/spec/dummy/config/environments/test.rb +42 -0
- data/spec/dummy/config/initializers/assets.rb +11 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/bootstrap_leather.rb +7 -0
- data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/spec/dummy/config/initializers/devise.rb +265 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/message_train.rb +10 -0
- data/spec/dummy/config/initializers/mime_types.rb +4 -0
- data/spec/dummy/config/initializers/paperclip.rb +2 -0
- data/spec/dummy/config/initializers/rolify.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +3 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/devise.en.yml +60 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/routes.rb +5 -0
- data/spec/dummy/config/secrets.yml +22 -0
- data/spec/dummy/config/unused/temporary_i18n_debugging.rb +12 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/db/migrate/20150721140013_devise_create_users.rb +42 -0
- data/spec/dummy/db/migrate/20150721141009_rolify_create_roles.rb +19 -0
- data/spec/dummy/db/migrate/20150721141128_create_groups.rb +11 -0
- data/spec/dummy/db/migrate/20150721150307_add_display_name_column_to_users.rb +6 -0
- data/spec/dummy/db/migrate/20150724142846_create_message_train_conversations.night_train.rb +10 -0
- data/spec/dummy/db/migrate/20150724142847_create_message_train_messages.night_train.rb +15 -0
- data/spec/dummy/db/migrate/20150724142848_create_message_train_attachments.night_train.rb +11 -0
- data/spec/dummy/db/migrate/20150724142849_create_message_train_receipts.night_train.rb +17 -0
- data/spec/dummy/db/migrate/20150724142850_create_message_train_ignores.night_train.rb +12 -0
- data/spec/dummy/db/schema.rb +121 -0
- data/spec/dummy/db/seeds/conversations.seeds.rb +86 -0
- data/spec/dummy/db/seeds/development/conversations.seeds.rb +9 -0
- data/spec/dummy/db/seeds/groups.seeds.rb +9 -0
- data/spec/dummy/db/seeds/test/conversations.seeds.rb +9 -0
- data/spec/dummy/db/seeds/users.seeds.rb +15 -0
- data/spec/dummy/db/seeds.rb +0 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/lib/assets/.keep +0 -0
- data/spec/dummy/log/.keep +0 -0
- data/spec/dummy/public/404.html +67 -0
- data/spec/dummy/public/422.html +67 -0
- data/spec/dummy/public/500.html +66 -0
- data/spec/dummy/public/capybara.html +193 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/factories/attachment.rb +6 -0
- data/spec/factories/group.rb +20 -0
- data/spec/factories/message.rb +68 -0
- data/spec/factories/user.rb +8 -0
- data/spec/features/boxes_spec.rb +50 -0
- data/spec/features/conversations_spec.rb +36 -0
- data/spec/features/messages_spec.rb +53 -0
- data/spec/helpers/message_train/boxes_helper_spec.rb +44 -0
- data/spec/message_train_spec.rb +7 -0
- data/spec/models/group_spec.rb +23 -0
- data/spec/models/message_train/attachment_spec.rb +12 -0
- data/spec/models/message_train/box_spec.rb +195 -0
- data/spec/models/message_train/conversation_spec.rb +98 -0
- data/spec/models/message_train/ignore_spec.rb +27 -0
- data/spec/models/message_train/message_spec.rb +83 -0
- data/spec/models/message_train/receipt_spec.rb +73 -0
- data/spec/models/user_spec.rb +68 -0
- data/spec/rails_helper.rb +83 -0
- data/spec/spec_helper.rb +92 -0
- data/spec/support/controller_behaviors.rb +88 -0
- data/spec/support/controller_macros.rb +13 -0
- data/spec/support/conversations.rb +10 -0
- data/spec/support/factory_girl.rb +3 -0
- data/spec/support/feature_behaviors.rb +62 -0
- data/spec/support/groups.rb +3 -0
- data/spec/support/loaded_site.rb +5 -0
- data/spec/support/shared_connection.rb +13 -0
- data/spec/support/users.rb +6 -0
- data/spec/support/utilities.rb +8 -0
- data/vendor/assets/javascripts/bootstrap-tags.js +623 -0
- data/vendor/assets/stylesheets/bootstrap-tags.css +78 -0
- metadata +608 -0
@@ -0,0 +1,74 @@
|
|
1
|
+
module MessageTrain
|
2
|
+
class MessagesController < MessageTrain::ApplicationController
|
3
|
+
before_filter :load_message, only: [:show, :edit, :update]
|
4
|
+
|
5
|
+
# GET /box/in/messages/1
|
6
|
+
def show
|
7
|
+
respond_to do |format|
|
8
|
+
format.json { render :show }
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# GET /box/in/messages/new
|
13
|
+
def new
|
14
|
+
@message = @box.new_message(conversation_id: params[:conversation_id])
|
15
|
+
end
|
16
|
+
|
17
|
+
# GET /box/in/messages/1/edit
|
18
|
+
def edit
|
19
|
+
unless @message.draft
|
20
|
+
raise ActiveRecord::RecordNotFound
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# POST /box/in/messages
|
25
|
+
def create
|
26
|
+
@message = @box.send_message(message_params)
|
27
|
+
if @box.errors.all.empty?
|
28
|
+
if @message.draft
|
29
|
+
redirect_to message_train.box_path(:drafts), alert: :message_saved_as_draft.l
|
30
|
+
else
|
31
|
+
redirect_to message_train.box_path(:sent), notice: :message_sent.l
|
32
|
+
end
|
33
|
+
else
|
34
|
+
flash[:error] = @box.message
|
35
|
+
render :new
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# PATCH/PUT /box/in/messages/1
|
40
|
+
def update
|
41
|
+
unless @message.draft
|
42
|
+
raise ActiveRecord::RecordNotFound
|
43
|
+
end
|
44
|
+
@box.update_message(@message, message_params)
|
45
|
+
if @box.errors.all.empty?
|
46
|
+
if @message.draft
|
47
|
+
redirect_to message_train.box_conversation_url(@box, @message.conversation), alert: :message_saved_as_draft.l
|
48
|
+
else
|
49
|
+
redirect_to message_train.box_path(:sent), notice: :message_sent.l
|
50
|
+
end
|
51
|
+
else
|
52
|
+
flash[:error] = @box.message
|
53
|
+
render :edit
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
def load_message
|
59
|
+
@message = @box.find_message(params[:id])
|
60
|
+
end
|
61
|
+
|
62
|
+
# Never trust parameters from the scary internet, only allow the white list through.
|
63
|
+
def message_params
|
64
|
+
params.require(:message).permit(
|
65
|
+
:conversation_id,
|
66
|
+
:subject,
|
67
|
+
:body,
|
68
|
+
:draft,
|
69
|
+
attachments: [:attachment],
|
70
|
+
recipients_to_save: MessageTrain.configuration.recipient_tables
|
71
|
+
)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module MessageTrain
|
2
|
+
class ParticipantsController < MessageTrain::ApplicationController
|
3
|
+
before_filter :load_participants
|
4
|
+
before_filter :load_participant, only: :show
|
5
|
+
|
6
|
+
# GET /box/in/participants
|
7
|
+
def index
|
8
|
+
respond_to do |format|
|
9
|
+
format.json { render :index }
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# GET /box/in/participants/1
|
14
|
+
def show
|
15
|
+
respond_to do |format|
|
16
|
+
format.json { render :show }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def load_participants
|
23
|
+
current_participant = send(MessageTrain.configuration.current_user_method)
|
24
|
+
method = MessageTrain.configuration.address_book_method
|
25
|
+
if current_participant.respond_to? method
|
26
|
+
@participants = current_participant.send(method)
|
27
|
+
else
|
28
|
+
@participants = current_participant.class.all
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def load_participant
|
33
|
+
@participant = @participants.find(params[:id])
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module MessageTrain
|
2
|
+
module BoxesHelper
|
3
|
+
def box_nav_item(box)
|
4
|
+
text = box.title
|
5
|
+
link = message_train.box_path(box.division)
|
6
|
+
if box.unread_count > 0
|
7
|
+
text << ' '
|
8
|
+
text << badge(box.unread_count.to_s, 'info pull-right')
|
9
|
+
end
|
10
|
+
nav_item text, link
|
11
|
+
end
|
12
|
+
def box_list_item(box, html_options = {})
|
13
|
+
render partial: 'message_train/boxes/list_item', locals: { box: box, html_options: html_options }
|
14
|
+
end
|
15
|
+
def boxes_widget
|
16
|
+
boxes = send(MessageTrain.configuration.current_user_method).all_boxes
|
17
|
+
render partial: 'message_train/boxes/widget', locals: { boxes: boxes }
|
18
|
+
end
|
19
|
+
def boxes_dropdown_list
|
20
|
+
boxes = send(MessageTrain.configuration.current_user_method).all_boxes
|
21
|
+
render partial: 'message_train/boxes/dropdown_list', locals: { boxes: boxes }
|
22
|
+
end
|
23
|
+
def box_participant_name(participant)
|
24
|
+
participant.send(MessageTrain.configuration.name_columns[participant.class.table_name.to_sym])
|
25
|
+
end
|
26
|
+
def box_participant_slug(participant)
|
27
|
+
participant.send(MessageTrain.configuration.slug_columns[participant.class.table_name.to_sym])
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module MessageTrain
|
2
|
+
module ConversationsHelper
|
3
|
+
def conversation_senders(conversation)
|
4
|
+
conversation.messages.collect { |x| box_participant_name(x.sender) }.uniq.to_sentence
|
5
|
+
end
|
6
|
+
|
7
|
+
def conversation_class(box, conversation)
|
8
|
+
css_classes = []
|
9
|
+
|
10
|
+
if conversation.includes_unread_for?(@box.parent)
|
11
|
+
css_classes << 'unread'
|
12
|
+
else
|
13
|
+
css_classes << 'read'
|
14
|
+
end
|
15
|
+
|
16
|
+
if conversation.includes_drafts_by?(@box.parent)
|
17
|
+
css_classes << 'draft'
|
18
|
+
end
|
19
|
+
|
20
|
+
unless conversation.includes_undeleted_for?(box.parent)
|
21
|
+
css_classes << 'hide'
|
22
|
+
end
|
23
|
+
|
24
|
+
if box.division == :trash
|
25
|
+
unless conversation.includes_trashed_for?(box.parent)
|
26
|
+
css_classes << 'hide'
|
27
|
+
end
|
28
|
+
else
|
29
|
+
unless conversation.includes_untrashed_for?(box.parent)
|
30
|
+
css_classes << 'hide'
|
31
|
+
end
|
32
|
+
if box.division == :ignored
|
33
|
+
unless conversation.is_ignored?(box.parent)
|
34
|
+
css_classes << 'hide'
|
35
|
+
end
|
36
|
+
else
|
37
|
+
if conversation.is_ignored?(box.parent)
|
38
|
+
css_classes << 'hide'
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
css_classes.uniq.join(' ')
|
43
|
+
end
|
44
|
+
|
45
|
+
def conversation_trashed_toggle(conversation)
|
46
|
+
render partial: 'message_train/conversations/trashed_toggle', locals: { conversation: conversation }
|
47
|
+
end
|
48
|
+
|
49
|
+
def conversation_read_toggle(conversation)
|
50
|
+
render partial: 'message_train/conversations/read_toggle', locals: { conversation: conversation }
|
51
|
+
end
|
52
|
+
|
53
|
+
def conversation_ignored_toggle(conversation)
|
54
|
+
render partial: 'message_train/conversations/ignored_toggle', locals: { conversation: conversation }
|
55
|
+
end
|
56
|
+
|
57
|
+
def conversation_deleted_toggle(conversation)
|
58
|
+
render partial: 'message_train/conversations/deleted_toggle', locals: { conversation: conversation }
|
59
|
+
end
|
60
|
+
|
61
|
+
def conversation_toggle(conversation, icon, mark_to_set, method, title, options = {})
|
62
|
+
options[:remote] = true
|
63
|
+
options[:method] = method
|
64
|
+
options[:title] = title
|
65
|
+
render partial: 'message_train/conversations/toggle', locals: {
|
66
|
+
conversation: conversation,
|
67
|
+
icon: icon,
|
68
|
+
mark_to_set: mark_to_set,
|
69
|
+
options: options
|
70
|
+
}
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module MessageTrain
|
2
|
+
module MessagesHelper
|
3
|
+
def message_class(box, message)
|
4
|
+
css_classes = []
|
5
|
+
|
6
|
+
if message.is_unread_for?(@box.parent)
|
7
|
+
css_classes << 'unread panel-info'
|
8
|
+
else
|
9
|
+
css_classes << 'read'
|
10
|
+
end
|
11
|
+
|
12
|
+
if message.draft
|
13
|
+
css_classes << 'draft'
|
14
|
+
end
|
15
|
+
|
16
|
+
if box.division == :trash
|
17
|
+
unless message.is_trashed_for?(box.parent)
|
18
|
+
css_classes << 'hide'
|
19
|
+
end
|
20
|
+
else
|
21
|
+
unless message.is_untrashed_for?(box.parent)
|
22
|
+
css_classes << 'hide'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
css_classes.join(' ')
|
26
|
+
end
|
27
|
+
|
28
|
+
def message_trashed_toggle(message)
|
29
|
+
render partial: 'message_train/messages/trashed_toggle', locals: { message: message }
|
30
|
+
end
|
31
|
+
|
32
|
+
def message_read_toggle(message)
|
33
|
+
render partial: 'message_train/messages/read_toggle', locals: { message: message }
|
34
|
+
end
|
35
|
+
|
36
|
+
def message_deleted_toggle(message)
|
37
|
+
render partial: 'message_train/messages/deleted_toggle', locals: { message: message }
|
38
|
+
end
|
39
|
+
|
40
|
+
def message_toggle(message, icon, mark_to_set, title, options = {})
|
41
|
+
options[:remote] = true
|
42
|
+
options[:id] = "mark_#{mark_to_set}_#{message.id}"
|
43
|
+
options[:class] = "mark-link"
|
44
|
+
options[:method] = :put
|
45
|
+
options[:title] = title
|
46
|
+
render partial: 'message_train/messages/toggle', locals: {
|
47
|
+
message: message,
|
48
|
+
icon: icon,
|
49
|
+
mark_to_set: mark_to_set,
|
50
|
+
options: options
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
def message_recipients(message)
|
55
|
+
message.recipients.collect { |x| box_participant_name(x) }.to_sentence
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module MessageTrain
|
2
|
+
class Attachment < ActiveRecord::Base
|
3
|
+
belongs_to :message
|
4
|
+
has_attached_file :attachment
|
5
|
+
validates_attachment_presence :attachment
|
6
|
+
validates_attachment_content_type :attachment, content_type: [
|
7
|
+
"application/pdf",
|
8
|
+
"application/vnd.ms-excel",
|
9
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
10
|
+
"application/msword",
|
11
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
12
|
+
"application/rtf",
|
13
|
+
"text/plain",
|
14
|
+
/^image\/.*$/
|
15
|
+
]
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,268 @@
|
|
1
|
+
module MessageTrain
|
2
|
+
class Box
|
3
|
+
include ActiveModel::Model
|
4
|
+
attr_accessor :parent, :division, :errors, :results
|
5
|
+
alias_method :id, :division
|
6
|
+
|
7
|
+
def initialize(parent, division)
|
8
|
+
@parent = parent
|
9
|
+
@division = division
|
10
|
+
@results = Results.new(self)
|
11
|
+
@errors = Errors.new(self)
|
12
|
+
end
|
13
|
+
|
14
|
+
def marks
|
15
|
+
{ conversations: nil }
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_param
|
19
|
+
division.to_s
|
20
|
+
end
|
21
|
+
|
22
|
+
def unread_count
|
23
|
+
conversations(unread: true).count
|
24
|
+
end
|
25
|
+
|
26
|
+
def conversations(options = {})
|
27
|
+
found = parent.conversations(division).with_undeleted_for(parent)
|
28
|
+
if options[:read] == false || options[:unread]
|
29
|
+
found = found.with_unread_for(parent)
|
30
|
+
end
|
31
|
+
if division == :trash
|
32
|
+
found = found.with_trashed_for(parent)
|
33
|
+
else
|
34
|
+
found = found.with_untrashed_for(parent)
|
35
|
+
if division == :drafts
|
36
|
+
found = found.with_drafts_by(parent)
|
37
|
+
else
|
38
|
+
found = found.with_ready_for(parent)
|
39
|
+
end
|
40
|
+
if division == :ignored
|
41
|
+
found = found.ignored(parent)
|
42
|
+
else
|
43
|
+
found = found.unignored(parent)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
found
|
47
|
+
end
|
48
|
+
|
49
|
+
def find_conversation(id)
|
50
|
+
parent.all_conversations.find(id)
|
51
|
+
end
|
52
|
+
|
53
|
+
def find_message(id)
|
54
|
+
parent.all_messages.find(id)
|
55
|
+
end
|
56
|
+
|
57
|
+
def new_message(args = {})
|
58
|
+
if args[:conversation_id].nil?
|
59
|
+
message = MessageTrain::Message.new(args)
|
60
|
+
else
|
61
|
+
conversation = find_conversation(args[:conversation_id])
|
62
|
+
message = conversation.messages.build(args)
|
63
|
+
message.subject = "Re: #{conversation.subject}"
|
64
|
+
recipient_arrays = {}
|
65
|
+
conversation.default_recipients_for(parent).each do |recipient|
|
66
|
+
table_name = recipient.class.table_name
|
67
|
+
recipient_arrays[table_name] ||= []
|
68
|
+
recipient_arrays[table_name] << recipient.send(MessageTrain.configuration.slug_columns[table_name.to_sym])
|
69
|
+
end
|
70
|
+
recipient_arrays.each do |key, array|
|
71
|
+
message.recipients_to_save[key] = array.join(', ')
|
72
|
+
end
|
73
|
+
end
|
74
|
+
message
|
75
|
+
end
|
76
|
+
|
77
|
+
def send_message(attributes)
|
78
|
+
message_to_send = MessageTrain::Message.new attributes
|
79
|
+
message_to_send.sender = parent
|
80
|
+
unless message_to_send.save
|
81
|
+
errors.add(message_to_send, message_to_send.errors.full_messages.to_sentence)
|
82
|
+
end
|
83
|
+
message_to_send
|
84
|
+
end
|
85
|
+
|
86
|
+
def update_message(message_to_update, attributes)
|
87
|
+
attributes.delete(:sender)
|
88
|
+
if message_to_update.sender == parent
|
89
|
+
message_to_update.update(attributes)
|
90
|
+
unless message_to_update.errors.empty?
|
91
|
+
errors.add(message_to_update, message_to_update.errors.full_messages.to_sentence)
|
92
|
+
end
|
93
|
+
else
|
94
|
+
errors.add(message_to_update, :access_to_message_id_denied.l(id: message_to_update.id))
|
95
|
+
end
|
96
|
+
message_to_update.reload
|
97
|
+
end
|
98
|
+
|
99
|
+
def ignore(object)
|
100
|
+
case object.class.name
|
101
|
+
when 'Hash'
|
102
|
+
ignore object.values
|
103
|
+
when 'Array'
|
104
|
+
object.collect { |item| ignore(item) }.uniq == [true]
|
105
|
+
when 'String', 'Fixnum'
|
106
|
+
id = object.to_i
|
107
|
+
object = parent.all_conversations.find_by_id(id)
|
108
|
+
if object.nil?
|
109
|
+
errors.add(self, :class_id_not_found_in_box.l(class: 'Conversation', id: id.to_s))
|
110
|
+
else
|
111
|
+
ignore(object)
|
112
|
+
end
|
113
|
+
when 'MessageTrain::Conversation'
|
114
|
+
if authorize(object)
|
115
|
+
if object.set_ignored(parent)
|
116
|
+
results.add(object, :update_successful.l)
|
117
|
+
else
|
118
|
+
errors.add(object, object.errors.full_messages.to_sentence)
|
119
|
+
end
|
120
|
+
else
|
121
|
+
false
|
122
|
+
end
|
123
|
+
else
|
124
|
+
errors.add(self, :cannot_ignore_type.l(type: object.class.name))
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def unignore(object)
|
129
|
+
case object.class.name
|
130
|
+
when 'Hash'
|
131
|
+
unignore object.values
|
132
|
+
when 'Array'
|
133
|
+
object.collect { |item| unignore(item) }.uniq == [true]
|
134
|
+
when 'String', 'Fixnum'
|
135
|
+
id = object.to_i
|
136
|
+
object = parent.all_conversations.find_by_id(id)
|
137
|
+
if object.nil?
|
138
|
+
errors.add(self, :class_id_not_found_in_box.l(class: 'Conversation', id: id.to_s))
|
139
|
+
else
|
140
|
+
unignore(object)
|
141
|
+
end
|
142
|
+
when 'MessageTrain::Conversation'
|
143
|
+
if authorize(object)
|
144
|
+
if object.set_unignored(parent)
|
145
|
+
results.add(object, :update_successful.l)
|
146
|
+
else
|
147
|
+
errors.add(object, object.errors.full_messages.to_sentence)
|
148
|
+
end
|
149
|
+
else
|
150
|
+
false
|
151
|
+
end
|
152
|
+
else
|
153
|
+
errors.add(self, :cannot_ignore_type.l(type: object.class.name))
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def title
|
158
|
+
"box_title_#{division.to_s}".to_sym.l
|
159
|
+
end
|
160
|
+
|
161
|
+
def message
|
162
|
+
if !errors.all.empty?
|
163
|
+
errors.all.collect { |x| x[:message] }.uniq.to_sentence
|
164
|
+
elsif results.all.empty?
|
165
|
+
:nothing_to_do.l
|
166
|
+
else
|
167
|
+
results.all.collect { |x| x[:message] }.uniq.to_sentence
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def mark(mark_to_set, objects)
|
172
|
+
objects.each do |key, object|
|
173
|
+
if object.present?
|
174
|
+
case object.class.name
|
175
|
+
when 'Hash'
|
176
|
+
mark(mark_to_set, { key => object.values } )
|
177
|
+
when 'Array'
|
178
|
+
object.collect { |item| mark(mark_to_set, key => item) }.uniq == [true]
|
179
|
+
when 'String', 'Fixnum'
|
180
|
+
id = object.to_i
|
181
|
+
object = parent.send("all_#{key}".to_sym).find_by_id(id)
|
182
|
+
if object.nil?
|
183
|
+
errors.add(self, :class_id_not_found_in_box.l(class: key.to_s.classify, id: id.to_s))
|
184
|
+
else
|
185
|
+
mark(mark_to_set, key => object)
|
186
|
+
end
|
187
|
+
when 'MessageTrain::Conversation', 'MessageTrain::Message'
|
188
|
+
if authorize(object)
|
189
|
+
if object.mark(mark_to_set, parent)
|
190
|
+
results.add(object, :update_successful.l)
|
191
|
+
else
|
192
|
+
errors.add(object, object.errors.full_messages.to_sentence)
|
193
|
+
end
|
194
|
+
else
|
195
|
+
false
|
196
|
+
end
|
197
|
+
else
|
198
|
+
errors.add(self, :cannot_mark_type.l(type: object.class.name))
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def authorize(object)
|
205
|
+
case object.class.name
|
206
|
+
when 'MessageTrain::Conversation'
|
207
|
+
if object.includes_receipts_for? parent
|
208
|
+
true
|
209
|
+
else
|
210
|
+
errors.add(object, :access_to_conversation_id_denied.l(id: object.id))
|
211
|
+
end
|
212
|
+
when 'MessageTrain::Message'
|
213
|
+
if object.receipts.for(parent).any?
|
214
|
+
true
|
215
|
+
else
|
216
|
+
errors.add(object, :access_to_message_id_denied.l(id: object.id))
|
217
|
+
end
|
218
|
+
else
|
219
|
+
errors.add(object, :dont_know_how_to_mark_object.l(object: object.class.name))
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
class Results
|
224
|
+
attr_accessor :items, :box
|
225
|
+
|
226
|
+
def initialize(box)
|
227
|
+
@items = []
|
228
|
+
@box = box
|
229
|
+
end
|
230
|
+
|
231
|
+
def add(object, message)
|
232
|
+
item = {}
|
233
|
+
if object.is_a? MessageTrain::Box
|
234
|
+
item[:css_id] = 'box'
|
235
|
+
item[:path] = MessageTrain::Engine.routes.path_for({
|
236
|
+
controller: 'message_train/boxes',
|
237
|
+
action: :show,
|
238
|
+
division: object.division
|
239
|
+
})
|
240
|
+
elsif object.new_record?
|
241
|
+
item[:css_id] = "#{object.class.table_name.singularize}"
|
242
|
+
item[:path] = nil
|
243
|
+
else
|
244
|
+
item[:css_id] = "#{object.class.table_name.singularize}_#{object.id.to_s}"
|
245
|
+
item[:path] = MessageTrain::Engine.routes.path_for({
|
246
|
+
controller: object.class.table_name.gsub('message_train_', 'message_train/'),
|
247
|
+
action: :show,
|
248
|
+
box_division: box.division,
|
249
|
+
id: object.id
|
250
|
+
})
|
251
|
+
end
|
252
|
+
item[:message] = message
|
253
|
+
items << item
|
254
|
+
true
|
255
|
+
end
|
256
|
+
|
257
|
+
def all
|
258
|
+
items
|
259
|
+
end
|
260
|
+
end
|
261
|
+
class Errors < Results
|
262
|
+
def add(object, message)
|
263
|
+
super object, message
|
264
|
+
false
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
module MessageTrain
|
2
|
+
class Conversation < ActiveRecord::Base
|
3
|
+
has_many :messages
|
4
|
+
has_many :ignores
|
5
|
+
has_many :receipts, through: :messages
|
6
|
+
|
7
|
+
# Scopes
|
8
|
+
default_scope { order('updated_at DESC') }
|
9
|
+
scope :ignored, ->(participant) { where('id IN (?)', ignored_ids_for(participant))}
|
10
|
+
scope :unignored, ->(participant) { where('NOT(id IN (?))', ignored_ids_for(participant))}
|
11
|
+
scope :filter_by_receipt_method_ids, ->(receipt_method, participant) {
|
12
|
+
all.collect { |x| x.receipts.send(receipt_method, participant).conversation_ids }.flatten.uniq
|
13
|
+
}
|
14
|
+
scope :filter_by_receipt_method, ->(receipt_method, participant) {
|
15
|
+
where('id IN (?)', filter_by_receipt_method_ids(receipt_method, participant))
|
16
|
+
}
|
17
|
+
scope :with_drafts_by, ->(participant) {
|
18
|
+
ids_with_drafts = all.collect { |x| x.messages.drafts.by(participant).conversation_ids }.flatten.uniq
|
19
|
+
where('id IN (?)', ids_with_drafts)
|
20
|
+
}
|
21
|
+
scope :with_ready_for, ->(participant) {
|
22
|
+
ids_with_ready = all.collect { |x| x.messages.ready.with_receipts_for(participant).conversation_ids }.flatten.uniq
|
23
|
+
where('id IN (?)', ids_with_ready)
|
24
|
+
}
|
25
|
+
|
26
|
+
def default_recipients_for(sender)
|
27
|
+
recipients = messages.with_receipts_for(sender)
|
28
|
+
.collect { |x| x.receipts.collect { |y| y.recipient } }
|
29
|
+
.flatten
|
30
|
+
.uniq
|
31
|
+
recipients.delete(sender)
|
32
|
+
recipients
|
33
|
+
end
|
34
|
+
|
35
|
+
def set_ignored(participant)
|
36
|
+
ignores.first_or_create!(participant: participant)
|
37
|
+
end
|
38
|
+
|
39
|
+
def set_unignored(participant)
|
40
|
+
ignores.where(participant: participant).destroy_all
|
41
|
+
end
|
42
|
+
|
43
|
+
def is_ignored?(participant)
|
44
|
+
if ignores.empty?
|
45
|
+
false
|
46
|
+
else
|
47
|
+
!ignores.find_all_by_participant(participant).empty?
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def mark(mark, participant)
|
52
|
+
messages.mark(mark, participant)
|
53
|
+
end
|
54
|
+
|
55
|
+
def includes_drafts_by?(participant)
|
56
|
+
!messages.drafts_by(participant).empty?
|
57
|
+
end
|
58
|
+
|
59
|
+
def method_missing(method_sym, *arguments, &block)
|
60
|
+
# the first argument is a Symbol, so you need to_s it if you want to pattern match
|
61
|
+
if method_sym.to_s =~ /^includes_(.*_(by|to|for))\?$/
|
62
|
+
!receipts.send($1.to_sym, arguments.first).empty?
|
63
|
+
else
|
64
|
+
super
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.method_missing(method_sym, *arguments, &block)
|
69
|
+
# the first argument is a Symbol, so you need to_s it if you want to pattern match
|
70
|
+
if method_sym.to_s =~ /^with_((.*)_(by|to|for))$/
|
71
|
+
self.filter_by_receipt_method($1.to_sym, arguments.first)
|
72
|
+
else
|
73
|
+
super
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# It's important to know Object defines respond_to to take two parameters: the method to check, and whether to include private methods
|
78
|
+
# http://www.ruby-doc.org/core/classes/Object.html#M000333
|
79
|
+
def respond_to?(method_sym, include_private = false)
|
80
|
+
if method_sym.to_s =~ /^includes_((.*)_(by|to|for))\?$/
|
81
|
+
true
|
82
|
+
else
|
83
|
+
super
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.respond_to?(method_sym, include_private = false)
|
88
|
+
if method_sym.to_s =~ /^(.*)_(by|to|for)$/
|
89
|
+
true
|
90
|
+
else
|
91
|
+
super
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
def self.ignored_ids_for(participant)
|
98
|
+
MessageTrain::Ignore.find_all_by_participant(participant).conversation_ids
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module MessageTrain
|
2
|
+
class Ignore < ActiveRecord::Base
|
3
|
+
belongs_to :participant, polymorphic: true
|
4
|
+
belongs_to :conversation
|
5
|
+
|
6
|
+
validates_presence_of :conversation, :participant
|
7
|
+
|
8
|
+
scope :find_all_by_participant, ->(participant) { where('participant_type = ? AND participant_id = ?', participant.class.name, participant.id) }
|
9
|
+
|
10
|
+
def self.conversation_ids
|
11
|
+
all.collect { |y| y.conversation_id }
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.conversations
|
15
|
+
MessageTrain::Conversation.where('id IN (?)', conversation_ids )
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|