eventifier 0.0.7 → 0.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/.travis.yml +7 -8
  4. data/Gemfile +1 -1
  5. data/README.textile +44 -3
  6. data/app/assets/images/eventifier/notifications-bell.png +0 -0
  7. data/app/assets/images/eventifier/notifications-tower.png +0 -0
  8. data/app/assets/javascripts/eventifier/notifications.coffee +196 -0
  9. data/app/assets/javascripts/eventifier/templates/dropdown.hamlc +10 -0
  10. data/app/assets/javascripts/eventifier/templates/settings.hamlc +11 -0
  11. data/app/assets/stylesheets/eventifier/notifications.scss +167 -0
  12. data/app/controllers/eventifier/application_controller.rb +4 -0
  13. data/app/controllers/eventifier/notifications_controller.rb +36 -0
  14. data/app/controllers/eventifier/preferences_controller.rb +19 -0
  15. data/app/helpers/eventifier/notification_helper.rb +87 -0
  16. data/app/helpers/eventifier/path_helper.rb +15 -0
  17. data/app/mailers/eventifier/mailer.rb +12 -0
  18. data/app/models/event_tracking.rb +3 -0
  19. data/app/models/eventifier/event.rb +22 -0
  20. data/app/models/eventifier/ghost.rb +21 -0
  21. data/app/models/eventifier/notification.rb +38 -0
  22. data/app/models/eventifier/notification_setting.rb +14 -0
  23. data/app/views/eventifier/_notification.haml +1 -0
  24. data/app/views/eventifier/email/_notification.haml +2 -0
  25. data/app/views/eventifier/mailer/notifications.haml +5 -0
  26. data/app/views/eventifier/notifications/index.jbuilder +6 -0
  27. data/config/routes.rb +7 -0
  28. data/config.ru +7 -0
  29. data/{lib/generators/eventifier/install/templates/migration.rb → db/migrate/1_eventifier_setup.rb} +2 -3
  30. data/db/migrate/2_eventifier_notification_settings.rb +11 -0
  31. data/db/migrate/3_notification_sent_status.rb +15 -0
  32. data/db/migrate/4_event_groupable.rb +19 -0
  33. data/eventifier.gemspec +12 -15
  34. data/lib/eventifier/delivery.rb +59 -0
  35. data/lib/eventifier/{railtie.rb → engine.rb} +4 -2
  36. data/lib/eventifier/event_subscriber.rb +44 -0
  37. data/lib/eventifier/event_tracking.rb +5 -58
  38. data/lib/eventifier/mailers/helpers.rb +70 -0
  39. data/lib/eventifier/notifier/notification_mapping.rb +34 -0
  40. data/lib/eventifier/notifier/notification_subscriber.rb +23 -0
  41. data/lib/eventifier/notifier/notifier.rb +21 -0
  42. data/lib/eventifier/preferences.rb +44 -0
  43. data/lib/eventifier/relationship.rb +36 -0
  44. data/lib/eventifier/trackable_class.rb +74 -0
  45. data/lib/eventifier/tracker.rb +18 -0
  46. data/lib/eventifier.rb +35 -11
  47. data/lib/generators/eventifier/install/install_generator.rb +2 -5
  48. data/lib/generators/eventifier/install/templates/events.en.yaml +1 -0
  49. data/lib/tasks/email.rake +8 -0
  50. data/spec/controllers/eventifier/notifications_controller_spec.rb +24 -0
  51. data/spec/controllers/eventifier/preferences_controller_spec.rb +56 -0
  52. data/spec/eventifier/delivery_spec.rb +110 -0
  53. data/spec/eventifier/preferences_spec.rb +69 -0
  54. data/spec/eventifier/relationship_spec.rb +33 -0
  55. data/spec/eventifier_spec.rb +6 -48
  56. data/spec/fabricators/fabricator.rb +11 -11
  57. data/spec/helpers/eventifier/notification_helper_spec.rb +169 -0
  58. data/spec/helpers/eventifier/path_helper_spec.rb +19 -0
  59. data/spec/integration/eventifier_spec.rb +113 -70
  60. data/spec/integration/internationalisation_spec.rb +39 -0
  61. data/spec/internal/app/controllers/application_controller.rb +3 -0
  62. data/spec/internal/app/lib/event_tracking.rb +12 -0
  63. data/spec/internal/app/models/category.rb +3 -0
  64. data/spec/internal/app/models/post.rb +8 -0
  65. data/spec/internal/app/models/subscription.rb +4 -0
  66. data/spec/internal/app/models/user.rb +4 -0
  67. data/spec/internal/app/views/eventifier/dropdown/_awesome_object.haml +0 -0
  68. data/spec/internal/config/database.yml +4 -0
  69. data/spec/internal/config/initializers/eventifier.rb +1 -0
  70. data/spec/internal/config/locales/events.en.yml +20 -0
  71. data/spec/internal/config/routes.rb +5 -0
  72. data/spec/internal/db/schema.rb +31 -0
  73. data/spec/internal/log/.gitignore +1 -0
  74. data/spec/internal/public/favicon.ico +0 -0
  75. data/spec/models/eventifier/event_spec.rb +23 -0
  76. data/spec/{ghost_spec.rb → models/eventifier/ghost_spec.rb} +3 -3
  77. data/spec/{notification_spec.rb → models/eventifier/notification_spec.rb} +1 -9
  78. data/spec/notification_mailer_spec.rb +8 -4
  79. data/spec/notifier/notification_mapping_spec.rb +28 -0
  80. data/spec/spec_helper.rb +14 -8
  81. data/spec/support/controller_helpers.rb +14 -0
  82. metadata +205 -128
  83. data/Gemfile.lock +0 -97
  84. data/lib/eventifier/active_record/event.rb +0 -10
  85. data/lib/eventifier/active_record/event_observer.rb +0 -8
  86. data/lib/eventifier/active_record/event_tracking.rb +0 -11
  87. data/lib/eventifier/active_record/ghost.rb +0 -9
  88. data/lib/eventifier/active_record/notification.rb +0 -13
  89. data/lib/eventifier/active_record_support.rb +0 -5
  90. data/lib/eventifier/event_helper.rb +0 -40
  91. data/lib/eventifier/event_mixin.rb +0 -45
  92. data/lib/eventifier/event_observer_mixin.rb +0 -44
  93. data/lib/eventifier/ghost_mixin.rb +0 -27
  94. data/lib/eventifier/helper_methods.rb +0 -36
  95. data/lib/eventifier/mongoid/event.rb +0 -16
  96. data/lib/eventifier/mongoid/event_observer.rb +0 -7
  97. data/lib/eventifier/mongoid/event_tracking.rb +0 -13
  98. data/lib/eventifier/mongoid/ghost.rb +0 -15
  99. data/lib/eventifier/mongoid/notification.rb +0 -19
  100. data/lib/eventifier/mongoid/user_patch.rb +0 -3
  101. data/lib/eventifier/mongoid_support.rb +0 -6
  102. data/lib/eventifier/notification_helper.rb +0 -42
  103. data/lib/eventifier/notification_mailer.rb +0 -18
  104. data/lib/eventifier/notification_mixin.rb +0 -50
  105. data/lib/eventifier/version.rb +0 -3
  106. data/spec/event_helper_spec.rb +0 -67
  107. data/spec/event_observer_spec.rb +0 -20
  108. data/spec/event_spec.rb +0 -39
  109. data/spec/helper_methods_spec.rb +0 -36
  110. data/spec/notification_helper_spec.rb +0 -65
  111. data/spec/support/action_mailer.rb +0 -3
  112. data/spec/support/database_cleaner.rb +0 -3
  113. data/spec/test_classes/active_record_support.rb +0 -72
  114. data/spec/test_classes/mongoid_support.rb +0 -34
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 15920100b421b49950f3d1e3423514d8c2a8ecd8
4
+ data.tar.gz: 8b5c7df0a4c737676a07c8d00ff9a694070a872f
5
+ SHA512:
6
+ metadata.gz: e96258ba280481331e93dfc861fc26e22b5525e19ac8890e71d71efde10a54e3e0018ed67b191668c8b63fef95f7cfb8938ade01e8036b9717460695aa36669e
7
+ data.tar.gz: 37e145a2626ebaf891cff51bf77be2846fca4a99b4d52a4ac19be066f5aaeb73285f1d90a64826b822cc32b83b0ca5352f7bcbf6f899b421171f3a76db803a6a
data/.gitignore CHANGED
@@ -1,3 +1,4 @@
1
1
  *.gem
2
+ Gemfile.lock
2
3
  tmp
3
4
  .idea/*
data/.travis.yml CHANGED
@@ -1,14 +1,13 @@
1
1
  language: ruby
2
2
  rvm:
3
- # - 1.8.7
4
3
  - 1.9.2
5
4
  - 1.9.3
6
- # - jruby-18mode # JRuby in 1.8 mode
7
5
  # - jruby-19mode # JRuby in 1.9 mode
8
-
9
- env:
10
- - ORM=active_record
11
- - ORM=mongoid
12
-
13
6
  before_script:
14
- - psql -c 'create database eventifier;' -U postgres
7
+ - psql -c 'create database eventifier;' -U postgres
8
+ notifications:
9
+ campfire:
10
+ rooms:
11
+ - secure: "PZeuyuOFIPVzA6Lr4unFdatKz/hYlwk8LRBK1DMWl7lPWL4RSxoCVY/Lh5mK\nj+1bYtPM73WL38xNLDsXeD6YpS1CkB66PaUcmhP+EnyH+5L1NZ2hKJG1zY4x\njC2ux6hKSstmQheRlIBaWhHUpGqZkoDUPxAI4C1qz44FkfHtI40="
12
+ on_success: change
13
+ on_failure: change
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
1
  source "http://rubygems.org"
2
2
 
3
3
  # Specify your gem's dependencies in eventifier.gemspec
4
- gemspec
4
+ gemspec
data/README.textile CHANGED
@@ -19,7 +19,6 @@ class EventTracking
19
19
  events_for Post do
20
20
  track_on [:create, :update, :destroy], :attributes => { :except => %w(updated_at) }
21
21
  notify :group => :members, :on => [:create, :update]
22
- url -> post { [post.category, post] }
23
22
  end
24
23
 
25
24
  events_for Announcement do
@@ -36,13 +35,55 @@ h2. Installation
36
35
 
37
36
  <pre><code>
38
37
  rails generate eventifier:install
38
+ rake eventifier:install:migrations
39
+ </code></pre>
40
+
41
+ h4. Add required gems
42
+
43
+ <pre><code>
44
+ # Gemfile
45
+ group :assets do
46
+ gem 'compass-rails'
47
+ gem 'haml_coffee_assets'
48
+ gem 'jbuilder'
49
+ end
50
+ </code></pre>
51
+
52
+ h4. Add stylesheets
53
+
54
+ <pre><code>
55
+ # app/assets/stylesheets/application.scss
56
+ /*= require eventifier/notifications*/
57
+ </code></pre>
58
+
59
+ h4. Add javascript
60
+
61
+ <pre><code>
62
+ # app/assets/javascripts/application.coffee
63
+ #= require eventifier/notifications
64
+
65
+ new NotificationDropdown el: $(".notifications")
66
+ </code></pre>
67
+
68
+ h4. Add the required routes
69
+
70
+ <pre><code>
71
+ # config/routes.rb
72
+ mount Eventifier::Engine => '/'
73
+ </code></pre>
74
+
75
+ h2. Sending of emails
76
+
77
+ You want to add a scheduled task to run the following task every x minutes
78
+
79
+ <pre><code>
80
+ rake eventifier:email:deliver
39
81
  </code></pre>
40
82
 
41
83
  h2. Requirements
42
84
 
43
85
  * ActiveRecord
44
86
 
45
-
46
87
  h2. Testing
47
88
 
48
89
  Creating the database:
@@ -55,4 +96,4 @@ Spec
55
96
 
56
97
  h2. Contributors
57
98
 
58
- * PJ Murray
99
+ * PJ Murray
@@ -0,0 +1,196 @@
1
+ #= require hamlcoffee
2
+ #= require ./templates/dropdown
3
+ #= require ./templates/settings
4
+ # Usage
5
+ # window.notifications = new NotificationDropdown el: $('.notifications'), limit: 5
6
+
7
+ class window.NotificationDropdown
8
+ template: JST['eventifier/templates/dropdown']
9
+ settingsTemplate: JST['eventifier/templates/settings']
10
+
11
+ constructor: (options) ->
12
+ {@el, @limit, @pollTime} = options
13
+ @limit = @limit || 5
14
+ @pollTime = @pollTime || 15
15
+
16
+
17
+ [@notifications, @renderedNotifications, @unreadCount, @lastReadAt] = [[], [], 0, new Date()]
18
+
19
+ @render()
20
+ @loadMore(limit: 14)
21
+
22
+ render: =>
23
+ @el.html(@template(@)).attr('tabindex', 0)
24
+
25
+ @checkVisibility()
26
+ @setEvents()
27
+ @poll()
28
+
29
+ checkVisibility: =>
30
+ @el.addClass("notifications_active").find('#notification_dropdown').attr('opacity': 0)
31
+ @el.find('#notification_dropdown').offset (index, coords)=>
32
+ coords.left = 10 if coords.left < 0
33
+ if coords.left + @el.find('#notification_dropdown').width() > $(window).width()
34
+ coords.left = $(window).width() - @el.find('#notification_dropdown').width() - 5
35
+
36
+ @el.find('#notification_dropdown').offset coords
37
+
38
+ if @el.find('#notification_dropdown').position().left > -@el.find('#notification_dropdown').width()/2
39
+ @el.find('#notification_dropdown').addClass('left_nipple')
40
+
41
+ @el.removeClass("notifications_active").find('#notification_dropdown').attr('opacity': 1)
42
+
43
+ setEvents: =>
44
+ @el.on 'click', '.toggle_dropdown', @toggleDropdown
45
+ @el.on 'click', '.toggle_settings', @toggleSettings
46
+ @el.on 'click', '#email_settings_default', @defaultSettings
47
+ @el.on 'click', '.save_settings', @saveSettings
48
+ @el.on 'addNotifications', @renderNotifications
49
+ @el.on 'addNotifications', @setUnreadCount
50
+ @el.on 'poll', @poll
51
+ @el.on 'scroll', 'ol', @scrolling
52
+ $(window).on 'click', @blurNotifications
53
+
54
+ @
55
+
56
+ renderNotifications: =>
57
+ @el.find(".none").remove() if @notifications.length > 0
58
+ $.each @notifications, (index, notification)=>
59
+ unless $.inArray(notification.id, @renderedNotifications) >= 0
60
+ if new Date(notification.created_at) > @lastReadAt
61
+ @el
62
+ .find('ol')
63
+ .prepend $("<li />")
64
+ .addClass('unread')
65
+ .html(notification.html)
66
+ else
67
+ @el
68
+ .find('ol')
69
+ .append($("<li />").html(notification.html))
70
+ @renderedNotifications.push notification.id
71
+
72
+ isActive: =>
73
+ @el.hasClass('notifications_active')
74
+
75
+ isAlerting: =>
76
+ @unreadCount > 0
77
+
78
+ toggleDropdown: (event)=>
79
+ event.preventDefault() if event?
80
+
81
+ @el.toggleClass('notifications_active')
82
+ @setLastRead()
83
+
84
+ toggleSettings: (event)=>
85
+ event.preventDefault() if event?
86
+ $.ajax
87
+ url: "/preferences"
88
+ success: (data)=>
89
+ @el.find("#settings_pane").html(@settingsTemplate(data))
90
+ @defaultSettings() if @arrayFromObject($.makeArray(data)).default
91
+ @el.toggleClass('show_settings')
92
+
93
+ defaultSettings: =>
94
+ @el.find("#settings_pane").toggleClass("disabled")
95
+ @el.find("input:not([id='email_settings_default'])").each ->
96
+ $(@).attr(disabled: !$(@).attr('disabled')).prop('checked', true)
97
+
98
+ saveSettings: (event)=>
99
+ event.preventDefault() if event?
100
+
101
+ serializedSettings = {}
102
+ @el.find("input:checked").each ->
103
+ serializedSettings[@name] = @value
104
+
105
+ $.ajax
106
+ url: "/preferences"
107
+ type: "PUT"
108
+ data: preferences: serializedSettings
109
+ success: (data)=> @el.toggleClass('show_settings')
110
+ error: -> alert "There was a problem saving your settings"
111
+
112
+ hide: =>
113
+ @el.removeClass('notifications_active')
114
+ $(window).off 'click', @blurNotifications
115
+
116
+ blurNotifications: (event)=>
117
+ if @isActive() and $.inArray(@el[0], $(event.target).parents()) < 0
118
+ @toggleDropdown()
119
+
120
+ loadMore: (params = {})=>
121
+ $.ajax
122
+ url: "/notifications"
123
+ dataType: 'json'
124
+ data: params
125
+ success: @addNotifications
126
+ error: => @el.off 'poll', @poll
127
+
128
+ addNotifications: (data)=>
129
+ @lastReadAt = new Date(data.last_read_at)
130
+ new_notifications = $.grep(data.notifications, (notification)=>
131
+ $.inArray(notification.id, $.map(@notifications, (n)->n.id)) < 0
132
+ )
133
+ @notifications = @notifications.concat new_notifications
134
+ @el.off('scroll', 'ol', @scrolling) if data.notifications == []
135
+ @el.trigger 'addNotifications'
136
+
137
+ updateAlert: =>
138
+ if @unreadCount == 0
139
+ displayCount = null
140
+ @el.removeClass('alerting')
141
+ else
142
+ displayCount = @unreadCount
143
+ @el.addClass('alerting')
144
+
145
+ @el.find(".notification_alert").html(displayCount)
146
+
147
+ setUnreadCount: =>
148
+ @unreadCount = $.grep(@notifications, (notification)=>
149
+ new Date(notification.created_at) > @lastReadAt
150
+ ).length
151
+
152
+ @updateAlert()
153
+
154
+ @unreadCount
155
+
156
+ setLastRead: =>
157
+ if @isAlerting()
158
+ @lastReadAt = new Date()
159
+ @setUnreadCount()
160
+ $.post '/notifications/touch'
161
+
162
+ poll: =>
163
+ @loadMore(recent: true, since: @lastLookTime())
164
+
165
+ setTimeout =>
166
+ @el.trigger 'poll'
167
+ , @pollTime*1000
168
+
169
+ oldestNotificationTime: =>
170
+ Math.min.apply Math, $.map(@notifications, (notification)->
171
+ notification.created_at
172
+ )
173
+
174
+ newestNotificationTime: =>
175
+ if @notifications.length
176
+ Math.max.apply Math, $.map(@notifications, (notification)->
177
+ notification.created_at
178
+ )
179
+ else
180
+ 0
181
+
182
+ lastLookTime: =>
183
+ Math.max(@lastReadAt.getTime()/1000, @newestNotificationTime())
184
+
185
+ scrolling: =>
186
+ scrollWindow = @$el.find('ol')
187
+
188
+ if (scrollWindow.scrollTop() + scrollWindow.innerHeight() >= scrollWindow[0].scrollHeight - 50)
189
+ @loadMore(after: @oldestNotificationTime())
190
+
191
+ arrayFromObject: (collection)->
192
+ serializedObject = {}
193
+ $.each collection, ->
194
+ serializedObject[@key] = @value
195
+
196
+ serializedObject
@@ -0,0 +1,10 @@
1
+ %a.toggle_dropdown{ href: "#" }
2
+ View Notifications
3
+ %span.notification_alert
4
+ #notification_dropdown
5
+ %header
6
+ %a.toggle_settings{ href: '#' } Settings
7
+ %h4 Notifications
8
+ %ol
9
+ %li.none You have no notifications
10
+ #settings_pane
@@ -0,0 +1,11 @@
1
+ %form#email_notification_settings
2
+
3
+ %strong Receive emails for:
4
+
5
+ - for i, setting of @
6
+ %label{ for: "email_settings_#{setting.key}" }
7
+ %input{ type: 'checkbox', checked: setting.value, id: "email_settings_#{setting.key}", name: setting.key, value: setting.value }
8
+ = setting.label
9
+
10
+ .buttons
11
+ %button.save_settings Save
@@ -0,0 +1,167 @@
1
+ @import "compass/css3/images";
2
+ @import "compass/css3/box-shadow";
3
+ @import "compass/css3/transition";
4
+ @import "compass/css3/transform";
5
+
6
+ @mixin respond-to($media) {
7
+ @if $media == iphone {
8
+ @media only screen and (max-width: 320px) { @content }
9
+ }
10
+ @else if $media == iphone-landscape {
11
+ @media only screen and (max-width: 480px) { @content }
12
+ }
13
+ @else if $media == ipad {
14
+ @media only screen and (max-width: 980px) { @content }
15
+ }
16
+ @else if $media == ipad-landscape {
17
+ @media only screen and (max-width: 1024px) { @content }
18
+ }
19
+ }
20
+
21
+ .notifications {
22
+ display: inline-block;
23
+ float: none;
24
+ position: relative;
25
+ width: 30px;
26
+ height: 30px;
27
+ text-align: left;
28
+ &:focus { outline: 0 }
29
+ @include respond-to(iphone-landscape) {
30
+ float: none;
31
+ display: inline-block;
32
+ margin-bottom: 10px;
33
+ top: 0px;
34
+ }
35
+ > a {
36
+ display: block;
37
+ background: image-url('eventifier/notifications-tower.png') no-repeat;
38
+ text-indent: -9999em;
39
+ width: 100%;
40
+ height: 100%;
41
+ opacity: 0.5;
42
+ &:hover { opacity: 1; }
43
+ .notification_alert {
44
+ display: none;
45
+ border-radius: 8px;
46
+ position: absolute;
47
+ top: -5px;
48
+ right: -5px;
49
+ padding: 3px 6px;
50
+ background: red;
51
+ color: white;
52
+ border: 1px solid black;
53
+ box-shadow: 0 1px 1px rgba(black, 0.4);
54
+ text-shadow: 0 1px 1px rgba(black, 0.4);
55
+ font-size: 11px;
56
+ text-indent: 0;
57
+ line-height: 1;
58
+ }
59
+ }
60
+ &.alerting > a, &.notifications_active > a { opacity: 1; }
61
+ &.alerting > a .notification_alert { display: block; }
62
+ #settings_pane {
63
+ display: none;
64
+ margin: 0;
65
+ padding: 0;
66
+ width: 100%;
67
+ height: 100%;
68
+ max-height:380px;
69
+ background-color: #f2f2f2;
70
+ strong {
71
+ display: block;
72
+ font-size: 13px;
73
+ padding: 5px 5px 10px;
74
+ }
75
+ label {
76
+ display: block;
77
+ margin-bottom: 15px;
78
+ font-size: 12px;
79
+ line-height: 1.2;
80
+ padding-left: 35px;
81
+ input {
82
+ float: left;
83
+ margin-left: -25px;
84
+ }
85
+ }
86
+ &.disabled label:not([for='email_settings_default']) { opacity: 0.5; }
87
+ .buttons { text-align: center; }
88
+ }
89
+ &.show_settings {
90
+ #notification_dropdown ol { display: none; }
91
+ #settings_pane { display: block; }
92
+ }
93
+ #notification_dropdown {
94
+ position: absolute;
95
+ display: none;
96
+ width: 290px;
97
+ outline: none;
98
+ z-index: 100;
99
+ top: 44px;
100
+ right: -50px;
101
+ border: 2px solid #d9d9d9;
102
+ border-radius: 2px;
103
+ @include box-shadow(inset 0px 1px 0px #475560, 0px 0px 15px rgba(0,0,0,.23));
104
+ @include transition(all .1s ease-out);
105
+ &:before {
106
+ content: '';
107
+ position: absolute;
108
+ top: -8px;
109
+ right: 60px;
110
+ background: #fff;
111
+ width: 14px;
112
+ height: 14px;
113
+ @include transform(rotate(45deg));
114
+ box-shadow: -1px -1px 1px rgba(black, .2);
115
+ @include respond-to(iphone-landscape) { display: none }
116
+ }
117
+ &.left_nipple:before { left: 30px; right: auto; }
118
+ header {
119
+ margin: 0;
120
+ padding: 5px;
121
+ background: white;
122
+ h4 {
123
+ margin: 0;
124
+ font-weight: bold;
125
+ font-size: 12px;
126
+ line-height: 1.3;
127
+ }
128
+ a.toggle_settings {
129
+ float: right;
130
+ font-size: 11px;
131
+ }
132
+ }
133
+ ol {
134
+ display: block;
135
+ margin: 0;
136
+ padding: 0;
137
+ width: 100%;
138
+ max-height:380px;
139
+ overflow: auto;
140
+ list-style: none;
141
+ background-color: #f2f2f2;
142
+ li {
143
+ display: block;
144
+ margin: 0;
145
+ padding: 5px;
146
+ font-size: 11px;
147
+ border-top: 1px solid white;
148
+ border-bottom: 1px solid #e5e5e5;
149
+ &.unread { background-color: #d3d3d3; }
150
+ a {
151
+ display: block;
152
+ overflow: auto;
153
+ padding-left: 38px;
154
+ }
155
+ img.avatar {
156
+ float: left;
157
+ margin-left: -35px;
158
+ width: 28px;
159
+ height: 28px;
160
+ }
161
+ }
162
+ }
163
+ }
164
+ &.notifications_active #notification_dropdown {
165
+ display: block;
166
+ }
167
+ }
@@ -0,0 +1,4 @@
1
+ class Eventifier::ApplicationController < ApplicationController
2
+ before_filter :authenticate_user!
3
+ layout false
4
+ end
@@ -0,0 +1,36 @@
1
+ class Eventifier::NotificationsController < Eventifier::ApplicationController
2
+ def index
3
+ @notifications = notifications
4
+ end
5
+
6
+ def touch
7
+ current_user.update_attribute :notifications_last_read_at, Time.zone.now
8
+
9
+ render :json => {'status' => 'OK'}
10
+ end
11
+
12
+ private
13
+
14
+ def notifications
15
+ scope = current_user.notifications.limit(per_page)
16
+ scope = scope.where("notifications.created_at < ?", after) if params[:after]
17
+ scope = scope.where("notifications.created_at > ?", since) if params[:since]
18
+ scope = scope.where(
19
+ "notifications.created_at > ?", current_user.notifications_last_read_at
20
+ ) if params[:recent]
21
+
22
+ scope
23
+ end
24
+
25
+ def after
26
+ Time.zone.at params[:after].to_i
27
+ end
28
+
29
+ def per_page
30
+ (params[:limit] || 5).to_i
31
+ end
32
+
33
+ def since
34
+ Time.zone.at params[:since].to_i
35
+ end
36
+ end
@@ -0,0 +1,19 @@
1
+ class Eventifier::PreferencesController < Eventifier::ApplicationController
2
+ def show
3
+ render :json => Eventifier::Preferences.new(current_user).to_hashes
4
+ end
5
+
6
+ def update
7
+ settings = Eventifier::NotificationSetting.for_user current_user
8
+ settings.preferences['email'] ||= {}
9
+ params[:preferences] ||= {}
10
+
11
+ Eventifier::Preferences.new(current_user).to_hashes.each do |hash|
12
+ settings.preferences['email'][hash[:key]] = !params[:preferences][hash[:key]].nil?
13
+ end
14
+
15
+ settings.save
16
+
17
+ render :json => {'status' => 'OK'}
18
+ end
19
+ end
@@ -0,0 +1,87 @@
1
+ module Eventifier
2
+ module NotificationHelper
3
+ # A helper method for outputting a notification message.
4
+ #
5
+ # Uses I18n messages from config/locales/events.en.yml to generate these messages, or defaults to a standard.
6
+ #
7
+ # Example:
8
+ #
9
+ # %ul#recent_notifications
10
+ # - current_user.notifications.each do |notification|
11
+ # %li= notification_message notification.event
12
+
13
+ def notification_message event
14
+ default = "notifications.default.#{event.verb}".to_sym
15
+ if event.verb.to_sym == :update
16
+ if event.change_data.keys.count == 1
17
+ key = "notifications.#{event.eventable_type.downcase}.#{event.verb}.attributes.#{event.change_data.keys.first}"
18
+ default = ["notifications.#{event.eventable_type.downcase}.#{event.verb}.single".to_sym, default]
19
+ else
20
+ key = "notifications.#{event.eventable_type.downcase}.#{event.verb}.multiple"
21
+ end
22
+ else
23
+ key = "notifications.#{event.eventable_type.downcase}.#{event.verb}"
24
+ end
25
+ message = I18n.translate key, :default => default, :"user.name" => event.user.name, :"event.type" => event.eventable_type
26
+
27
+ replace_vars(message, event).html_safe
28
+ end
29
+
30
+ def event_message event
31
+ if event.verb.to_sym == :update
32
+ if event.change_data.keys.count == 1
33
+ key = "events.#{event.eventable_type.downcase}.#{event.verb}.single"
34
+ else
35
+ key = "events.#{event.eventable_type.downcase}.#{event.verb}.multiple"
36
+ end
37
+ else
38
+ key = "events.#{event.eventable_type.downcase}.#{event.verb}"
39
+ end
40
+ message = I18n.translate key, :default => :"events.default.#{event.verb}", "user.name" => event.user.name, :"event.type" => event.eventable_type
41
+
42
+ replace_vars(message, event).html_safe
43
+ end
44
+
45
+ # Used to replace {{variables}} in I18n messages
46
+ def replace_vars message, event
47
+ event = load_event_for_template event
48
+ message.scan(/{{[^}]*}}/) do |replaceable|
49
+ method = "event."+replaceable.to_s.gsub(/[{|}]/, '').to_s
50
+ replace_text = eval(method) rescue ""
51
+
52
+ case replaceable.to_s
53
+ when "{{object.name}}"
54
+ replace_text = "<strong class='target'>#{replace_text}</strong>"
55
+ when "{{object.title}}"
56
+ replace_text = "<strong class='target'>#{replace_text}</strong>"
57
+ when "{{user.name}}"
58
+ replace_text = "<strong class='user'>#{replace_text}</strong>"
59
+ else
60
+ replace_text = "<strong>#{replace_text}</strong>"
61
+ end
62
+
63
+ message = message.gsub(replaceable.to_s, replace_text.to_s.gsub("_", " "))
64
+ end
65
+ message
66
+ end
67
+
68
+ # Make is so you can include object in the I18n descriptions and it refers to the eventable object the event is referring to.
69
+ def load_event_for_template event
70
+ def event.object; eventable; end
71
+ def event.object_type; eventable_type; end
72
+
73
+ event
74
+ end
75
+
76
+ def pastize(verb)
77
+ case verb
78
+ when 'create'
79
+ "created"
80
+ when 'update'
81
+ "changed"
82
+ when 'destroy'
83
+ "deleted"
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,15 @@
1
+ module Eventifier
2
+ module PathHelper
3
+ def render_partial_view notification, context = nil
4
+ render(partial: partial_path(notification, context), object: notification.event, locals: { notification: notification, event: notification.event, object: notification.event.eventable })
5
+ end
6
+
7
+ def partial_path notification, context = nil
8
+ if lookup_context.exists?(notification.event.eventable_type.underscore, [:eventifier, context].compact.join("/"), true)
9
+ [:eventifier, context, notification.event.eventable_type.underscore].compact.join("/")
10
+ else
11
+ [:eventifier, context, 'notification'].compact.join("/")
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,12 @@
1
+ class Eventifier::Mailer < ::ActionMailer::Base
2
+ include Eventifier::Mailers::Helpers
3
+
4
+ helper 'eventifier/notification'
5
+ helper 'eventifier/path'
6
+
7
+ default :from => Eventifier.mailer_sender
8
+
9
+ def notifications(user, records)
10
+ eventifier_mail(user, records, :notifications)
11
+ end
12
+ end
@@ -0,0 +1,3 @@
1
+ class EventTracking
2
+ #
3
+ end