user_announcements 0.0.3 → 0.0.4
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.
- data/README.md +103 -14
- data/app/controllers/admin/announcements_controller.rb +7 -3
- data/app/controllers/hidden_announcements_controller.rb +3 -4
- data/app/helpers/user_announcements/admin_helper.rb +43 -0
- data/app/helpers/user_announcements/misc_helper.rb +39 -0
- data/app/helpers/user_announcements/roles_helper.rb +44 -0
- data/app/helpers/user_announcements/show_announcements.rb +66 -0
- data/app/helpers/user_announcements/style_helper.rb +20 -0
- data/app/helpers/user_announcements_helper.rb +5 -99
- data/app/models/announcement.rb +14 -18
- data/app/models/announcement_finder.rb +68 -25
- data/app/views/admin/announcements/_announcement.html.erb +5 -4
- data/app/views/admin/announcements/_form.html.erb +15 -8
- data/app/views/admin/announcements/index.html.erb +2 -1
- data/app/views/hidden_announcements/_announcement.html.erb +2 -2
- data/lib/generators/user_announcements/install_generator.rb +2 -2
- data/lib/generators/user_announcements/templates/initializer.rb +23 -6
- data/lib/generators/user_announcements/templates/migration.rb +1 -0
- data/lib/generators/user_announcements/templates/template.css +78 -0
- data/lib/user_announcements/engine.rb +15 -10
- data/lib/user_announcements/version.rb +1 -1
- metadata +9 -6
- data/app/controllers/admin/base_controller.rb +0 -12
- data/lib/generators/user_announcements/templates/css.scss +0 -33
data/README.md
CHANGED
@@ -5,9 +5,15 @@
|
|
5
5
|
[](https://coveralls.io/r/stevedowney/user_announcements?branch=master)
|
6
6
|
[](https://codeclimate.com/github/stevedowney/user_announcements)
|
7
7
|
|
8
|
-
|
8
|
+
## Features:
|
9
9
|
|
10
|
-
|
10
|
+
* admins
|
11
|
+
* page for maintaining announcements
|
12
|
+
* announcements can be scoped by user role
|
13
|
+
* users
|
14
|
+
* hide announcements
|
15
|
+
* view past and hidden announcements
|
16
|
+
* unhide hidden announcements
|
11
17
|
|
12
18
|
### Acknowledgements
|
13
19
|
|
@@ -15,6 +21,13 @@ This gem was inspired by the [Site-Wide Announcements (revised)](http://railscas
|
|
15
21
|
episode of [RailsCasts](http://railscasts.com/). If you don't have a premium account you can see the
|
16
22
|
[original episode](http://railscasts.com/episodes/103-site-wide-announcements).
|
17
23
|
|
24
|
+
## Assumptions
|
25
|
+
|
26
|
+
* your controllers respond to `ensure_admin_user` which ensures only admin users can create/edit/delete
|
27
|
+
announcemets
|
28
|
+
* your controllers respond to `current_user`, which is also a `helper_method`
|
29
|
+
* if you implement roles, `current_user` responds to `#has_role?(<role>)`
|
30
|
+
|
18
31
|
## Installation
|
19
32
|
|
20
33
|
Add it to your Gemfile:
|
@@ -35,28 +48,53 @@ Install files:
|
|
35
48
|
rails generate user_announcements:install
|
36
49
|
```
|
37
50
|
|
38
|
-
|
39
|
-
|
40
|
-
```ruby
|
41
|
-
# ../config/initializers/user_announcements.rb
|
51
|
+
## Getting Started
|
42
52
|
|
43
|
-
|
44
|
-
# c.bootstrap = false
|
45
|
-
c.bootstrap = true
|
53
|
+
### Controller Methods Example
|
46
54
|
|
55
|
+
```ruby
|
56
|
+
class ApplicationController < ActionController::Base
|
57
|
+
protect_from_forgery
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def ensure_admin_user
|
62
|
+
current_user.has_role?('admin')
|
63
|
+
end
|
64
|
+
|
65
|
+
def current_user
|
66
|
+
@user ||= User.find(session[:user_id])
|
67
|
+
end
|
68
|
+
helper_method :current_user
|
69
|
+
|
70
|
+
end
|
47
71
|
```
|
48
72
|
|
49
|
-
|
73
|
+
### User Model Methods
|
74
|
+
|
50
75
|
|
51
|
-
|
76
|
+
```ruby
|
77
|
+
class User < ActiveRecord::Base
|
78
|
+
|
79
|
+
def has_role?(role)
|
80
|
+
return true if role.blank?
|
81
|
+
return true if role == admin && self.admin?
|
82
|
+
# ... more elaborate role checking code here?
|
83
|
+
false
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
```
|
52
88
|
|
53
|
-
|
89
|
+
### Create an Announcement
|
54
90
|
|
55
91
|
```
|
56
92
|
http://<your app>/admin/announcements
|
57
93
|
```
|
58
94
|
|
59
|
-
|
95
|
+
### View the Announcement
|
96
|
+
|
97
|
+
Add the helper method to your layout:
|
60
98
|
|
61
99
|
```erb
|
62
100
|
#../layouts/application.html.erb
|
@@ -66,8 +104,59 @@ To see the announcment add the helper method to your views, e.g.:
|
|
66
104
|
...
|
67
105
|
```
|
68
106
|
|
69
|
-
|
107
|
+
Now visit some page that uses that layout to see the announcement.
|
108
|
+
|
109
|
+
Non-admin users can see current and past announcements, including ones they have hidden,
|
110
|
+
by visiting:
|
70
111
|
|
71
112
|
```
|
72
113
|
http://<your app>/announcements
|
73
114
|
```
|
115
|
+
|
116
|
+
### URL Helpers
|
117
|
+
|
118
|
+
The following url helpers are available:
|
119
|
+
|
120
|
+
```ruby
|
121
|
+
# admin manages announcements
|
122
|
+
admin_announcements_path
|
123
|
+
|
124
|
+
# user manages own announcements
|
125
|
+
hidden_announcements_path
|
126
|
+
```
|
127
|
+
|
128
|
+
## Configuration
|
129
|
+
|
130
|
+
There are several configuration settings found in `../config/initializers/user_announcements.rb`.
|
131
|
+
|
132
|
+
```ruby
|
133
|
+
# note: all options accept lambdas
|
134
|
+
|
135
|
+
UserAnnouncements.config do |config|
|
136
|
+
|
137
|
+
# Bootstrap
|
138
|
+
config.bootstrap = true
|
139
|
+
config.styles = [['Yellow', ''], ['Red', 'alert-error'], ['Green', 'alert-success'], ['Blue', 'alert-info']]
|
140
|
+
|
141
|
+
# non-Bootstrap
|
142
|
+
# config.bootstrap = false
|
143
|
+
# config.styles = [['Yellow', 'yellow'], ['Red', 'red'], ['Green', 'green'], ['Blue', 'blue']]
|
144
|
+
|
145
|
+
# Announcement defaults
|
146
|
+
config.default_active = true
|
147
|
+
config.default_starts_at = lambda { Time.now.in_time_zone }
|
148
|
+
config.default_ends_at = lambda { 1.week.from_now.in_time_zone.end_of_day }
|
149
|
+
config.default_style = ''
|
150
|
+
# config.default_roles = ['admin']
|
151
|
+
|
152
|
+
# Roles
|
153
|
+
# config.roles = []
|
154
|
+
# config.roles = ['', 'admin']
|
155
|
+
# config.roles = [ ['Public', ''], ['Administrator', 'admin'] ]
|
156
|
+
# config.roles = lambda { MyRoleClass.map { |role| [role.name, role.id] } }
|
157
|
+
|
158
|
+
end
|
159
|
+
```
|
160
|
+
|
161
|
+
Don't forget to restart your Rails server after changes to the config file.
|
162
|
+
|
@@ -1,8 +1,9 @@
|
|
1
|
-
class Admin::AnnouncementsController <
|
1
|
+
class Admin::AnnouncementsController < ApplicationController
|
2
|
+
before_filter :ensure_admin_user
|
2
3
|
before_filter :find_announcement, :only => [:edit, :update, :destroy]
|
3
4
|
|
4
5
|
def index
|
5
|
-
@announcements = AnnouncementFinder.
|
6
|
+
@announcements = AnnouncementFinder.for_admin
|
6
7
|
end
|
7
8
|
|
8
9
|
def new
|
@@ -22,7 +23,10 @@ class Admin::AnnouncementsController < Admin::BaseController
|
|
22
23
|
end
|
23
24
|
|
24
25
|
def update
|
25
|
-
|
26
|
+
attributes = params.fetch(:announcement)
|
27
|
+
.reverse_merge(roles: [])
|
28
|
+
|
29
|
+
if @announcement.update_attributes(attributes)
|
26
30
|
redirect_to admin_announcements_path, :flash => { success: 'Announcement updated' }
|
27
31
|
else
|
28
32
|
render "edit"
|
@@ -1,8 +1,7 @@
|
|
1
1
|
class HiddenAnnouncementsController < ApplicationController
|
2
|
-
|
3
|
-
|
2
|
+
|
4
3
|
def index
|
5
|
-
@announcements = AnnouncementFinder.
|
4
|
+
@announcements = AnnouncementFinder.for_edit(current_user)
|
6
5
|
@hidden_announcement_ids = HiddenAnnouncement.hidden_announcement_ids_for(current_user.id)
|
7
6
|
end
|
8
7
|
|
@@ -19,5 +18,5 @@ class HiddenAnnouncementsController < ApplicationController
|
|
19
18
|
HiddenAnnouncement.delete_all(user_id: current_user.id, announcement_id: params.fetch(:announcement_id))
|
20
19
|
redirect_to action: 'index'
|
21
20
|
end
|
22
|
-
|
21
|
+
|
23
22
|
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module UserAnnouncements::AdminHelper
|
2
|
+
|
3
|
+
def _ua_active_model_errors(model)
|
4
|
+
return unless model.errors.present?
|
5
|
+
|
6
|
+
content_tag(:div, class: ['alert', 'alert-error']) do
|
7
|
+
content_tag(:h4, "Correct the following errors:") +
|
8
|
+
content_tag(:ul) do
|
9
|
+
safe_join(model.errors.full_messages.map { |msg| content_tag(:li, msg) }, "\n")
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def ua_table_tag(options={})
|
15
|
+
content_tag(:table, options.merge(ua_table_attrs)) do
|
16
|
+
yield
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def boolean_display(boolean)
|
21
|
+
( boolean ? '✔' : ' ' ).html_safe
|
22
|
+
end
|
23
|
+
|
24
|
+
def ua_datetime_p(f, method)
|
25
|
+
content_tag(:p, class: 'datetime-select') do
|
26
|
+
f.label(method) +
|
27
|
+
ua_br +
|
28
|
+
f.datetime_select(method)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def ua_table_attrs
|
35
|
+
if ua_bootstrap?
|
36
|
+
{class: "ua-table bootstrap table table-striped table-bordered table-hover"}
|
37
|
+
else
|
38
|
+
{class: 'ua-table non-bootstrap'}
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module UserAnnouncements::MiscHelper
|
2
|
+
|
3
|
+
def ua_datetime_display(datetime)
|
4
|
+
if datetime.present?
|
5
|
+
datetime.to_s(:short)
|
6
|
+
else
|
7
|
+
nil
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
# Return a <br> tag if not bootstrap
|
12
|
+
def ua_br
|
13
|
+
"<br />".html_safe unless ua_bootstrap?
|
14
|
+
end
|
15
|
+
|
16
|
+
def flash_messages
|
17
|
+
return nil if flash.empty?
|
18
|
+
result = []
|
19
|
+
flash.each do |name, msg|
|
20
|
+
result << build_message(name, msg)
|
21
|
+
end
|
22
|
+
result.join("").html_safe
|
23
|
+
end
|
24
|
+
|
25
|
+
def build_message(msg_type, msg)
|
26
|
+
content_tag(:div, :class => "alert alert-#{msg_type.to_s}") do
|
27
|
+
content_tag(:a, 'x', :class => "close", "data-dismiss" => "alert") + msg.html_safe
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def ua_bootstrap?
|
32
|
+
if params.has_key?(:bootstrap)
|
33
|
+
params[:bootstrap] == 'true'
|
34
|
+
else
|
35
|
+
UserAnnouncements.config.bootstrap
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module UserAnnouncements::RolesHelper
|
2
|
+
Role = Struct.new(:name, :id)
|
3
|
+
|
4
|
+
# Roles checkboxes for admin detail form
|
5
|
+
def ua_roles_html(f)
|
6
|
+
return unless _ua_roles.present?
|
7
|
+
|
8
|
+
f.label(:roles) +
|
9
|
+
ua_br +
|
10
|
+
_ua_roles_checkboxes(_ua_roles)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Roles display on index page
|
14
|
+
def ua_roles_display(announcement)
|
15
|
+
role_names = announcement.roles.map { |role_id| _ua_role_id_to_name(role_id) }
|
16
|
+
|
17
|
+
safe_join(role_names, '<br />'.html_safe)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def _ua_roles_checkboxes(roles)
|
23
|
+
safe_join( roles.map { |role| _ua_role_checkbox(role) } )
|
24
|
+
end
|
25
|
+
|
26
|
+
def _ua_role_checkbox(role)
|
27
|
+
id = "role_#{role.id}"
|
28
|
+
checked = @announcement.roles.include?(role.id)
|
29
|
+
|
30
|
+
check_box_tag('announcement[roles][]', role.id, checked, id: id) + ' ' +
|
31
|
+
label_tag(id, role.name, style: "display: inline; margin-right: 1em")
|
32
|
+
end
|
33
|
+
|
34
|
+
def _ua_role_id_to_name(role_id)
|
35
|
+
_ua_roles.detect { |role| role.id == role_id }.try(:name)
|
36
|
+
end
|
37
|
+
|
38
|
+
def _ua_roles
|
39
|
+
@_ua_roles ||= Array(UserAnnouncements[:roles]).map do |role|
|
40
|
+
name_id = (role.is_a?(Array) ? role : [role, role]).map(&:to_s)
|
41
|
+
Role.new(name_id.first, name_id.last)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module UserAnnouncements::ShowAnnouncements
|
2
|
+
|
3
|
+
# Helper method for displaying messages for +current_user+.
|
4
|
+
def user_announcements(options={})
|
5
|
+
return if controller.controller_name == 'hidden_announcements'
|
6
|
+
|
7
|
+
announcements_rows = AnnouncementFinder.for_display(current_user)
|
8
|
+
divs = announcements_rows.map do |announcement|
|
9
|
+
_ua_announcement_div(announcement)
|
10
|
+
end
|
11
|
+
|
12
|
+
safe_join(divs, "\n")
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def _ua_announcement_div(ann)
|
18
|
+
if ua_bootstrap?
|
19
|
+
_ua_announcement_div_bootstrap(ann)
|
20
|
+
else
|
21
|
+
_ua_announcement_div_non_bootstrap(ann)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def _ua_announcement_div_bootstrap(ann)
|
26
|
+
div_for ann, class: _ua_announcement_classes(ann.style), style: 'width:40em' do
|
27
|
+
link_to(*_ua_hide_announcement_link_args(ann)) do
|
28
|
+
content_tag(:button, raw('×'), type: 'button', class: 'close')
|
29
|
+
end +
|
30
|
+
ann.message.html_safe
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def _ua_announcement_div_non_bootstrap(ann)
|
35
|
+
div_for(ann, class: _ua_announcement_classes(ann.style)) do
|
36
|
+
ann.message.html_safe +
|
37
|
+
link_to("hide announcement", *_ua_hide_announcement_link_args(ann))
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def _ua_hide_announcement_link_args(announcement)
|
42
|
+
url = hidden_announcements_path(announcement_id: announcement)
|
43
|
+
options = {
|
44
|
+
method: :post,
|
45
|
+
remote: true,
|
46
|
+
id: "hide_#{dom_id(announcement)}"
|
47
|
+
}
|
48
|
+
|
49
|
+
[url, options]
|
50
|
+
end
|
51
|
+
|
52
|
+
def _ua_unhide_announcement_link(announcement)
|
53
|
+
if @hidden_announcement_ids.include?(announcement.id)
|
54
|
+
content_tag(:div) do
|
55
|
+
link_to('Unhide', hidden_announcement_path(announcement, announcement_id: announcement), method: :delete)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
def _ua_announcement_classes(style)
|
62
|
+
bootstrap_or_non = ua_bootstrap? ? 'bootstrap alert' : 'non-bootstrap'
|
63
|
+
[bootstrap_or_non, style.presence].compact.join(' ')
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module UserAnnouncements::StyleHelper
|
2
|
+
|
3
|
+
# Style dropdown for admin detail form
|
4
|
+
def ua_style_html(f)
|
5
|
+
return unless _ua_styles.present?
|
6
|
+
|
7
|
+
f.label(:style) +
|
8
|
+
ua_br +
|
9
|
+
f.select(:style, _ua_styles)
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def _ua_styles
|
15
|
+
@_ua_styles ||= UserAnnouncements[:styles].map do |style|
|
16
|
+
(style.is_a?(Array) ? style : [style, style]).map(&:to_s)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -1,101 +1,7 @@
|
|
1
1
|
module UserAnnouncementsHelper
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
announcements_rows = AnnouncementFinder.current_for_user(current_user)
|
8
|
-
divs = announcements_rows.map do |announcement|
|
9
|
-
announcement_div(announcement)
|
10
|
-
end
|
11
|
-
|
12
|
-
safe_join(divs, "\n")
|
13
|
-
end
|
14
|
-
|
15
|
-
def announcement_div(announcement)
|
16
|
-
if bootstrap?
|
17
|
-
announcement_div_bootstrap(announcement)
|
18
|
-
else
|
19
|
-
announcement_div_non_bootstrap(announcement)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
def announcement_div_bootstrap(announcement)
|
24
|
-
div_for announcement, class: 'alert', style: 'width:40em' do
|
25
|
-
link_to(*hide_announcement_link_args(announcement)) do
|
26
|
-
content_tag(:button, raw('×'), type: 'button', class: 'close')
|
27
|
-
end +
|
28
|
-
announcement.message.html_safe
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
def hide_announcement_link_args(announcement)
|
33
|
-
url = hidden_announcements_path(announcement_id: announcement)
|
34
|
-
options = {
|
35
|
-
method: :post,
|
36
|
-
remote: true,
|
37
|
-
id: "hide_#{dom_id(announcement)}"
|
38
|
-
}
|
39
|
-
|
40
|
-
[url, options]
|
41
|
-
end
|
42
|
-
|
43
|
-
def announcement_div_non_bootstrap(announcement)
|
44
|
-
div_for(announcement, class: 'non-bootstrap') do
|
45
|
-
announcement.message.html_safe +
|
46
|
-
link_to("hide announcement", *hide_announcement_link_args(announcement))
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
def unhide_announcement_link(announcement)
|
51
|
-
if @hidden_announcement_ids.include?(announcement.id)
|
52
|
-
content_tag(:div) do
|
53
|
-
link_to('Unhide', hidden_announcement_path(announcement, announcement_id: announcement), method: :delete)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
def ua_table_attrs
|
59
|
-
if bootstrap?
|
60
|
-
{class: "ua-table bootstrap table table-striped table-bordered table-condensed table-hover"}
|
61
|
-
else
|
62
|
-
{class: 'ua-table non-bootstrap'}
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
def ua_table_tag(options={})
|
67
|
-
content_tag(:table, options.merge(ua_table_attrs)) do
|
68
|
-
yield
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
def boolean_display(boolean)
|
73
|
-
( boolean ? '✔' : ' ' ).html_safe
|
74
|
-
end
|
75
|
-
|
76
|
-
def active_model_errors(model)
|
77
|
-
return unless model.errors.present?
|
78
|
-
|
79
|
-
content_tag(:div, class: ['alert', 'alert-error']) do
|
80
|
-
content_tag(:h4, "Correct the following errors:") +
|
81
|
-
content_tag(:ul) do
|
82
|
-
safe_join(model.errors.full_messages.map { |msg| content_tag(:li, msg) }, "\n")
|
83
|
-
end
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
def ua_datetime_p(f, method)
|
88
|
-
content_tag(:p, class: 'datetime-select') do
|
89
|
-
f.label(method) +
|
90
|
-
f.datetime_select(method)
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
def bootstrap?
|
95
|
-
if params.has_key?(:bootstrap)
|
96
|
-
params[:bootstrap] == 'true'
|
97
|
-
else
|
98
|
-
UserAnnouncements.config.bootstrap
|
99
|
-
end
|
100
|
-
end
|
2
|
+
include UserAnnouncements::AdminHelper
|
3
|
+
include UserAnnouncements::RolesHelper
|
4
|
+
include UserAnnouncements::MiscHelper
|
5
|
+
include UserAnnouncements::StyleHelper
|
6
|
+
include UserAnnouncements::ShowAnnouncements
|
101
7
|
end
|
data/app/models/announcement.rb
CHANGED
@@ -1,9 +1,12 @@
|
|
1
1
|
class Announcement < ActiveRecord::Base
|
2
|
-
|
2
|
+
serialize :roles, class_name = Array
|
3
|
+
serialize :types, class_name = Array
|
4
|
+
|
5
|
+
attr_accessible :message, :starts_at, :ends_at, :active, :roles, :style
|
3
6
|
|
4
7
|
has_many :hidden_announcements, :dependent => :destroy
|
5
8
|
|
6
|
-
validates_presence_of :message
|
9
|
+
validates_presence_of :message
|
7
10
|
|
8
11
|
INACTIVE = 'inactive'
|
9
12
|
PAST = 'past'
|
@@ -23,6 +26,10 @@ class Announcement < ActiveRecord::Base
|
|
23
26
|
status == CURRENT
|
24
27
|
end
|
25
28
|
|
29
|
+
def starts_at_for_user
|
30
|
+
starts_at || created_at
|
31
|
+
end
|
32
|
+
|
26
33
|
def status_order
|
27
34
|
{ FUTURE => 1, CURRENT => 2, PAST => 3, INACTIVE => 4 }[status]
|
28
35
|
end
|
@@ -35,24 +42,13 @@ class Announcement < ActiveRecord::Base
|
|
35
42
|
|
36
43
|
def new_with_defaults
|
37
44
|
new do |ann|
|
38
|
-
ann.active =
|
39
|
-
ann.starts_at =
|
40
|
-
ann.ends_at =
|
45
|
+
ann.active = UserAnnouncements[:default_active]
|
46
|
+
ann.starts_at = UserAnnouncements[:default_starts_at]
|
47
|
+
ann.ends_at = UserAnnouncements[:default_ends_at]
|
48
|
+
ann.roles = Array(UserAnnouncements[:default_roles])
|
49
|
+
ann.style = UserAnnouncements[:default_style]
|
41
50
|
end
|
42
51
|
end
|
43
52
|
|
44
|
-
private
|
45
|
-
|
46
|
-
def get_default(column)
|
47
|
-
config_key = "default_#{column}"
|
48
|
-
default = UserAnnouncements.config.send(config_key)
|
49
|
-
|
50
|
-
if default.is_a?(Proc)
|
51
|
-
default.call
|
52
|
-
else
|
53
|
-
default
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
53
|
end
|
58
54
|
end
|
@@ -1,44 +1,87 @@
|
|
1
|
+
# Class to handle fetching +Announcement+ rows for various use cases.
|
1
2
|
class AnnouncementFinder
|
2
3
|
|
3
4
|
class << self
|
4
5
|
|
5
|
-
|
6
|
-
|
6
|
+
# Returns all +Announcement+s for admin edit page.
|
7
|
+
#
|
8
|
+
# @return [ActiveRecord::Relation<Announcement>]
|
9
|
+
def for_admin
|
10
|
+
Announcement
|
11
|
+
.all
|
12
|
+
.sort_by(&:status_order)
|
7
13
|
end
|
8
14
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
15
|
+
# Returns unhidden +Announcement+s that _user_ may see.
|
16
|
+
#
|
17
|
+
# @param user [User]
|
18
|
+
# @return [Array<Announcement>]
|
19
|
+
def for_display(user)
|
20
|
+
result = current
|
21
|
+
result = remove_hidden(user, result)
|
22
|
+
result = filter_by_role(user, result)
|
23
|
+
result
|
14
24
|
end
|
15
25
|
|
16
|
-
|
17
|
-
|
18
|
-
|
26
|
+
# Returns +Announcement+s that _user_ may edit.
|
27
|
+
#
|
28
|
+
# @param user [User]
|
29
|
+
# @return [ActiveRecord::Relation<Announcement>]
|
30
|
+
def for_edit(user)
|
31
|
+
Announcement
|
19
32
|
.active
|
20
33
|
.where("(starts_at is null or starts_at < :now)", :now => DateTime.now)
|
21
34
|
.order('created_at desc')
|
22
|
-
Rails.logger.debug '***** end past_or_current'
|
23
|
-
# sleep 1
|
24
|
-
r
|
25
|
-
end
|
26
|
-
|
27
|
-
def current_for_user(user)
|
28
|
-
for_user(user, current)
|
29
35
|
end
|
30
36
|
|
31
|
-
|
32
|
-
# # FIXME: apply roles
|
33
|
-
# past_or_current
|
34
|
-
# end
|
37
|
+
private
|
35
38
|
|
36
|
-
|
39
|
+
# Returns current +Announcement+s
|
40
|
+
#
|
41
|
+
# @return [ActiveRecord::Relation<Announcement>]
|
42
|
+
def current
|
43
|
+
Announcement
|
44
|
+
.active
|
45
|
+
.where("starts_at is null or starts_at < :now", now: DateTime.now)
|
46
|
+
.where("ends_at is null or ends_at > :now", now: DateTime.now)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Removes any +Announcement+s from _relation_ that _user_ has hidden.
|
50
|
+
#
|
51
|
+
# @param user [User]
|
52
|
+
# @param relation [ActiveRecord::Relation<Announcement>]
|
53
|
+
# @return [ActiveRecord::Relation<Announcement>]
|
54
|
+
def remove_hidden(user, relation)
|
37
55
|
hidden_announcement_ids = HiddenAnnouncement.hidden_announcement_ids_for(user.id)
|
38
|
-
|
39
|
-
|
40
|
-
|
56
|
+
|
57
|
+
if hidden_announcement_ids.present?
|
58
|
+
relation.where("id not in (?)", hidden_announcement_ids)
|
59
|
+
else
|
60
|
+
relation
|
61
|
+
end
|
41
62
|
end
|
63
|
+
|
64
|
+
# Removes any +Announcement+s from _relation_ that _user_ can't see.
|
65
|
+
#
|
66
|
+
# @param user [User]
|
67
|
+
# @param relation [ActiveRecord::Relation<Announcement>]
|
68
|
+
def filter_by_role(user, relation)
|
69
|
+
relation.select { |announcement| user_can_see(user, announcement.roles) }
|
70
|
+
end
|
71
|
+
|
72
|
+
# Returns +true+ if _user_ can see announcement based on _roles_.
|
73
|
+
#
|
74
|
+
# @param user [User, #has_role?]
|
75
|
+
# @param roles [Array<String>]
|
76
|
+
# @return [Boolean]
|
77
|
+
def user_can_see(user, roles)
|
78
|
+
if roles.detect(&:blank?) || roles.blank?
|
79
|
+
true
|
80
|
+
else
|
81
|
+
roles.any? { |role| user.has_role?(role) }
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
42
85
|
end
|
43
86
|
|
44
87
|
end
|
@@ -1,9 +1,10 @@
|
|
1
1
|
<tr id="<%= dom_id(announcement) %>" class='<%= announcement.status %>'>
|
2
2
|
<td><%=raw announcement.message %></td>
|
3
|
-
<td class='
|
4
|
-
<td class='
|
5
|
-
<td><%= announcement.status %></td>
|
6
|
-
<td><%= boolean_display(announcement.active)%></td>
|
3
|
+
<td class='datetime'><%= ua_datetime_display(announcement.starts_at) %></td>
|
4
|
+
<td class='datetime'><%= ua_datetime_display(announcement.ends_at) %></td>
|
5
|
+
<td class='status'><%= announcement.status %></td>
|
6
|
+
<td class='active'><%= boolean_display(announcement.active)%></td>
|
7
|
+
<td><%= ua_roles_display(announcement) %></td>
|
7
8
|
<td><%= link_to('edit', edit_admin_announcement_path(announcement), :class => 'btn btn-mini') %></td>
|
8
9
|
<td><%= link_to('delete', admin_announcement_path(announcement), :data => {confirm: 'Are you sure?'}, :method => :delete, :class => 'btn btn-mini btn-danger') %></td>
|
9
10
|
</tr>
|
@@ -1,10 +1,14 @@
|
|
1
|
-
<%= form_for [:admin, @announcement], :html => {:class => 'form-horizontal'} do |f| %>
|
2
|
-
<%=
|
3
|
-
|
4
|
-
|
5
|
-
|
1
|
+
<%= form_for [:admin, @announcement], :html => {:class => 'form-horizontal announcement-detail'} do |f| %>
|
2
|
+
<%= _ua_active_model_errors(f.object) %>
|
3
|
+
|
4
|
+
<p>
|
5
|
+
<%= f.check_box(:active) %>
|
6
|
+
<%= f.label :active %>
|
7
|
+
</p>
|
8
|
+
|
6
9
|
<p>
|
7
10
|
<%= f.label(:message) %>
|
11
|
+
<%= ua_br %>
|
8
12
|
<%= f.text_area :message, size: "60x10", class: 'span6' %>
|
9
13
|
</p>
|
10
14
|
|
@@ -13,10 +17,13 @@
|
|
13
17
|
<%= ua_datetime_p(f, :ends_at) %>
|
14
18
|
|
15
19
|
<p>
|
16
|
-
<%= f
|
17
|
-
<%= f.label :active %>
|
20
|
+
<%= ua_roles_html(f) %>
|
18
21
|
</p>
|
19
|
-
|
22
|
+
|
23
|
+
<p>
|
24
|
+
<%= ua_style_html(f) %>
|
25
|
+
</p>
|
26
|
+
|
20
27
|
<div class="form-actions">
|
21
28
|
<%= f.submit :class => 'btn btn-primary' %>
|
22
29
|
<%= link_to("Cancel", admin_announcements_path, :class => 'btn') %>
|
@@ -1,8 +1,8 @@
|
|
1
1
|
|
2
2
|
<tr id="<%= dom_id(announcement) %>">
|
3
|
-
<td class='
|
3
|
+
<td class='datetime'><%= ua_datetime_display(announcement.starts_at_for_user) %></td>
|
4
4
|
<td>
|
5
5
|
<%= announcement.message.html_safe %>
|
6
|
-
<%=
|
6
|
+
<%= _ua_unhide_announcement_link(announcement) %>
|
7
7
|
</td>
|
8
8
|
</tr>
|
@@ -9,14 +9,14 @@ module UserAnnouncements
|
|
9
9
|
|
10
10
|
desc <<DESC
|
11
11
|
Description:
|
12
|
-
Copies stylesheet to 'app/assets/stylesheets/user_announcements.css
|
12
|
+
Copies stylesheet to 'app/assets/stylesheets/user_announcements.css'
|
13
13
|
Copies configuration to 'config/initializers/user_announcements.rb'
|
14
14
|
Copies db migration to 'db/migrate/<timestamp>/create_user_announcement_tables.rb'
|
15
15
|
|
16
16
|
DESC
|
17
17
|
|
18
18
|
def install
|
19
|
-
copy_file "css
|
19
|
+
copy_file "template.css", 'app/assets/stylesheets/user_announcements.css'
|
20
20
|
copy_file "initializer.rb", 'config/initializers/user_announcements.rb'
|
21
21
|
migration_template "migration.rb", "db/migrate/create_user_announcement_tables.rb"
|
22
22
|
end
|
@@ -1,9 +1,26 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
# note: all options accept lambdas
|
2
|
+
|
3
|
+
UserAnnouncements.config do |config|
|
4
|
+
|
5
|
+
# Bootstrap
|
6
|
+
config.bootstrap = true
|
7
|
+
config.styles = [['Yellow', ''], ['Red', 'alert-error'], ['Green', 'alert-success'], ['Blue', 'alert-info']]
|
8
|
+
|
9
|
+
# non-Bootstrap
|
10
|
+
# config.bootstrap = false
|
11
|
+
# config.styles = [['Yellow', 'yellow'], ['Red', 'red'], ['Green', 'green'], ['Blue', 'blue']]
|
12
|
+
|
13
|
+
# Announcement defaults
|
14
|
+
config.default_active = true
|
15
|
+
config.default_starts_at = lambda { Time.now.in_time_zone }
|
16
|
+
config.default_ends_at = lambda { 1.week.from_now.in_time_zone.end_of_day }
|
17
|
+
config.default_style = ''
|
18
|
+
# config.default_roles = ['admin']
|
4
19
|
|
5
|
-
#
|
6
|
-
# config.
|
7
|
-
# config.
|
20
|
+
# Roles
|
21
|
+
# config.roles = []
|
22
|
+
# config.roles = ['', 'admin']
|
23
|
+
# config.roles = [ ['Public', ''], ['Administrator', 'admin'] ]
|
24
|
+
# config.roles = lambda { MyRoleClass.map { |role| [role.name, role.id] } }
|
8
25
|
|
9
26
|
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
table.ua-table td.status {
|
2
|
+
width: 1%;
|
3
|
+
text-align: center;
|
4
|
+
}
|
5
|
+
|
6
|
+
table.ua-table th.active {
|
7
|
+
width: 1%;
|
8
|
+
}
|
9
|
+
|
10
|
+
table.ua-table td.active {
|
11
|
+
text-align: center;
|
12
|
+
}
|
13
|
+
|
14
|
+
table.ua-table td.datetime {
|
15
|
+
width: 6em;
|
16
|
+
white-space: nowrap;
|
17
|
+
}
|
18
|
+
|
19
|
+
table.ua-table th.roles {
|
20
|
+
width: 1%;
|
21
|
+
}
|
22
|
+
|
23
|
+
table.ua-table.non-bootstrap {
|
24
|
+
border-collapse:collapse;
|
25
|
+
}
|
26
|
+
|
27
|
+
table.ua-table.non-bootstrap th, table.ua-table.non-bootstrap td {
|
28
|
+
border: 1px solid black;
|
29
|
+
padding: 0.5em;
|
30
|
+
}
|
31
|
+
|
32
|
+
table.ua-table td {
|
33
|
+
vertical-align: top;
|
34
|
+
}
|
35
|
+
|
36
|
+
.announcement.non-bootstrap {
|
37
|
+
background-color: #fcf8e3;
|
38
|
+
color: #c09853;
|
39
|
+
border-bottom: solid 1px black;
|
40
|
+
width: 100%;
|
41
|
+
padding: 10px;
|
42
|
+
text-align: center;
|
43
|
+
}
|
44
|
+
|
45
|
+
|
46
|
+
.announcement.non-bootstrap a {
|
47
|
+
font-size: 12px;
|
48
|
+
margin-left: 5px;
|
49
|
+
color: #6E5910;
|
50
|
+
}
|
51
|
+
}
|
52
|
+
|
53
|
+
.announcement.non-bootstrap.yellow {
|
54
|
+
background-color: #fcf8e3;
|
55
|
+
color: #c09853;
|
56
|
+
}
|
57
|
+
|
58
|
+
.announcement.non-bootstrap.red {
|
59
|
+
background-color: #f2dede;
|
60
|
+
/* border-color: #eed3d7;*/
|
61
|
+
color: #b94a48;
|
62
|
+
}
|
63
|
+
|
64
|
+
.announcement.non-bootstrap.green {
|
65
|
+
background-color: #dff0d8;
|
66
|
+
/* border-color: #d6e9c6;*/
|
67
|
+
color: #468847;
|
68
|
+
}
|
69
|
+
|
70
|
+
.announcement.non-bootstrap.blue {
|
71
|
+
background-color: #d9edf7;
|
72
|
+
/* border-color: #bce8f1;*/
|
73
|
+
color: #3a87ad;
|
74
|
+
}
|
75
|
+
|
76
|
+
form.announcement-detail label{
|
77
|
+
margin-bottom: 0.4em;
|
78
|
+
}
|
@@ -1,23 +1,28 @@
|
|
1
1
|
module UserAnnouncements
|
2
2
|
|
3
3
|
class Engine < ::Rails::Engine
|
4
|
+
|
4
5
|
config.generators.integration_tool :rspec
|
5
6
|
config.generators.test_framework :rspec
|
6
7
|
|
7
|
-
config.bootstrap = true
|
8
|
-
|
9
|
-
config.default_active = true
|
10
|
-
config.default_starts_at = lambda { Time.now.in_time_zone }
|
11
|
-
config.default_ends_at = lambda { 1.week.from_now.in_time_zone.end_of_day }
|
12
|
-
|
13
|
-
config.roles = []
|
14
|
-
config.types = []
|
15
|
-
config.styles = %w(error succes info)
|
16
|
-
|
17
8
|
end
|
18
9
|
|
19
10
|
def self.config(&block)
|
20
11
|
yield Engine.config if block
|
21
12
|
Engine.config
|
22
13
|
end
|
14
|
+
|
15
|
+
def self.[](key)
|
16
|
+
setting = config.send(key)
|
17
|
+
|
18
|
+
if setting.is_a?(Proc)
|
19
|
+
setting.call
|
20
|
+
else
|
21
|
+
setting
|
22
|
+
end
|
23
|
+
|
24
|
+
rescue NameError
|
25
|
+
Rails.logger.debug "[UserAnnouncements] Tried to access unknown UserAnnouncements.config key: #{key.inspect}"
|
26
|
+
nil
|
27
|
+
end
|
23
28
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: user_announcements
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-05-
|
12
|
+
date: 2013-05-26 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
@@ -339,8 +339,12 @@ extensions: []
|
|
339
339
|
extra_rdoc_files: []
|
340
340
|
files:
|
341
341
|
- app/controllers/admin/announcements_controller.rb
|
342
|
-
- app/controllers/admin/base_controller.rb
|
343
342
|
- app/controllers/hidden_announcements_controller.rb
|
343
|
+
- app/helpers/user_announcements/admin_helper.rb
|
344
|
+
- app/helpers/user_announcements/misc_helper.rb
|
345
|
+
- app/helpers/user_announcements/roles_helper.rb
|
346
|
+
- app/helpers/user_announcements/show_announcements.rb
|
347
|
+
- app/helpers/user_announcements/style_helper.rb
|
344
348
|
- app/helpers/user_announcements_helper.rb
|
345
349
|
- app/models/announcement.rb
|
346
350
|
- app/models/announcement_finder.rb
|
@@ -355,9 +359,9 @@ files:
|
|
355
359
|
- app/views/hidden_announcements/index.html.erb
|
356
360
|
- config/routes.rb
|
357
361
|
- lib/generators/user_announcements/install_generator.rb
|
358
|
-
- lib/generators/user_announcements/templates/css.scss
|
359
362
|
- lib/generators/user_announcements/templates/initializer.rb
|
360
363
|
- lib/generators/user_announcements/templates/migration.rb
|
364
|
+
- lib/generators/user_announcements/templates/template.css
|
361
365
|
- lib/tasks/user_announcements_tasks.rake
|
362
366
|
- lib/user_announcements/engine.rb
|
363
367
|
- lib/user_announcements/version.rb
|
@@ -389,7 +393,6 @@ rubyforge_project:
|
|
389
393
|
rubygems_version: 1.8.25
|
390
394
|
signing_key:
|
391
395
|
specification_version: 3
|
392
|
-
summary: Manage and display site-wide announcements by user,
|
393
|
-
type.
|
396
|
+
summary: Manage and display site-wide announcements by user, scoped by user role.
|
394
397
|
test_files: []
|
395
398
|
has_rdoc:
|
@@ -1,33 +0,0 @@
|
|
1
|
-
table.ua-table {
|
2
|
-
|
3
|
-
td.date {
|
4
|
-
white-space: nowrap;
|
5
|
-
}
|
6
|
-
|
7
|
-
&.non-bootstrap {
|
8
|
-
border: 1px solid black;
|
9
|
-
|
10
|
-
th, td {
|
11
|
-
border: 1px solid black;
|
12
|
-
padding: 0.5em;
|
13
|
-
}
|
14
|
-
|
15
|
-
td {
|
16
|
-
vertical-align: top;
|
17
|
-
}
|
18
|
-
|
19
|
-
}
|
20
|
-
}
|
21
|
-
|
22
|
-
.announcement.non-bootstrap {
|
23
|
-
width: 100%;
|
24
|
-
background-color: #F1F094;
|
25
|
-
border-bottom: solid 1px black;
|
26
|
-
padding: 10px;
|
27
|
-
text-align: center;
|
28
|
-
a {
|
29
|
-
font-size: 12px;
|
30
|
-
margin-left: 5px;
|
31
|
-
color: #6E5910;
|
32
|
-
}
|
33
|
-
}
|