message_train 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (214) hide show
  1. checksums.yaml +7 -0
  2. data/.document +5 -0
  3. data/.rspec +1 -0
  4. data/.ruby-gemset +1 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +7 -0
  7. data/Gemfile +51 -0
  8. data/LICENSE.txt +20 -0
  9. data/README.rdoc +83 -0
  10. data/Rakefile +71 -0
  11. data/VERSION +1 -0
  12. data/app/assets/javascripts/message_train.js +138 -0
  13. data/app/assets/stylesheets/message_train.scss +107 -0
  14. data/app/controllers/message_train/application_controller.rb +64 -0
  15. data/app/controllers/message_train/boxes_controller.rb +37 -0
  16. data/app/controllers/message_train/conversations_controller.rb +38 -0
  17. data/app/controllers/message_train/messages_controller.rb +74 -0
  18. data/app/controllers/message_train/participants_controller.rb +36 -0
  19. data/app/helpers/message_train/application_helper.rb +4 -0
  20. data/app/helpers/message_train/boxes_helper.rb +30 -0
  21. data/app/helpers/message_train/conversations_helper.rb +73 -0
  22. data/app/helpers/message_train/messages_helper.rb +58 -0
  23. data/app/models/message_train/attachment.rb +17 -0
  24. data/app/models/message_train/box.rb +268 -0
  25. data/app/models/message_train/conversation.rb +101 -0
  26. data/app/models/message_train/ignore.rb +18 -0
  27. data/app/models/message_train/message.rb +146 -0
  28. data/app/models/message_train/receipt.rb +70 -0
  29. data/app/views/application/404.html.haml +3 -0
  30. data/app/views/message_train/application/results.json.jbuilder +8 -0
  31. data/app/views/message_train/boxes/_dropdown_list.html.haml +5 -0
  32. data/app/views/message_train/boxes/_list_item.html.haml +5 -0
  33. data/app/views/message_train/boxes/_widget.html.haml +6 -0
  34. data/app/views/message_train/boxes/show.html.haml +47 -0
  35. data/app/views/message_train/conversations/_conversation.html.haml +9 -0
  36. data/app/views/message_train/conversations/_deleted_toggle.html.haml +1 -0
  37. data/app/views/message_train/conversations/_ignored_toggle.html.haml +4 -0
  38. data/app/views/message_train/conversations/_read_toggle.html.haml +4 -0
  39. data/app/views/message_train/conversations/_toggle.html.haml +1 -0
  40. data/app/views/message_train/conversations/_trashed_toggle.html.haml +4 -0
  41. data/app/views/message_train/conversations/show.html.haml +11 -0
  42. data/app/views/message_train/conversations/show.json.jbuilder +2 -0
  43. data/app/views/message_train/messages/_deleted_toggle.html.haml +1 -0
  44. data/app/views/message_train/messages/_form.html.haml +8 -0
  45. data/app/views/message_train/messages/_message.html.haml +35 -0
  46. data/app/views/message_train/messages/_read_toggle.html.haml +4 -0
  47. data/app/views/message_train/messages/_toggle.html.haml +1 -0
  48. data/app/views/message_train/messages/_trashed_toggle.html.haml +4 -0
  49. data/app/views/message_train/messages/edit.html.haml +2 -0
  50. data/app/views/message_train/messages/new.html.haml +2 -0
  51. data/app/views/message_train/messages/show.json.jbuilder +2 -0
  52. data/app/views/message_train/participants/_field.html.haml +4 -0
  53. data/app/views/message_train/participants/_participant.json.jbuilder +5 -0
  54. data/app/views/message_train/participants/index.json.jbuilder +3 -0
  55. data/app/views/message_train/participants/show.json.jbuilder +1 -0
  56. data/bin/rails +12 -0
  57. data/config/initializers/bootstrap_pager_config.rb +10 -0
  58. data/config/initializers/date_time.rb +5 -0
  59. data/config/locales/en.yml +50 -0
  60. data/config/routes.rb +11 -0
  61. data/db/migrate/20150721145319_create_message_train_conversations.rb +9 -0
  62. data/db/migrate/20150721160322_create_message_train_messages.rb +14 -0
  63. data/db/migrate/20150721161144_create_message_train_attachments.rb +10 -0
  64. data/db/migrate/20150721161940_create_message_train_receipts.rb +16 -0
  65. data/db/migrate/20150721163838_create_message_train_ignores.rb +11 -0
  66. data/lib/generators/message_train/install/install_generator.rb +30 -0
  67. data/lib/generators/message_train/install/templates/initializer.rb +10 -0
  68. data/lib/generators/message_train/utils.rb +16 -0
  69. data/lib/message_train/configuration.rb +35 -0
  70. data/lib/message_train/engine.rb +19 -0
  71. data/lib/message_train/localization.rb +31 -0
  72. data/lib/message_train/mixin.rb +83 -0
  73. data/lib/message_train/version.rb +7 -0
  74. data/lib/message_train.rb +20 -0
  75. data/lib/tasks/message_train_tasks.rake +0 -0
  76. data/message_train.gemspec +325 -0
  77. data/night_train.gemspec +274 -0
  78. data/spec/controllers/message_train/boxes_controller_spec.rb +82 -0
  79. data/spec/controllers/message_train/conversations_controller_spec.rb +86 -0
  80. data/spec/controllers/message_train/messages_controller_spec.rb +210 -0
  81. data/spec/controllers/message_train/participants_controller_spec.rb +35 -0
  82. data/spec/dummy/.rspec +2 -0
  83. data/spec/dummy/Rakefile +6 -0
  84. data/spec/dummy/app/assets/files/message_train/attachments/1917-Boys_Race_Above-Wiki.jpg +0 -0
  85. data/spec/dummy/app/assets/files/message_train/attachments/ACD_Silverbarn's_Mayumi.jpg +0 -0
  86. data/spec/dummy/app/assets/files/message_train/attachments/ACD_obedience.jpg +0 -0
  87. data/spec/dummy/app/assets/files/message_train/attachments/A_Man_and_His_Dog.jpg +0 -0
  88. data/spec/dummy/app/assets/files/message_train/attachments/Alaskan_Malamute_agility_a-frame.jpg +0 -0
  89. data/spec/dummy/app/assets/files/message_train/attachments/American_water_spaniel_02.jpg +0 -0
  90. data/spec/dummy/app/assets/files/message_train/attachments/Aport_konkurs_rybnik.jpg +0 -0
  91. data/spec/dummy/app/assets/files/message_train/attachments/Askerhytta.jpg +0 -0
  92. data/spec/dummy/app/assets/files/message_train/attachments/Bambisj.jpg +0 -0
  93. data/spec/dummy/app/assets/files/message_train/attachments/Cattle_dog_skating.jpg +0 -0
  94. data/spec/dummy/app/assets/files/message_train/attachments/Dog's_Olympics_training_ground_-_geograph.org.uk_-_660324.jpg +0 -0
  95. data/spec/dummy/app/assets/files/message_train/attachments/DogShowHierarchy.png +0 -0
  96. data/spec/dummy/app/assets/files/message_train/attachments/Dog_weight_pull.jpg +0 -0
  97. data/spec/dummy/app/assets/files/message_train/attachments/Draghuntpostcard.jpg +0 -0
  98. data/spec/dummy/app/assets/files/message_train/attachments/Esham_51.JPG +0 -0
  99. data/spec/dummy/app/assets/files/message_train/attachments/FastCourseExample.jpg +0 -0
  100. data/spec/dummy/app/assets/files/message_train/attachments/Fotos_joel_141.JPG +0 -0
  101. data/spec/dummy/app/assets/files/message_train/attachments/Galgo_Spanish_male_brindle.jpg +0 -0
  102. data/spec/dummy/app/assets/files/message_train/attachments/Golden-retriever-carlos-bei-der-dummyarbeit.jpg +0 -0
  103. data/spec/dummy/app/assets/files/message_train/attachments/Habavk.jpg +0 -0
  104. data/spec/dummy/app/assets/files/message_train/attachments/Haie_rci.svg +1714 -0
  105. data/spec/dummy/app/assets/files/message_train/attachments/Hanging_18.jpg +0 -0
  106. data/spec/dummy/app/assets/files/message_train/attachments/Hundewa.jpg +0 -0
  107. data/spec/dummy/app/assets/files/message_train/attachments/K9ProSports_AttackOnHandler.jpg +0 -0
  108. data/spec/dummy/app/assets/files/message_train/attachments/K9ProSports_heel.jpg +0 -0
  109. data/spec/dummy/app/assets/files/message_train/attachments/K9Pro_Civil_agitation.JPG +0 -0
  110. data/spec/dummy/app/assets/files/message_train/attachments/TrainingSchutzhundRetrieveOverWall.jpg +0 -0
  111. data/spec/dummy/app/assets/files/message_train/attachments/example.pdf +0 -0
  112. data/spec/dummy/app/assets/files/message_train/attachments/letterlegal5.doc +0 -0
  113. data/spec/dummy/app/assets/files/message_train/attachments/pdf-sample.pdf +0 -0
  114. data/spec/dummy/app/assets/files/message_train/attachments/sample.pdf +0 -0
  115. data/spec/dummy/app/assets/files/message_train/attachments/tips.doc +0 -0
  116. data/spec/dummy/app/assets/files/message_train/attachments/wd-spectools-word-sample-04.doc +0 -0
  117. data/spec/dummy/app/assets/images/logo.svg +140 -0
  118. data/spec/dummy/app/assets/javascripts/application.js +17 -0
  119. data/spec/dummy/app/assets/stylesheets/application.css.scss +35 -0
  120. data/spec/dummy/app/controllers/application_controller.rb +14 -0
  121. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  122. data/spec/dummy/app/mailers/.keep +0 -0
  123. data/spec/dummy/app/models/.keep +0 -0
  124. data/spec/dummy/app/models/group.rb +19 -0
  125. data/spec/dummy/app/models/role.rb +10 -0
  126. data/spec/dummy/app/models/user.rb +23 -0
  127. data/spec/dummy/app/views/layouts/_top_navigation.html.haml +5 -0
  128. data/spec/dummy/app/views/layouts/application.html.haml +29 -0
  129. data/spec/dummy/bin/bundle +3 -0
  130. data/spec/dummy/bin/rails +4 -0
  131. data/spec/dummy/bin/rake +4 -0
  132. data/spec/dummy/bin/setup +29 -0
  133. data/spec/dummy/config/application.rb +40 -0
  134. data/spec/dummy/config/boot.rb +5 -0
  135. data/spec/dummy/config/database.yml +25 -0
  136. data/spec/dummy/config/environment.rb +5 -0
  137. data/spec/dummy/config/environments/development.rb +41 -0
  138. data/spec/dummy/config/environments/production.rb +79 -0
  139. data/spec/dummy/config/environments/test.rb +42 -0
  140. data/spec/dummy/config/initializers/assets.rb +11 -0
  141. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  142. data/spec/dummy/config/initializers/bootstrap_leather.rb +7 -0
  143. data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
  144. data/spec/dummy/config/initializers/devise.rb +265 -0
  145. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  146. data/spec/dummy/config/initializers/inflections.rb +16 -0
  147. data/spec/dummy/config/initializers/message_train.rb +10 -0
  148. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  149. data/spec/dummy/config/initializers/paperclip.rb +2 -0
  150. data/spec/dummy/config/initializers/rolify.rb +7 -0
  151. data/spec/dummy/config/initializers/session_store.rb +3 -0
  152. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  153. data/spec/dummy/config/locales/devise.en.yml +60 -0
  154. data/spec/dummy/config/locales/en.yml +23 -0
  155. data/spec/dummy/config/routes.rb +5 -0
  156. data/spec/dummy/config/secrets.yml +22 -0
  157. data/spec/dummy/config/unused/temporary_i18n_debugging.rb +12 -0
  158. data/spec/dummy/config.ru +4 -0
  159. data/spec/dummy/db/migrate/20150721140013_devise_create_users.rb +42 -0
  160. data/spec/dummy/db/migrate/20150721141009_rolify_create_roles.rb +19 -0
  161. data/spec/dummy/db/migrate/20150721141128_create_groups.rb +11 -0
  162. data/spec/dummy/db/migrate/20150721150307_add_display_name_column_to_users.rb +6 -0
  163. data/spec/dummy/db/migrate/20150724142846_create_message_train_conversations.night_train.rb +10 -0
  164. data/spec/dummy/db/migrate/20150724142847_create_message_train_messages.night_train.rb +15 -0
  165. data/spec/dummy/db/migrate/20150724142848_create_message_train_attachments.night_train.rb +11 -0
  166. data/spec/dummy/db/migrate/20150724142849_create_message_train_receipts.night_train.rb +17 -0
  167. data/spec/dummy/db/migrate/20150724142850_create_message_train_ignores.night_train.rb +12 -0
  168. data/spec/dummy/db/schema.rb +121 -0
  169. data/spec/dummy/db/seeds/conversations.seeds.rb +86 -0
  170. data/spec/dummy/db/seeds/development/conversations.seeds.rb +9 -0
  171. data/spec/dummy/db/seeds/groups.seeds.rb +9 -0
  172. data/spec/dummy/db/seeds/test/conversations.seeds.rb +9 -0
  173. data/spec/dummy/db/seeds/users.seeds.rb +15 -0
  174. data/spec/dummy/db/seeds.rb +0 -0
  175. data/spec/dummy/db/test.sqlite3 +0 -0
  176. data/spec/dummy/lib/assets/.keep +0 -0
  177. data/spec/dummy/log/.keep +0 -0
  178. data/spec/dummy/public/404.html +67 -0
  179. data/spec/dummy/public/422.html +67 -0
  180. data/spec/dummy/public/500.html +66 -0
  181. data/spec/dummy/public/capybara.html +193 -0
  182. data/spec/dummy/public/favicon.ico +0 -0
  183. data/spec/factories/attachment.rb +6 -0
  184. data/spec/factories/group.rb +20 -0
  185. data/spec/factories/message.rb +68 -0
  186. data/spec/factories/user.rb +8 -0
  187. data/spec/features/boxes_spec.rb +50 -0
  188. data/spec/features/conversations_spec.rb +36 -0
  189. data/spec/features/messages_spec.rb +53 -0
  190. data/spec/helpers/message_train/boxes_helper_spec.rb +44 -0
  191. data/spec/message_train_spec.rb +7 -0
  192. data/spec/models/group_spec.rb +23 -0
  193. data/spec/models/message_train/attachment_spec.rb +12 -0
  194. data/spec/models/message_train/box_spec.rb +195 -0
  195. data/spec/models/message_train/conversation_spec.rb +98 -0
  196. data/spec/models/message_train/ignore_spec.rb +27 -0
  197. data/spec/models/message_train/message_spec.rb +83 -0
  198. data/spec/models/message_train/receipt_spec.rb +73 -0
  199. data/spec/models/user_spec.rb +68 -0
  200. data/spec/rails_helper.rb +83 -0
  201. data/spec/spec_helper.rb +92 -0
  202. data/spec/support/controller_behaviors.rb +88 -0
  203. data/spec/support/controller_macros.rb +13 -0
  204. data/spec/support/conversations.rb +10 -0
  205. data/spec/support/factory_girl.rb +3 -0
  206. data/spec/support/feature_behaviors.rb +62 -0
  207. data/spec/support/groups.rb +3 -0
  208. data/spec/support/loaded_site.rb +5 -0
  209. data/spec/support/shared_connection.rb +13 -0
  210. data/spec/support/users.rb +6 -0
  211. data/spec/support/utilities.rb +8 -0
  212. data/vendor/assets/javascripts/bootstrap-tags.js +623 -0
  213. data/vendor/assets/stylesheets/bootstrap-tags.css +78 -0
  214. metadata +608 -0
@@ -0,0 +1,146 @@
1
+ module MessageTrain
2
+ class Message < ActiveRecord::Base
3
+ # Serializations
4
+ serialize :recipients_to_save, Hash
5
+
6
+ # Relationships
7
+ belongs_to :conversation
8
+ belongs_to :sender, polymorphic: true
9
+ has_many :attachments
10
+ has_many :receipts
11
+
12
+ # Validations
13
+ validates_presence_of :sender, :subject
14
+
15
+ # Callbacks
16
+ before_create :create_conversation_if_blank
17
+ after_create :generate_sender_receipt
18
+ after_save :generate_receipts_or_set_draft
19
+ after_save :set_conversation_subject_if_alone
20
+
21
+ # Scopes
22
+ default_scope { order('updated_at DESC') }
23
+ scope :filter_by_receipt_method_ids, ->(receipt_method, participant) {
24
+ all.collect { |x| x.receipts.send(receipt_method, participant).message_ids }.flatten
25
+ }
26
+ scope :filter_by_receipt_method, ->(receipt_method, participant) {
27
+ where('id IN (?)', filter_by_receipt_method_ids(receipt_method, participant))
28
+ }
29
+ scope :filter_out_by_receipt_method, ->(receipt_method, participant) {
30
+ where('NOT(id IN (?))', filter_by_receipt_method_ids(receipt_method, participant))
31
+ }
32
+ scope :ready, -> { where('draft = ?', false) }
33
+ scope :drafts, -> { where('draft = ?', true) }
34
+ scope :by, ->(participant) { where('sender_type = ? AND sender_id = ?', participant.class.name, participant.id) }
35
+ scope :drafts_by, ->(participant) { drafts.by(participant) }
36
+
37
+ def mark(mark_to_set, participant)
38
+ receipt_to_mark = receipts.for(participant).first
39
+ unless receipt_to_mark.nil?
40
+ receipt_to_mark.mark(mark_to_set)
41
+ end
42
+ end
43
+
44
+ def self.mark(mark_to_set, participant)
45
+ all.each do |message|
46
+ message.mark(mark_to_set, participant)
47
+ end
48
+ end
49
+
50
+ def recipients
51
+ receipts.recipient_receipt.collect { |x| x.recipient }
52
+ end
53
+
54
+ def self.conversation_ids
55
+ all.collect { |y| y.conversation_id }
56
+ end
57
+
58
+ def self.conversations
59
+ MessageTrain::Conversation.where('id IN (?)', conversation_ids )
60
+ end
61
+
62
+ def method_missing(method_sym, *arguments, &block)
63
+ # the first argument is a Symbol, so you need to_s it if you want to pattern match
64
+ if method_sym.to_s =~ /^is_((.*)_(by|to|for))\?$/
65
+ !receipts.send($1.to_sym, arguments.first).empty?
66
+ elsif method_sym.to_s =~ /^mark_(.*)_for$/
67
+ receipts.for(arguments.first).first.mark($1.to_sym)
68
+ else
69
+ super
70
+ end
71
+ end
72
+
73
+ def self.method_missing(method_sym, *arguments, &block)
74
+ # the first argument is a Symbol, so you need to_s it if you want to pattern match
75
+ if method_sym.to_s =~ /^with_(.*_(by|to|for))$/
76
+ filter_by_receipt_method($1.to_sym, arguments.first)
77
+ else
78
+ super
79
+ end
80
+ end
81
+
82
+ # It's important to know Object defines respond_to to take two parameters: the method to check, and whether to include private methods
83
+ # http://www.ruby-doc.org/core/classes/Object.html#M000333
84
+ def respond_to?(method_sym, include_private = false)
85
+ if method_sym.to_s =~ /^is_.*_(by|to|for)\?$/ || method_sym.to_s =~ /^mark_.*_for\?$/
86
+ true
87
+ else
88
+ super
89
+ end
90
+ end
91
+
92
+ def self.respond_to?(method_sym, include_private = false)
93
+ if method_sym.to_s =~ /^.*_(by|to|for)$/
94
+ true
95
+ else
96
+ super
97
+ end
98
+ end
99
+
100
+ private
101
+ def create_conversation_if_blank
102
+ if conversation.nil?
103
+ self.conversation = Conversation.create(subject: subject)
104
+ end
105
+ end
106
+
107
+ def generate_sender_receipt
108
+ receipts.first_or_create!(recipient_type: sender.class.name, recipient_id: sender.id, sender: true)
109
+ end
110
+
111
+ def generate_receipts_or_set_draft
112
+ unless draft
113
+ recipients_to_save.each do |table, slugs|
114
+ model_name = table.classify
115
+ model = model_name.constantize
116
+
117
+ slugs.split(',').each do |slug|
118
+ slug = slug.strip
119
+ slug_column = MessageTrain.configuration.slug_columns[table.to_sym] || :slug
120
+ if model.exists?(slug_column => slug)
121
+ recipient = model.find_by(slug_column => slug)
122
+ unless conversation.is_ignored?(recipient)
123
+ receipts.create!(recipient_type: model_name, recipient_id: recipient.id)
124
+ end
125
+ else
126
+ errors.add :recipients_to_save, :name_not_found.l(name: slug)
127
+ end
128
+ end
129
+ end
130
+ reload
131
+ if recipients.empty?
132
+ update_attribute :draft, true
133
+ else
134
+ conversation.update_attribute(:updated_at, Time.now)
135
+ end
136
+ end
137
+ end
138
+
139
+ def set_conversation_subject_if_alone
140
+ if conversation.messages.count == 1
141
+ conversation.update_attribute(:subject, subject)
142
+ end
143
+ end
144
+
145
+ end
146
+ end
@@ -0,0 +1,70 @@
1
+ module MessageTrain
2
+ class Receipt < ActiveRecord::Base
3
+ belongs_to :recipient, polymorphic: true
4
+ belongs_to :message
5
+ validates_presence_of :recipient, :message
6
+
7
+ default_scope { order("created_at DESC") }
8
+ scope :sender_receipt, -> { where('sender = ?', true) }
9
+ scope :recipient_receipt, -> { where('sender = ?', false) }
10
+ scope :by, ->(sender) { sender_receipt.for(sender) }
11
+ scope :for, ->(recipient) { where('recipient_type = ? AND recipient_id = ?', recipient.class.name, recipient.id) }
12
+ scope :to, ->(recipient) { recipient_receipt.for(recipient) }
13
+ scope :trashed, ->(setting = true) { where('marked_trash = ?', setting) }
14
+ scope :read, ->(setting = true) { where('marked_read = ?', setting) }
15
+ scope :deleted, ->(setting = true) { where('marked_deleted = ?', setting) }
16
+
17
+ def mark(mark_to_set)
18
+ if mark_to_set.to_s =~ /^un/
19
+ setting = false
20
+ suffix = mark_to_set.to_s.gsub(/^un/, '')
21
+ else
22
+ setting = true
23
+ suffix = mark_to_set.to_s
24
+ end
25
+ column = "marked_#{suffix}".to_sym
26
+ update_attribute(column, setting)
27
+ end
28
+
29
+ def self.message_ids
30
+ all.collect { |y| y.message_id }
31
+ end
32
+
33
+ def self.messages
34
+ MessageTrain::Message.where('id IN (?)', message_ids )
35
+ end
36
+
37
+ def self.conversation_ids
38
+ all.collect { |y| y.message.conversation_id }
39
+ end
40
+
41
+ def self.conversations
42
+ MessageTrain::Conversation.where('id IN (?)', conversation_ids )
43
+ end
44
+
45
+ def self.method_missing(method_sym, *arguments, &block)
46
+ # the first argument is a Symbol, so you need to_s it if you want to pattern match
47
+ if method_sym.to_s =~ /^receipts_(by|to|for)$/
48
+ send($1.to_sym, arguments.first)
49
+ elsif method_sym.to_s =~ /^(.*)_(by|to|for)$/
50
+ send($1.to_sym).send($2.to_sym, arguments.first)
51
+ elsif method_sym.to_s =~ /^mark_(.*)$/
52
+ mark($1.to_sym)
53
+ elsif method_sym.to_s =~ /^un(.*)$/
54
+ send($1.to_sym, false)
55
+ else
56
+ super
57
+ end
58
+ end
59
+
60
+ # It's important to know Object defines respond_to to take two parameters: the method to check, and whether to include private methods
61
+ # http://www.ruby-doc.org/core/classes/Object.html#M000333
62
+ def self.respond_to?(method_sym, include_private = false)
63
+ if method_sym.to_s =~ /^(.*)_(by|to|for)$/ || method_sym.to_s =~ /^(un|mark_)(.*)$/
64
+ true
65
+ else
66
+ super
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,3 @@
1
+ - add_hero_unit do
2
+ %h1
3
+ 404 - Page Not Found
@@ -0,0 +1,8 @@
1
+ json.message @box.message
2
+ unless @box.results.all.empty?
3
+ json.results @box.results.all do |item|
4
+ json.css_id item[:css_id]
5
+ json.path item[:path]
6
+ json.message item[:message]
7
+ end
8
+ end
@@ -0,0 +1,5 @@
1
+ - unless boxes.empty?
2
+ = dropdown_nav_item "#{:box_title_in.l} #{badge boxes.first.unread_count.to_s}".html_safe, '#' do
3
+ - boxes.each do |box|
4
+ = box_nav_item box
5
+ = nav_item :compose.l, message_train.new_box_message_path(:in)
@@ -0,0 +1,5 @@
1
+ - if box.present?
2
+ %li{ html_options }
3
+ = link_to box.title, message_train.box_path(box.division)
4
+ - if box.unread_count > 0
5
+ = badge box.unread_count.to_s
@@ -0,0 +1,6 @@
1
+ - unless boxes.empty?
2
+ %h3= :messages.l
3
+ .text-center= link_to :compose.l, message_train.new_box_message_path(:in), class: 'btn btn-lg btn-primary btn-compose'
4
+ %ul.list-group
5
+ - boxes.each do |box|
6
+ = box_list_item box, class: 'list-group-item'
@@ -0,0 +1,47 @@
1
+ - add_title @box.title
2
+ = form_tag message_train.box_path(@box.division), method: :put, remote: true, id: 'box', data: { type: :json } do
3
+ - unless @conversations.empty?
4
+ #box-actions
5
+ .btn-group.check-all
6
+ %button.btn.btn-default{ type: 'button' }
7
+ = check_box_tag 'check_all', 'all', false
8
+ %button.btn.btn-default.dropdown-toggle{ aria: { expanded: 'false', haspopup: 'true' }, data: { toggle: 'dropdown'}, type: 'button' }
9
+ %span.caret
10
+ %span.sr-only= :toggle_dropdown.l
11
+ %ul.dropdown-menu
12
+ %li
13
+ = link_to :all.l, '#0', class: 'check', data: { selector: '.message_train_conversation'}
14
+ = link_to :none.l, '#0', class: 'check', data: { selector: ''}
15
+ = link_to :read.l, '#0', class: 'check', data: { selector: '.message_train_conversation.read'}
16
+ = link_to :unread.l, '#0', class: 'check', data: { selector: '.message_train_conversation.unread'}
17
+ .btn-group
18
+ %button.btn.btn-info.dropdown-toggle{ aria: { expanded: 'false', haspopup: 'true' }, data: { toggle: 'dropdown'}, type: 'button' }
19
+ = :mark.l
20
+ %span.caret
21
+ %ul.dropdown-menu
22
+ %li
23
+ = link_to :mark_as_name.l(name: :read.l), '#0', class: 'mark', id: 'mark-read', data: { mark: 'read'}
24
+ %li
25
+ = link_to :mark_as_name.l(name: :unread.l), '#0', class: 'mark', id: 'mark-unread', data: { mark: 'unread'}
26
+ - if @box.division == :trash
27
+ %li
28
+ = link_to :mark_as_name.l(name: :untrashed.l), '#0', class: 'mark', id: 'mark-untrash', data: { mark: 'untrash'}
29
+ %li.divider{ role: 'separator' }
30
+ %li
31
+ = link_to :mark_as_name.l(name: :deleted.l), '#0', class: 'mark', id: 'mark-deleted', data: { mark: 'deleted'}
32
+ - else
33
+ %li
34
+ = link_to :mark_as_name.l(name: :trashed.l), '#0', class: 'mark', id: 'mark-trash', data: { mark: 'trash'}
35
+ %li.divider{ role: 'separator' }
36
+ - if @box.division == :ignored
37
+ %li
38
+ = link_to :mark_as_name.l(name: :unignored.l), '#0', class: 'mark', id: 'mark-unignored', data: { mark: 'unignore'}
39
+ - else
40
+ %li
41
+ = link_to :mark_as_name.l(name: :ignored.l), '#0', class: 'mark', id: 'mark-ignored', data: { mark: 'ignore'}
42
+ = link_to :compose.l, message_train.new_box_message_path(@box.division), class: 'btn btn-primary'
43
+ %span#spinner.hide= icon 'refresh spinning'
44
+ = hidden_field_tag :mark_to_set
45
+ %table#message_train_conversations.table.table-condensed
46
+ = render @conversations
47
+ = paginate @conversations
@@ -0,0 +1,9 @@
1
+ %tr.message_train_conversation{ class: conversation_class(@box, conversation), id: 'message_train_conversation_' + conversation.id.to_s }
2
+ %td.col-xs-1= check_box_tag "objects[conversations][#{conversation.id.to_s}]", conversation.id
3
+ %td.col-xs-2= link_to conversation_senders(conversation), message_train.box_conversation_path(@box.division, conversation)
4
+ %td.col-xs-8= link_to conversation.subject, message_train.box_conversation_path(@box.division, conversation)
5
+ %td.col-xs-1.conversation-actions
6
+ = conversation_read_toggle conversation
7
+ = conversation_trashed_toggle conversation
8
+ - if @box.division == :trash
9
+ = conversation_deleted_toggle conversation
@@ -0,0 +1 @@
1
+ = conversation_toggle conversation, 'remove', :deleted, :put, :mark_as_name.l(name: :deleted.l), data: { confirm: :delete_forever_this_cannot_be_undone.l }
@@ -0,0 +1,4 @@
1
+ - if conversation.is_ignored?(@box.parent)
2
+ = conversation_toggle conversation, 'volume-up', :unignore, :delete, :mark_as_name.l(name: :unignored.l)
3
+ - else
4
+ = conversation_toggle conversation, 'volume-off', :ignore, :delete, :mark_as_name.l(name: :ignored.l), data: { confirm: :are_you_sure.l}
@@ -0,0 +1,4 @@
1
+ - if conversation.includes_unread_for?(@box.parent)
2
+ = conversation_toggle conversation, 'eye-open', :read, :put, :mark_as_name.l(name: :read.l)
3
+ - else
4
+ = conversation_toggle conversation, 'eye-close', :unread, :put, :mark_as_name.l(name: :unread.l)
@@ -0,0 +1 @@
1
+ = icon_link_to icon, '', message_train.box_path(@box.division, objects: { 'conversations' => {conversation.id.to_s => conversation.id.to_s}}, mark_to_set: mark_to_set, format: :json), options
@@ -0,0 +1,4 @@
1
+ - if conversation.includes_untrashed_for?(@box.parent)
2
+ = conversation_toggle conversation, 'trash', :trash, :put, :mark_as_name.l(name: :trashed.l), data: { confirm: :are_you_sure.l}
3
+ - else
4
+ = conversation_toggle conversation, 'inbox', :untrash, :put, :mark_as_name.l(name: :untrashed.l)
@@ -0,0 +1,11 @@
1
+ - add_title @conversation.subject
2
+ - add_subtitle :started_at_time.l(time: @conversation.created_at)
3
+
4
+ #box
5
+ #conversation-actions.pull-right
6
+ .btn.btn-default.btn-lg= conversation_ignored_toggle(@conversation)
7
+ #message_train_conversation.clear-both
8
+ - unless @messages.empty?
9
+ #message_train_messages.messages.panel-group{aria: { multiselectable: 'true' }, role: "tablist"}
10
+ = render @messages
11
+ = paginate @messages
@@ -0,0 +1,2 @@
1
+ json.id @conversation.id
2
+ json.html render partial: 'conversation', formats: [:html], locals: { conversation: @conversation }
@@ -0,0 +1 @@
1
+ = message_toggle message, 'remove', :deleted, :mark_as_name.l(name: :deleted.l)
@@ -0,0 +1,8 @@
1
+ - resource = message.new_record? ? message_train.box_messages_path(@box) : message_train.box_message_path(@box, message)
2
+ = bootstrap_form_for message, url: resource do |f|
3
+ - MessageTrain.configuration.recipient_tables.each do |table_sym|
4
+ = render partial: 'message_train/participants/field', locals: { message: message, field_name: table_sym }
5
+ = f.text_field :subject
6
+ = f.text_area :body
7
+ = f.check_box :draft
8
+ = f.submit :send.l
@@ -0,0 +1,35 @@
1
+ - if message.draft
2
+ .panel.panel-default{ class: message_class(@box, message), id: "message_train_message_#{message.id}", data: { mark_path: message_train.box_conversation_path(@box.division, id: message.conversation_id, objects: { 'messages' => {message.id.to_s => message.id.to_s} }) } }
3
+ .panel-heading{role: "tab", id: "message_#{message.id}_heading"}
4
+ .message-actions.pull-right
5
+ = message_read_toggle message
6
+ = message_trashed_toggle message
7
+ - if @box.division == :trash
8
+ = message_deleted_toggle message
9
+ %h4.panel-title
10
+ = link_to "#message_#{message.id}_collapse", aria: { controls: "message_#{message.id}_collapse", expanded: 'true' }, data: { parent: '#accordion', toggle: 'collapse' }, role: "button" do
11
+ %span.caret
12
+ = :message_draft.l
13
+ %small= message.created_at
14
+ .panel-collapse.collapse.in{aria: { labelledby: "message_#{message.id}_heading" }, role: "tabpanel", id: "message_#{message.id}_collapse"}
15
+ .panel-body
16
+ = render partial: 'message_train/messages/form', locals: { message: message }
17
+
18
+ - else
19
+ .panel.panel-default{ class: message_class(@box, message), id: "message_train_message_#{message.id}", data: { mark_path: message_train.box_conversation_path(@box.division, id: message.conversation_id, objects: { 'messages' => {message.id.to_s => message.id.to_s} }) } }
20
+ .panel-heading{role: "tab", id: "message_#{message.id}_heading"}
21
+ .message-actions.pull-right
22
+ = message_read_toggle message
23
+ = message_trashed_toggle message
24
+ - if @box.division == :trash
25
+ = message_deleted_toggle message
26
+ %h4.panel-title
27
+ = link_to "#message_#{message.id}_collapse", aria: { controls: "message_#{message.id}_collapse", expanded: 'true' }, data: { parent: '#accordion', toggle: 'collapse' }, role: "button" do
28
+ %span.caret
29
+ = :from_sender.l(sender: message.sender.display_name)
30
+ %small= message.created_at
31
+ .panel-collapse.collapse.in{aria: { labelledby: "message_#{message.id}_heading" }, role: "tabpanel", id: "message_#{message.id}_collapse"}
32
+ .panel-body
33
+ %p= :to_recipient.l(recipient: message_recipients(message))
34
+ %p.lead= message.subject
35
+ = message.body
@@ -0,0 +1,4 @@
1
+ - if message.is_unread_for?(@box.parent)
2
+ = message_toggle message, 'eye-open', :read, :mark_as_name.l(name: :read.l)
3
+ - else
4
+ = message_toggle message, 'eye-close', :unread, :mark_as_name.l(name: :unread.l)
@@ -0,0 +1 @@
1
+ = icon_link_to icon, '', message_train.box_path(@box.division, objects: { 'messages' => {message.id.to_s => message.id.to_s}}, mark_to_set: mark_to_set, format: :json), options
@@ -0,0 +1,4 @@
1
+ - if message.is_untrashed_for?(@box.parent)
2
+ = message_toggle message, 'trash', :trash, :mark_as_name.l(name: :trashed.l), data: { confirm: :are_you_sure.l}
3
+ - else
4
+ = message_toggle message, 'inbox', :untrash, :mark_as_name.l(name: :untrashed.l)
@@ -0,0 +1,2 @@
1
+ - add_title 'Edit Draft'
2
+ = render partial: 'form', locals: { message: @message }
@@ -0,0 +1,2 @@
1
+ - add_title 'New Message'
2
+ = render partial: 'form', locals: { message: @message }
@@ -0,0 +1,2 @@
1
+ json.id @message.id
2
+ json.html render partial: 'message', formats: [:html], locals: { message: @message }
@@ -0,0 +1,4 @@
1
+ .form-group
2
+ %label.control-label= "#{field_name.to_s.singularize.humanize.titleize} Recipients"
3
+ .recipient-input.tag-list{ data: { field_name: "message[recipients_to_save][#{field_name}]" }, id: "message_recipients_to_save_#{field_name}" }
4
+ .tag-data= message.recipients_to_save[field_name]
@@ -0,0 +1,5 @@
1
+ json.id participant.id
2
+ json.model_name participant.class.name
3
+ json.slug box_participant_slug(participant)
4
+ json.name box_participant_name(participant)
5
+ json.path message_train.box_participant_path(@box, participant.id)
@@ -0,0 +1,3 @@
1
+ json.participants @participants do |participant|
2
+ json.partial! partial: 'participant', locals: { participant: participant }
3
+ end
@@ -0,0 +1 @@
1
+ json.participant render partial: 'participant', locals: { participant: @participant }
data/bin/rails ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+ # This command will automatically be run when you run "rails" with Rails 4 gems installed from the root of your application.
3
+
4
+ ENGINE_ROOT = File.expand_path('../..', __FILE__)
5
+ ENGINE_PATH = File.expand_path('../../lib/message_train/engine', __FILE__)
6
+
7
+ # Set up gems listed in the Gemfile.
8
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
9
+ require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
10
+
11
+ require 'rails/all'
12
+ require 'rails/engine/commands'
@@ -0,0 +1,10 @@
1
+ BootstrapPager.configure do |config|
2
+ config.default_per_page = 10
3
+ # config.max_per_page = nil
4
+ # config.window = 4
5
+ # config.outer_window = 0
6
+ # config.left = 0
7
+ # config.right = 0
8
+ # config.page_method_name = :page
9
+ # config.param_name = :page
10
+ end
@@ -0,0 +1,5 @@
1
+ # Date
2
+ Date::DATE_FORMATS[:default] = "%m/%d/%Y"
3
+
4
+ # Time
5
+ Time::DATE_FORMATS[:default] = "%m/%d/%Y %I:%M %p"
@@ -0,0 +1,50 @@
1
+ en:
2
+ activerecord:
3
+ attributes:
4
+ 'message_train/message':
5
+ recipients_to_save: 'Recipient'
6
+ access_to_conversation_id_denied: "Access to Conversation %{id} denied"
7
+ access_to_message_id_denied: "Access to Message %{id} denied"
8
+ access_to_receipt_id_denied: "Access to Receipt %{id} denied"
9
+ all: 'All'
10
+ are_you_sure: 'Are you sure?'
11
+ box_title_in: 'Inbox'
12
+ box_title_sent: 'Sent Messages'
13
+ box_title_all: 'All Messages'
14
+ box_title_drafts: 'Drafts'
15
+ box_title_trash: 'Trash'
16
+ box_title_ignored: 'Ignored Conversations'
17
+ cannot_mark_type: "Cannot mark %{type}"
18
+ cannot_ignore_type: "Cannot ignore %{type}"
19
+ check_all: 'Check All'
20
+ class_id_not_found_in_box: "%{class} %{id} not found in box"
21
+ conversation_id_not_found: "Conversation %{id} not found"
22
+ compose: 'Compose'
23
+ delete_forever_this_cannot_be_undone: 'Delete forever? This cannot be undone.'
24
+ deleted: 'Deleted'
25
+ from_sender: "From: %{sender}"
26
+ ignored: 'Ignored'
27
+ mark: 'Mark'
28
+ mark_as_name: "Mark as %{name}" #e.g. Read, Unread etc.
29
+ message_draft: 'Message Draft'
30
+ message_saved_as_draft: 'Message saved as draft.'
31
+ message_sent: 'Message sent.'
32
+ messages: 'Messages'
33
+ name_not_found: "%{name} not found"
34
+ none: 'None'
35
+ nothing_to_do: 'Nothing to do'
36
+ read: 'Read'
37
+ recipient_not_found: "Recipient Not Found"
38
+ recipients: 'Recipients'
39
+ send: 'Send'
40
+ started_at_time: "Started at %{time}"
41
+ table_recipients: "%{table} Recipients"
42
+ to_recipient: 'To: %{recipient}'
43
+ toggle_dropdown: 'Toggle Dropdown'
44
+ trashed: 'Trashed'
45
+ unignored: 'Unignored'
46
+ unread: 'Unread'
47
+ untrashed: 'Untrashed'
48
+ update_successful: 'Update successful'
49
+ wrong_number_of_arguments_for_box_expected_right_got_wrong: "Wrong number of arguments for box (expected %{right}, got %{wrong})"
50
+ you_must_sign_in_or_sign_up_to_continue: 'You must sign in or sign up to continue.'
data/config/routes.rb ADDED
@@ -0,0 +1,11 @@
1
+ MessageTrain::Engine.routes.draw do
2
+ authenticated MessageTrain.configuration.user_route_authentication_method do
3
+ resources :boxes, path: 'box', param: :division, only: [:show, :update, :destroy] do
4
+ resources :conversations, only: [:show, :update, :destroy]
5
+ resources :messages, except: [:index, :destroy]
6
+ resources :participants, only: [:index, :show]
7
+ end
8
+ end
9
+
10
+ match '/box(/*path)', to: redirect(MessageTrain.configuration.user_sign_in_path), via: [:get, :put, :delete]
11
+ end
@@ -0,0 +1,9 @@
1
+ class CreateMessageTrainConversations < ActiveRecord::Migration
2
+ def change
3
+ create_table :message_train_conversations do |t|
4
+ t.string :subject
5
+
6
+ t.timestamps null: false
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,14 @@
1
+ class CreateMessageTrainMessages < ActiveRecord::Migration
2
+ def change
3
+ create_table :message_train_messages do |t|
4
+ t.references :conversation, index: true, foreign_key: true
5
+ t.references :sender, polymorphic: true, index: true
6
+ t.text :recipients_to_save
7
+ t.string :subject
8
+ t.text :body
9
+ t.boolean :draft, default: false
10
+
11
+ t.timestamps null: false
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,10 @@
1
+ class CreateMessageTrainAttachments < ActiveRecord::Migration
2
+ def change
3
+ create_table :message_train_attachments do |t|
4
+ t.references :message, index: true, foreign_key: true
5
+ t.attachment :attachment
6
+
7
+ t.timestamps null: false
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,16 @@
1
+ class CreateMessageTrainReceipts < ActiveRecord::Migration
2
+ def change
3
+ create_table :message_train_receipts do |t|
4
+ t.references :recipient, polymorphic: true
5
+ t.references :message, index: true, foreign_key: true
6
+ t.boolean :marked_read, default: false
7
+ t.boolean :marked_trash, default: false
8
+ t.boolean :marked_deleted, default: false
9
+ t.boolean :sender, default: false
10
+
11
+ t.timestamps null: false
12
+ end
13
+
14
+ add_index :message_train_receipts, [:recipient_type, :recipient_id], name: :index_message_train_receipts_on_recipient
15
+ end
16
+ end
@@ -0,0 +1,11 @@
1
+ class CreateMessageTrainIgnores < ActiveRecord::Migration
2
+ def change
3
+ create_table :message_train_ignores do |t|
4
+ t.references :participant, polymorphic: true
5
+ t.references :conversation, index: true, foreign_key: true
6
+
7
+ t.timestamps null: false
8
+ end
9
+ add_index :message_train_ignores, [:participant_type, :participant_id], name: 'participant_index'
10
+ end
11
+ end
@@ -0,0 +1,30 @@
1
+ module MessageTrain
2
+ class InstallGenerator < Rails::Generators::Base
3
+ source_root File.expand_path("../templates", __FILE__)
4
+ require File.expand_path('../../utils', __FILE__)
5
+ include Generators::Utils
6
+ include Rails::Generators::Migration
7
+
8
+ def hello
9
+ output "MessageTrain Installer will now install itself", :magenta
10
+ end
11
+
12
+ # all public methods in here will be run in order
13
+
14
+ def add_initializer
15
+ output "First, you'll need an initializer. This is where you put your configuration options.", :magenta
16
+ template "initializer.rb", "config/initializers/message_train.rb"
17
+ end
18
+
19
+ def add_migrations
20
+ output "Next come migrations.", :magenta
21
+ rake 'message_train:install:migrations'
22
+ end
23
+
24
+ def add_route
25
+ output "Adding MessageTrain to your routes.rb file", :magenta
26
+ gsub_file "config/routes.rb", /authenticated :[a-z_0-9]+ do\s+mount MessageTrain::Engine => '\/.*', :as => 'message_train'\s+end/, ''
27
+ route("authenticated :user do\n\t\tmount MessageTrain::Engine => '/', :as => 'message_train'\n\tend")
28
+ end
29
+ end
30
+ end