rails_notice 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +165 -0
  3. data/README.md +76 -0
  4. data/Rakefile +37 -0
  5. data/app/assets/config/rails_notice_manifest.js +3 -0
  6. data/app/assets/javascripts/controllers/notifications/index.js +0 -0
  7. data/app/assets/javascripts/controllers/rails_notice_admin/notification_settings/index.js +17 -0
  8. data/app/assets/javascripts/controllers/rails_notice_admin/notifications/index.js +15 -0
  9. data/app/assets/javascripts/controllers/rails_notice_admin/notify_settings/edit.js +3 -0
  10. data/app/assets/javascripts/controllers/rails_notice_admin/notify_settings/index.js +17 -0
  11. data/app/assets/javascripts/controllers/rails_notice_admin/notify_settings/new.js +18 -0
  12. data/app/assets/javascripts/rails_notice/cable.js +19 -0
  13. data/app/assets/javascripts/rails_notice/channels/notices.js +39 -0
  14. data/app/assets/javascripts/rails_notice/messenger-theme-future.js +33 -0
  15. data/app/assets/javascripts/rails_notice/messenger.min.js +2 -0
  16. data/app/assets/stylesheets/the_notify/cable.css +9 -0
  17. data/app/assets/stylesheets/the_notify/messenger-theme-air.css +357 -0
  18. data/app/assets/stylesheets/the_notify/messenger.css +105 -0
  19. data/app/channels/notices_channel.rb +7 -0
  20. data/app/channels/the_notify_connection.rb +30 -0
  21. data/app/controllers/concerns/rails_notice_controller.rb +18 -0
  22. data/app/controllers/rails_notice_admin/base_controller.rb +6 -0
  23. data/app/controllers/rails_notice_admin/notification_settings_controller.rb +55 -0
  24. data/app/controllers/rails_notice_admin/notifications_controller.rb +71 -0
  25. data/app/controllers/rails_notice_my/base_controller.rb +5 -0
  26. data/app/controllers/rails_notice_my/notification_settings_controller.rb +28 -0
  27. data/app/controllers/rails_notice_my/notifications_controller.rb +78 -0
  28. data/app/controllers/receivers_controller.rb +12 -0
  29. data/app/jobs/notification_job.rb +17 -0
  30. data/app/mailers/rails_notice_mailer.rb +21 -0
  31. data/app/models/annunciation.rb +4 -0
  32. data/app/models/concerns/i18n_helper.rb +14 -0
  33. data/app/models/concerns/the_notifiable.rb +33 -0
  34. data/app/models/concerns/the_publishable.rb +9 -0
  35. data/app/models/concerns/the_receivable.rb +17 -0
  36. data/app/models/notification.rb +156 -0
  37. data/app/models/notification_setting.rb +8 -0
  38. data/app/views/rails_notice/_link.html.erb +6 -0
  39. data/app/views/rails_notice_admin/base/_nav.html.erb +16 -0
  40. data/app/views/rails_notice_admin/notification_settings/_form.html.erb +7 -0
  41. data/app/views/rails_notice_admin/notification_settings/_search_form.html.erb +11 -0
  42. data/app/views/rails_notice_admin/notification_settings/edit.html.erb +14 -0
  43. data/app/views/rails_notice_admin/notification_settings/index.html.erb +52 -0
  44. data/app/views/rails_notice_admin/notification_settings/new.html.erb +13 -0
  45. data/app/views/rails_notice_admin/notification_settings/show.html.erb +0 -0
  46. data/app/views/rails_notice_admin/notifications/_form.html.erb +6 -0
  47. data/app/views/rails_notice_admin/notifications/_menu.html.erb +6 -0
  48. data/app/views/rails_notice_admin/notifications/_search_form.html.erb +11 -0
  49. data/app/views/rails_notice_admin/notifications/edit.html.erb +14 -0
  50. data/app/views/rails_notice_admin/notifications/index.html.erb +44 -0
  51. data/app/views/rails_notice_admin/notifications/new.html.erb +15 -0
  52. data/app/views/rails_notice_mailer/notify.html.erb +35 -0
  53. data/app/views/rails_notice_my/base/_nav_my.html.erb +4 -0
  54. data/app/views/rails_notice_my/notification_settings/_form.html.erb +5 -0
  55. data/app/views/rails_notice_my/notification_settings/show.html.erb +5 -0
  56. data/app/views/rails_notice_my/notifications/_navbar.html.erb +13 -0
  57. data/app/views/rails_notice_my/notifications/_notifications.html.erb +17 -0
  58. data/app/views/rails_notice_my/notifications/edit.html.erb +2 -0
  59. data/app/views/rails_notice_my/notifications/index.html.erb +28 -0
  60. data/app/views/rails_notice_my/notifications/index.js.erb +13 -0
  61. data/app/views/rails_notice_my/notifications/new.html.erb +1 -0
  62. data/app/views/rails_notice_my/notifications/read.js.erb +2 -0
  63. data/app/views/rails_notice_my/notifications/read_all.js.erb +2 -0
  64. data/app/views/rails_notice_my/notifications/show.html.erb +7 -0
  65. data/config/locales/en.yml +7 -0
  66. data/config/locales/zh.yml +7 -0
  67. data/config/routes.rb +29 -0
  68. data/db/migrate/20170110025557_create_notification.rb +21 -0
  69. data/db/migrate/20170209025946_create_notification_settings.rb +23 -0
  70. data/lib/rails_notice.rb +13 -0
  71. data/lib/rails_notice/active_record.rb +18 -0
  72. data/lib/rails_notice/config.rb +13 -0
  73. data/lib/rails_notice/engine.rb +9 -0
  74. data/lib/rails_notice/version.rb +3 -0
  75. data/lib/tasks/the_notify_tasks.rake +4 -0
  76. metadata +132 -0
@@ -0,0 +1,156 @@
1
+ class Notification < ApplicationRecord
2
+ serialize :cc_emails, Array
3
+
4
+ attribute :code, :string, default: 'default'
5
+ belongs_to :receiver, polymorphic: true
6
+ belongs_to :sender, polymorphic: true, optional: true
7
+ belongs_to :notifiable, polymorphic: true, optional: true
8
+ has_one :notification_setting, ->(o) { where(receiver_type: o.receiver_type) }, primary_key: :receiver_id, foreign_key: :receiver_id
9
+
10
+ default_scope -> { order(id: :desc) }
11
+ scope :unread, -> { where(read_at: nil) }
12
+ scope :have_read, -> { where.not(read_at: nil) }
13
+
14
+ after_create_commit :process_job,
15
+ :send_email,
16
+ :update_unread_count
17
+
18
+ def process_job
19
+ make_as_unread
20
+ if sending_at
21
+ NotificationJob.set(wait_until: sending_at).perform_later id
22
+ else
23
+ NotificationJob.perform_later(self.id)
24
+ end
25
+ end
26
+
27
+ def send_email
28
+ return unless email_enable?
29
+
30
+ if notify_setting[:mailer_class]
31
+ notify_method = notify_setting[:mailer_method] || 'notify'
32
+ if sending_at
33
+ notify_setting[:mailer_class].public_send(notify_method, self.notifiable_id).deliver_later(wait_until: sending_at)
34
+ else
35
+ notify_setting[:mailer_class].public_send(notify_method, self.notifiable_id).deliver_later
36
+ end
37
+ else
38
+ if sending_at
39
+ RailsNoticeMailer.notify(self.id).deliver_later(wait_until: sending_at)
40
+ else
41
+ RailsNoticeMailer.notify(self.id).deliver_later
42
+ end
43
+ end
44
+ end
45
+
46
+ def email_enable?
47
+ if receiver.notification_setting&.accept_email
48
+ return true
49
+ end
50
+
51
+ if receiver.notification_setting&.accept_email.is_a?(FalseClass)
52
+ return false
53
+ end
54
+
55
+ RailsNotice.config.default_send_email
56
+ end
57
+
58
+ def notifiable_detail
59
+ r = self.notifiable.as_json(**notify_setting.slice(:only, :except, :include, :methods))
60
+ Hash(r).with_indifferent_access
61
+ end
62
+
63
+ def notifiable_attributes
64
+ if verbose
65
+ r = notifiable_detail
66
+ r.transform_values! do |i|
67
+ next i unless i.acts_like?(:time)
68
+ if self.receiver.respond_to?(:timezone)
69
+ _zone = self.receiver.timezone
70
+ else
71
+ _zone = Time.zone.name
72
+ end
73
+ i.in_time_zone(_zone).strftime '%Y-%m-%d %H:%M:%S'
74
+ end
75
+ r
76
+ else
77
+ {}.with_indifferent_access
78
+ end
79
+ end
80
+
81
+ def notify_setting
82
+ notifiable_type.constantize.notifies[self.code] || {}
83
+ end
84
+
85
+ def tr_key(column)
86
+ "#{self.class.i18n_scope}.notify.#{notifiable.class.base_class.model_name.i18n_key}.#{self.code}.#{column}"
87
+ end
88
+
89
+ def code
90
+ super ? super.to_sym : :default
91
+ end
92
+
93
+ def title
94
+ return super unless notifiable
95
+ tr_values = notifiable_detail.slice *I18nHelper.interpolate_key(I18n.t(tr_key(:title)))
96
+ tr_values.merge! notify_setting.fetch(:tr_values, {})
97
+
98
+ if super.blank?
99
+ I18n.t tr_key(:title), tr_values
100
+ else
101
+ super
102
+ end
103
+ end
104
+
105
+ def body
106
+ return super unless notifiable
107
+ tr_values = notifiable_detail.slice *I18nHelper.interpolate_key(I18n.t(tr_key(:body)))
108
+ tr_values.merge! notify_setting.fetch(:tr_values, {})
109
+
110
+ if super.blank?
111
+ I18n.t tr_key(:body), tr_values
112
+ else
113
+ super
114
+ end
115
+ end
116
+
117
+ def cc_emails
118
+ r = notify_setting.fetch(:cc_emails, []).map do |i|
119
+ next i unless i.respond_to?(:call)
120
+ i.call(notifiable)
121
+ end
122
+ r.flatten.concat super
123
+ end
124
+
125
+ def unread_count
126
+ Rails.cache.read("#{self.receiver_type}_#{self.receiver_id}_unread") || 0
127
+ end
128
+
129
+ def make_as_unread
130
+ if read_at.present?
131
+ self.update(read_at: nil)
132
+ Rails.cache.increment "#{self.receiver_type}_#{self.receiver_id}_unread"
133
+ end
134
+ end
135
+
136
+ def make_as_read
137
+ if read_at.blank?
138
+ update(read_at: Time.now)
139
+ Rails.cache.decrement "#{self.receiver_type}_#{self.receiver_id}_unread"
140
+ end
141
+ end
142
+
143
+ def update_unread_count
144
+ Rails.cache.write "#{self.receiver_type}_#{self.receiver_id}_unread", Notification.where(receiver_id: self.receiver_id, receiver_type: self.receiver_type, read_at: nil).count, raw: true
145
+ end
146
+
147
+ def self.update_unread_count(receiver)
148
+ if Rails.cache.write "#{receiver.class.name}_#{receiver.id}_unread", Notification.where(receiver_id: receiver.id, receiver_type: receiver.class.name, read_at: nil).count, raw: true
149
+ Rails.cache.read "#{receiver.class.name}_#{receiver.id}_unread"
150
+ end
151
+ end
152
+
153
+ end
154
+
155
+ # notifiable_type:
156
+ # notifiable_id:
@@ -0,0 +1,8 @@
1
+ class NotificationSetting < ApplicationRecord
2
+ belongs_to :receiver, polymorphic: true
3
+ serialize :notifiable_types, Array
4
+
5
+ def notifiable_types
6
+ super + RailsNotice.config.default_notifiable_types
7
+ end
8
+ end
@@ -0,0 +1,6 @@
1
+ <% if current_receiver %>
2
+ <%= link_to notifications_path, class: 'item', remote: true, id: 'notify_show' do %>
3
+ <i class="bell icon"></i>
4
+ <span id="notice_count" style="padding-left: 5px"><%= current_receiver.unread_count %></span>
5
+ <% end %>
6
+ <% end %>
@@ -0,0 +1,16 @@
1
+ <div class="ui vertical fluid icon menu">
2
+ <div class="header center link item" id="close_side">
3
+ <i class="content icon"></i>
4
+ </div>
5
+ <div class="item ui dropdown">
6
+ <a class="<%= active_helper(controllers: ['notifications', 'notification_settings', 'notify_settings'], active_class: 'title active', item_class: 'title') %>">
7
+ <i class="users icon"></i>
8
+ <span>Notification</span>
9
+ </a>
10
+ <div class="<%= active_helper(controllers: ['notifications', 'notification_settings', 'notify_settings'], active_class: 'menu content active', item_class: 'menu content') %>">
11
+ <%= link_to 'Notification', admin_notifications_path, class: active_helper(controllers: 'notifications') %>
12
+ <%= link_to 'Receiver Settings', admin_notification_settings_path, class: active_helper(controllers: ['notification_settings']) %>
13
+ </div>
14
+ </div>
15
+ </div>
16
+
@@ -0,0 +1,7 @@
1
+ <%= form_with model: [:admin, @notification_setting], local: true do |f| %>
2
+ <%= render 'shared/error_messages', target: @notification_setting %>
3
+ <%= f.number_field :showtime %>
4
+ <%= f.check_box :accept_email %>
5
+ <%= f.collection_check_boxes :notifiable_types, RailsNotice.notifiable_types, :to_s, :to_s %>
6
+ <%= f.submit %>
7
+ <% end %>
@@ -0,0 +1,11 @@
1
+ <%= search_form_with do |f| %>
2
+ <div class="fields">
3
+ <div class="ui left action input">
4
+ <%= f.select :receiver_type, options_for_select(Notification.options_i18n(:receiver_type)), { on: { wrapper_input: false } }, { class: 'ui selection dropdown', id: 'q_receiver_type' } %>
5
+ <%= f.select :receiver_id, options_for_select([]), { on: { wrapper_input: false } }, { id: 'q_receiver_id', class: 'ui search dropdown' } %>
6
+ </div>
7
+ <%= f.submit 'Search' %>
8
+ </div>
9
+ <% end %>
10
+
11
+
@@ -0,0 +1,14 @@
1
+ <div>
2
+
3
+ <div class="ui segment breadcrumb">
4
+ <%= link_to 'Back', admin_notification_settings_path, class: 'section' %>
5
+ <div class="divider"> / </div>
6
+ <div class="active section">Edit</div>
7
+ </div>
8
+
9
+ <div class="ui segment">
10
+ <%= render 'form' %>
11
+ </div>
12
+
13
+ </div>
14
+
@@ -0,0 +1,52 @@
1
+ <div class="ui top attached menu borderless">
2
+ <div class="header item">Notification Settings</div>
3
+ <div class="right menu">
4
+ <div class="item"><%= link_to 'New Notification Setting', new_admin_notification_setting_path, class: 'ui teal button' %></div>
5
+ </div>
6
+ </div>
7
+
8
+ <div class="ui attached segment">
9
+ <%= render 'search_form' %>
10
+ </div>
11
+
12
+ <table class="ui bottom attached table">
13
+ <thead>
14
+ <tr>
15
+ <th><%= NotificationSetting.human_attribute_name(:id) %></th>
16
+ <th><%= NotificationSetting.human_attribute_name(:receiver_type) %></th>
17
+ <th><%= NotificationSetting.human_attribute_name(:receiver_id) %></th>
18
+ <th><%= NotificationSetting.human_attribute_name(:showtime) %></th>
19
+ <th><%= NotificationSetting.human_attribute_name(:accept_email) %></th>
20
+ <th><%= NotificationSetting.human_attribute_name(:notifiable_types) %></th>
21
+ <th></th>
22
+ </tr>
23
+ </thead>
24
+
25
+ <tbody>
26
+ <% @notification_settings.each do |notification_setting| %>
27
+ <tr>
28
+ <td><%= notification_setting.id %></td>
29
+ <td><%= notification_setting.receiver_type %></td>
30
+ <td><%= notification_setting.receiver_id %></td>
31
+ <td><%= notification_setting.showtime %></td>
32
+ <td>
33
+ <%= form_with model: notification_setting, url: admin_notification_setting_path(notification_setting.id), method: :patch, remote: true do |f| %>
34
+ <%= f.check_box :accept_email, onChange: "Rails.fire(this.form, 'submit')", label: false, on: {wrapper_all: false, offset: false} %>
35
+ <% end %>
36
+ </td>
37
+ <td class="ui labels">
38
+ <% notification_setting.notifiable_types.each do |notifiable_type| %>
39
+ <span class="ui label"><%= notifiable_type %></span>
40
+ <% end %>
41
+ </td>
42
+ <td class="ui labels">
43
+ <%= link_to 'Show', admin_notification_setting_path(notification_setting), class: 'ui blue label' %>
44
+ <%= link_to 'Edit', edit_admin_notification_setting_path(notification_setting), class: 'ui pink label' %>
45
+ <%= link_to 'Destroy', admin_notification_setting_path(notification_setting), method: :delete, data: { confirm: 'Are you sure?' }, class: 'ui red label' %>
46
+ </td>
47
+ </tr>
48
+ <% end %>
49
+ </tbody>
50
+ </table>
51
+
52
+ <%= paginate @notification_settings %>
@@ -0,0 +1,13 @@
1
+ <div>
2
+
3
+ <div class="ui segment breadcrumb">
4
+ <%= link_to 'Back', admin_notification_settings_path, class: 'section' %>
5
+ <div class="divider"> / </div>
6
+ <div class="active section">Add</div>
7
+ </div>
8
+
9
+ <div class="ui segment">
10
+ <%= render 'form' %>
11
+ </div>
12
+
13
+ </div>
@@ -0,0 +1,6 @@
1
+ <%= default_form_with model: [:admin, @user, @notification] do |f| %>
2
+ <%= f.text_field :title, label: '标题:' %>
3
+ <%= f.text_area :body, label: '内容:' %>
4
+ <%= f.text_field :link, label: 'Link:' %>
5
+ <%= f.submit %>
6
+ <% end %>
@@ -0,0 +1,6 @@
1
+ <div class="ui top attached borderless menu">
2
+ <div class="header item">Notification List</div>
3
+ <div class="right menu">
4
+ <div class="item"><%= link_to 'New', new_admin_notification_path, class: 'ui teal button' %></div>
5
+ </div>
6
+ </div>
@@ -0,0 +1,11 @@
1
+ <%= search_form_with scope: :q do |f| %>
2
+ <div class="fields">
3
+ <div class="ui left action input">
4
+ <%= f.select :receiver_type, options_for_select(Notification.options_i18n(:receiver_type)), { on: { wrapper_input: false } }, { class: 'ui selection dropdown', id: 'q_receiver_type' } %>
5
+ <%= f.select :receiver_id, options_for_select([]), { on: { wrapper_input: false } }, { id: 'q_receiver_id', class: 'ui search dropdown' } %>
6
+ </div>
7
+ <%= f.text_field :'body-like' %>
8
+ <%= f.submit 'Search' %>
9
+ </div>
10
+ <% end %>
11
+
@@ -0,0 +1,14 @@
1
+ <div class="sixteen wide column">
2
+
3
+ <div class="ui breadcrumb">
4
+ <%= link_to '返回', admin_user_notifications_path, class: 'section' %>
5
+ <div class="divider"> / </div>
6
+ <div class="active section">编辑</div>
7
+ </div>
8
+
9
+ <div>
10
+ <%= render 'form' %>
11
+ </div>
12
+
13
+ </div>
14
+
@@ -0,0 +1,44 @@
1
+ <%= render 'menu' %>
2
+
3
+ <div class="ui attached segment">
4
+ <%= render 'search_form' %>
5
+ </div>
6
+
7
+ <table class="ui table">
8
+ <thead>
9
+ <tr>
10
+ <th>ID</th>
11
+ <th>Receiver Type</th>
12
+ <th>Receiver ID</th>
13
+ <th>Title</th>
14
+ <th>Body</th>
15
+ <th>Link</th>
16
+ <th>Read At</th>
17
+ <th>Sent Result</th>
18
+ <th>Operation</th>
19
+ </tr>
20
+ </thead>
21
+
22
+ <tbody>
23
+ <% @notifications.each do |notification| %>
24
+ <tr>
25
+ <td><%= notification.id %></td>
26
+ <td><%= notification.receiver_type %></td>
27
+ <td><%= notification.receiver_id %></td>
28
+ <td><%= notification.title %></td>
29
+ <td><%= notification.body %></td>
30
+ <td><%= notification.link %></td>
31
+ <td><%= notification.read_at %></td>
32
+ <td>
33
+ <code><%= notification.sent_result %></code>
34
+ </td>
35
+ <td class="ui labels">
36
+ <%= link_to 'Push', push_admin_notification_path(notification), method: :patch, remote: true, data: { confirm: 'Are you sure?' }, class: 'ui mini green label' %>
37
+ <%= link_to 'Email', email_admin_notification_path(notification), method: :patch, remote: true, data: { confirm: 'Are you sure?' }, class: 'ui mini green label' %>
38
+ </td>
39
+ </tr>
40
+ <% end %>
41
+ </tbody>
42
+ </table>
43
+
44
+ <%= paginate @notifications %>
@@ -0,0 +1,15 @@
1
+ <div class="three wide column">
2
+ <%= render 'admin/users/nav' %>
3
+ </div>
4
+
5
+ <div class="thirteen wide column">
6
+ <div class="ui breadcrumb">
7
+ <%= link_to 'Back', admin_user_notifications_path(@user), class: 'section' %>
8
+ <div class="divider"> / </div>
9
+ <div class="active section">New</div>
10
+ </div>
11
+
12
+ <div>
13
+ <%= render 'form' %>
14
+ </div>
15
+ </div>
@@ -0,0 +1,35 @@
1
+ <p>Dear <%= @notification.receiver.endearing_name %>,</p>
2
+
3
+ <p><%= @notification.body %></p>
4
+
5
+ <% if @notification.notifiable && @notification.verbose %>
6
+
7
+ <table class="ui very basic table">
8
+ <tbody>
9
+ <% @notification.notifiable_attributes.each do |key, value| %>
10
+ <tr>
11
+ <td class="right aligned">
12
+ <%= @notification.notifiable.class.human_attribute_name(key) %>
13
+ </td>
14
+ <td>
15
+ <% if value.is_a?(Hash) %>
16
+ <%= simple_format_hash(value) %>
17
+ <% elsif value.is_a?(String) && @notification.notifiable.column_for_attribute(key).limit.to_i > 255 %>
18
+ <%= simple_format(value) %>
19
+ <% else %>
20
+ <%= value %>
21
+ <% end %>
22
+ </td>
23
+ </tr>
24
+ <% end %>
25
+ </tbody>
26
+ </table>
27
+
28
+ <% end %>
29
+
30
+ <p><%= t('rails_notice.detail') %>: <%= link_to @notification.link, @notification.link %></p>
31
+
32
+ <% if @notification.sender %>
33
+ <p>Best,<br>
34
+ <%= @notification.sender.endearing_name %></p>
35
+ <% end %>
@@ -0,0 +1,4 @@
1
+ <div class="ui menu vertical fluid">
2
+ <%= link_to t('rails_notice.unread'), notifications_path(receiver: params[:receiver]), class: active_params(scope: '', active_class: 'item active', item_class: 'item') %>
3
+ <%= link_to t('rails_notice.readed'), notifications_path(receiver: params[:receiver], scope: 'have_read'), class: active_params(scope: 'have_read', active_class: 'item active', item_class: 'item') %>
4
+ </div>