radiant-reader-extension 1.3.13 → 2.0.0.rc4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (107) hide show
  1. data/README.md +2 -2
  2. data/Rakefile +0 -19
  3. data/app/controllers/admin/group_invitations_controller.rb +78 -0
  4. data/app/controllers/admin/groups_controller.rb +8 -0
  5. data/app/controllers/admin/memberships_controller.rb +42 -0
  6. data/app/controllers/admin/messages_controller.rb +8 -3
  7. data/app/controllers/admin/permissions_controller.rb +42 -0
  8. data/app/controllers/admin/reader_configuration_controller.rb +0 -1
  9. data/app/controllers/admin/readers_controller.rb +1 -1
  10. data/app/controllers/password_resets_controller.rb +5 -5
  11. data/app/controllers/reader_action_controller.rb +13 -33
  12. data/app/controllers/reader_activations_controller.rb +4 -8
  13. data/app/controllers/reader_sessions_controller.rb +15 -26
  14. data/app/controllers/readers_controller.rb +26 -9
  15. data/app/helpers/reader_helper.rb +45 -17
  16. data/app/models/group.rb +80 -0
  17. data/app/models/membership.rb +13 -0
  18. data/app/models/message.rb +10 -7
  19. data/app/models/permission.rb +11 -0
  20. data/app/models/reader.rb +79 -35
  21. data/app/models/reader_notifier.rb +1 -0
  22. data/app/views/admin/group_invitations/new.html.haml +31 -0
  23. data/app/views/admin/group_invitations/preview.html.haml +58 -0
  24. data/app/views/admin/groups/_add_readers.html.haml +0 -0
  25. data/app/views/admin/groups/_form.html.haml +26 -0
  26. data/app/views/admin/groups/_list_head.html.haml +12 -0
  27. data/app/views/admin/groups/edit.html.haml +9 -0
  28. data/app/views/admin/groups/index.html.haml +44 -0
  29. data/app/views/admin/groups/new.html.haml +7 -0
  30. data/app/views/admin/groups/remove.html.haml +31 -0
  31. data/app/views/admin/groups/show.html.haml +74 -0
  32. data/app/views/admin/memberships/_reader.html.haml +9 -0
  33. data/app/views/admin/messages/_function.haml +31 -0
  34. data/app/views/admin/messages/_list_function.haml +7 -3
  35. data/app/views/admin/messages/_list_notes.html.haml +9 -0
  36. data/app/views/admin/messages/_message_description.html.haml +5 -1
  37. data/app/views/admin/messages/_message_group.html.haml +5 -0
  38. data/app/views/admin/messages/index.haml +4 -4
  39. data/app/views/admin/messages/show.html.haml +15 -15
  40. data/app/views/admin/pages/_listed.html.haml +16 -0
  41. data/app/views/admin/pages/_page_groups.html.haml +13 -0
  42. data/app/views/admin/permissions/_page.html.haml +24 -0
  43. data/app/views/admin/reader_configuration/edit.html.haml +3 -1
  44. data/app/views/admin/reader_configuration/show.html.haml +4 -2
  45. data/app/views/admin/readers/_form.html.haml +7 -7
  46. data/app/views/admin/readers/_password_fields.html.haml +6 -6
  47. data/app/views/admin/readers/_reader_groups.html.haml +7 -0
  48. data/app/views/admin/readers/edit.html.haml +2 -1
  49. data/app/views/admin/readers/index.html.haml +5 -2
  50. data/app/views/password_resets/create.html.haml +13 -8
  51. data/app/views/password_resets/edit.html.haml +26 -26
  52. data/app/views/password_resets/new.html.haml +28 -25
  53. data/app/views/reader_activations/_activation_required.haml +2 -2
  54. data/app/views/reader_activations/show.html.haml +11 -13
  55. data/app/views/reader_sessions/_login_form.html.haml +13 -12
  56. data/app/views/reader_sessions/new.html.haml +6 -8
  57. data/app/views/readers/_controls.html.haml +11 -9
  58. data/app/views/readers/_form.html.haml +32 -33
  59. data/app/views/readers/_memberships.html.haml +11 -0
  60. data/app/views/readers/edit.html.haml +11 -11
  61. data/app/views/readers/index.html.haml +9 -10
  62. data/app/views/readers/login.html.haml +10 -12
  63. data/app/views/readers/new.html.haml +11 -13
  64. data/app/views/readers/permission_denied.html.haml +7 -7
  65. data/app/views/readers/show.html.haml +7 -8
  66. data/app/views/shared/_standard_reader_parts.html.haml +14 -0
  67. data/app/views/site/not_allowed.html.haml +4 -0
  68. data/config/initializers/authlogic_connect_config.rb +12 -0
  69. data/config/initializers/radiant_config.rb +1 -0
  70. data/config/locales/en.yml +217 -177
  71. data/config/routes.rb +5 -0
  72. data/db/migrate/20090921125654_group_messages.rb +35 -0
  73. data/db/migrate/20091120083119_groups_public.rb +11 -0
  74. data/db/migrate/20110214101339_multiple_ownership.rb +13 -0
  75. data/lib/controller_extensions.rb +1 -1
  76. data/lib/group_tags.rb +65 -0
  77. data/lib/grouped_model.rb +125 -0
  78. data/lib/grouped_page.rb +39 -0
  79. data/lib/message_tags.rb +183 -0
  80. data/lib/radiant-reader-extension.rb +8 -0
  81. data/lib/reader_admin_ui.rb +29 -6
  82. data/lib/reader_tags.rb +7 -183
  83. data/lib/sanitize/config/generous.rb +49 -0
  84. data/lib/site_controller_extensions.rb +35 -0
  85. data/public/javascripts/reader.js +1 -1
  86. data/public/stylesheets/sass/reader.sass +18 -8
  87. data/radiant-reader-extension.gemspec +30 -176
  88. data/reader_extension.rb +31 -23
  89. data/spec/controllers/admin/messages_controller_spec.rb +1 -1
  90. data/spec/controllers/admin/readers_controller_spec.rb +0 -1
  91. data/spec/controllers/password_resets_controller_spec.rb +1 -1
  92. data/spec/controllers/reader_activations_controller_spec.rb +1 -1
  93. data/spec/controllers/readers_controller_spec.rb +67 -40
  94. data/spec/controllers/site_controller_spec.rb +63 -0
  95. data/spec/datasets/readers_dataset.rb +100 -11
  96. data/spec/models/group_spec.rb +46 -0
  97. data/spec/models/message_spec.rb +40 -15
  98. data/spec/models/page_spec.rb +81 -0
  99. data/spec/models/reader_notifier_spec.rb +1 -1
  100. data/spec/models/reader_spec.rb +17 -12
  101. metadata +99 -67
  102. data/.gitignore +0 -2
  103. data/VERSION +0 -1
  104. data/app/views/readers/_standard_parts.html.haml +0 -23
  105. data/spec/datasets/messages_dataset.rb +0 -49
  106. data/spec/datasets/reader_layouts_dataset.rb +0 -26
  107. data/spec/datasets/reader_sites_dataset.rb +0 -10
@@ -1,11 +1,12 @@
1
1
  require 'sanitize'
2
+ require "sanitize/config/generous"
2
3
 
3
4
  module ReaderHelper
4
5
  def standard_gravatar_for(reader=nil, url=nil)
5
6
  size = Radiant::Config['forum.gravatar_size'] || 40
6
7
  url ||= reader_url(reader)
7
8
  gravatar = gravatar_for(reader, {:size => size}, {:class => 'gravatar offset', :width => size, :height => size})
8
- link_to gravatar, url
9
+ content_tag(:div, link_to(gravatar, url), :class => "speaker")
9
10
  end
10
11
 
11
12
  def gravatar_for(reader=nil, gravatar_options={}, img_options ={})
@@ -25,28 +26,21 @@ module ReaderHelper
25
26
  link_to home_page.title, home_page.url, options
26
27
  end
27
28
 
28
- def clean_textilize(text)
29
- Sanitize.clean(textilize(text), Sanitize::Config::RELAXED)
29
+ def scrub_html(text)
30
+ Sanitize.clean(textilize(text))
30
31
  end
31
32
 
32
- def clean_textilize_without_paragraph(text)
33
- textiled = clean_textilize(text)
34
- if textiled[0..2] == "<p>" then textiled = textiled[3..-1] end
35
- if textiled[-4..-1] == "</p>" then textiled = textiled[0..-5] end
36
- textiled
33
+ def clean_html(text)
34
+ Sanitize.clean(textilize(text), Sanitize::Config::GENEROUS)
37
35
  end
38
36
 
39
- def truncate_words(text='', length=64, omission="...")
37
+ def truncate_words(text='', length=24, omission="...")
40
38
  return '' if text.blank?
41
39
  words = text.split
42
40
  omission = '' unless words.size > length
43
41
  words[0..(length-1)].join(" ") + omission
44
42
  end
45
43
 
46
- def truncate_and_textilize(text, length=64)
47
- clean_textilize( truncate_words(text, length) )
48
- end
49
-
50
44
  def pagination_and_summary_for(list, name='')
51
45
  %{<div class="pagination">
52
46
  #{will_paginate list, :container => false}
@@ -60,18 +54,52 @@ module ReaderHelper
60
54
  def pagination_summary(list, name='')
61
55
  total = list.total_entries
62
56
  if list.empty?
63
- %{#{t('no')} #{name.pluralize}}
57
+ %{#{t('reader_extension.no')} #{name.pluralize}}
64
58
  else
65
59
  name ||= t(list.first.class.to_s.underscore.gsub('_', ' '))
66
60
  if total == 1
67
- %{#{t('showing')} #{t('one')} #{name}}
61
+ %{#{t('reader_extension.showing')} #{t('reader_extension.one')} #{name}}
68
62
  elsif list.current_page == 1 && total < list.per_page
69
- %{#{t('all')} #{total} #{name.pluralize}}
63
+ %{#{t('reader_extension.all')} #{total} #{name.pluralize}}
70
64
  else
71
65
  start = list.offset + 1
72
66
  finish = ((list.offset + list.per_page) < list.total_entries) ? list.offset + list.per_page : list.total_entries
73
- %{#{start} #{t('to')} #{finish} #{t('of')} #{total} #{name.pluralize}}
67
+ %{#{start} #{t('reader_extension.to')} #{finish} #{t('reader_extension.of')} #{total} #{name.pluralize}}
68
+ end
69
+ end
70
+ end
71
+
72
+ def message_preview(subject, body, reader)
73
+ preview = <<EOM
74
+ From: #{current_user.name} &lt;#{current_user.email}&gt;
75
+ To: #{reader.name} &lt;#{reader.email}&gt;
76
+ Date: #{Time.now.to_date.to_s :long}
77
+ <strong>Subject: #{subject}</strong>
78
+
79
+ Dear #{reader.name},
80
+
81
+ #{body}
82
+
83
+ EOM
84
+ simple_format(preview)
85
+ end
86
+
87
+ def choose_page(object, field, select_options={})
88
+ root = Page.respond_to?(:homepage) ? Page.homepage : Page.find_by_parent_id(nil)
89
+ options = page_option_branch(root)
90
+ options.unshift ['<none>', nil]
91
+ select object, field, options, select_options
92
+ end
93
+
94
+ def page_option_branch(page, depth=0)
95
+ options = []
96
+ unless page.title.first == '_'
97
+ options << ["#{". " * depth}#{h(page.title)}", page.id]
98
+ page.children.each do |child|
99
+ options += page_option_branch(child, depth + 1)
74
100
  end
75
101
  end
102
+ options
76
103
  end
104
+
77
105
  end
@@ -0,0 +1,80 @@
1
+ class Group < ActiveRecord::Base
2
+
3
+ has_site if respond_to? :has_site
4
+ default_scope :order => 'name'
5
+
6
+ belongs_to :created_by, :class_name => 'User'
7
+ belongs_to :updated_by, :class_name => 'User'
8
+ belongs_to :homepage, :class_name => 'Page'
9
+
10
+ has_many :messages
11
+ has_many :permissions
12
+ has_many :pages, :through => :permissions
13
+ has_many :memberships
14
+ has_many :readers, :through => :memberships
15
+
16
+ validates_presence_of :name
17
+ validates_uniqueness_of :name
18
+
19
+ named_scope :with_home_page, { :conditions => "homepage_id IS NOT NULL", :include => :homepage }
20
+ named_scope :subscribable, { :conditions => "public = 1" }
21
+ named_scope :unsubscribable, { :conditions => "public = 0" }
22
+
23
+ named_scope :from_list, lambda { |ids|
24
+ { :conditions => ["groups.id IN (#{ids.map{"?"}.join(',')})", *ids] }
25
+ }
26
+
27
+ named_scope :attached_to, lambda { |objects|
28
+ conditions = objects.map{|o| "(pp.permitted_type = ? AND pp.permitted_id = ?)" }.join(" OR ")
29
+ binds = objects.map{|o| [o.class.to_s, o.id]}.flatten
30
+ {
31
+ :select => "groups.*, count(pp.group_id) AS pcount",
32
+ :joins => "INNER JOIN permissions as pp on pp.group_id = groups.id",
33
+ :conditions => [conditions, *binds],
34
+ :having => "pcount > 0", # otherwise attached_to([]) returns all groups
35
+ :group => column_names.map { |n| self.table_name + '.' + n }.join(','),
36
+ :readonly => false
37
+ }
38
+ }
39
+
40
+ def url
41
+ homepage.url if homepage
42
+ end
43
+
44
+ def send_welcome_to(reader)
45
+ if reader.activated? # welcomes are also triggered on activation
46
+ if message = Message.belonging_to(self).for_function('group_welcome').first # only if a group_welcome message exists *belonging to this group*
47
+ message.deliver_to(reader)
48
+ end
49
+ end
50
+ end
51
+
52
+ def admit(reader)
53
+ self.readers << reader
54
+ end
55
+
56
+ def permission_for(object)
57
+ self.permissions.for(object).first
58
+ end
59
+
60
+ def membership_for(reader)
61
+ self.memberships.for(reader).first
62
+ end
63
+
64
+ # we can't has_many through the polymorphic permission relationship, so this is called from has_groups
65
+ # and for eg. Page, it defines:
66
+ # Permission.for_pages named_scope
67
+ # Group.page_permissions => set of permission objects
68
+ # Group.pages => set of page objects
69
+
70
+ def self.define_retrieval_methods(classname)
71
+ type_scope = "for_#{classname.downcase.pluralize}".intern
72
+ Permission.send :named_scope, type_scope, :conditions => { :permitted_type => classname }
73
+ define_method("#{classname.downcase}_permissions") { self.permissions.send type_scope }
74
+ define_method("#{classname.downcase.pluralize}") { self.send("#{classname.to_s.downcase}_permissions".intern).map(&:permitted) }
75
+ end
76
+
77
+
78
+
79
+ end
80
+
@@ -0,0 +1,13 @@
1
+ class Membership < ActiveRecord::Base
2
+
3
+ belongs_to :group
4
+ belongs_to :reader
5
+
6
+ named_scope :for, lambda { |reader|
7
+ {
8
+ :conditions => ["memberships.reader_id = ?", reader.id]
9
+ }
10
+ }
11
+
12
+ end
13
+
@@ -1,14 +1,12 @@
1
1
  class Message < ActiveRecord::Base
2
2
 
3
+ has_groups
3
4
  has_site if respond_to? :has_site
4
5
 
5
6
  belongs_to :layout
6
7
  belongs_to :created_by, :class_name => 'User'
7
8
  belongs_to :updated_by, :class_name => 'User'
8
9
 
9
- has_many :message_readers
10
- has_many :readers, :through => :message_readers
11
-
12
10
  has_many :deliveries, :class_name => 'MessageReader', :conditions => ["message_readers.sent_at IS NOT NULL and message_readers.sent_at <= ?", Time.now.to_s(:db)]
13
11
  has_many :recipients, :through => :deliveries, :source => :reader
14
12
 
@@ -29,7 +27,7 @@ class Message < ActiveRecord::Base
29
27
 
30
28
  # has to return a named_scope for chainability
31
29
  def possible_readers
32
- Reader.active
30
+ groups.any? ? Reader.in_groups(groups) : Reader.scoped({})
33
31
  end
34
32
 
35
33
  def undelivered_readers
@@ -57,15 +55,20 @@ class Message < ActiveRecord::Base
57
55
  end
58
56
 
59
57
  def preview(reader=nil)
60
- reader ||= possible_readers.first || Reader.find_or_create_for_user(created_by)
58
+ reader ||= possible_readers.first || Reader.for_user(created_by)
61
59
  ReaderNotifier.create_message(reader, self)
62
60
  end
63
61
 
64
62
  def function
65
63
  MessageFunction[self.function_id]
66
64
  end
67
- def self.functional(function)
68
- for_function(MessageFunction[function]).first
65
+ def self.functional(function, group=nil)
66
+ messages = for_function(function)
67
+ if group
68
+ messages.belonging_to(group).first
69
+ else
70
+ messages.ungrouped.first
71
+ end
69
72
  end
70
73
  def has_function?
71
74
  !function.nil?
@@ -0,0 +1,11 @@
1
+ class Permission < ActiveRecord::Base
2
+
3
+ belongs_to :group
4
+ belongs_to :permitted, :polymorphic => true
5
+
6
+ named_scope :for, lambda { |object|
7
+ { :conditions => ["permissions.permitted_id = ? and permissions.permitted_type = ?", object.id, object.class.to_s] }
8
+ }
9
+
10
+ end
11
+
data/app/models/reader.rb CHANGED
@@ -5,37 +5,39 @@ class Reader < ActiveRecord::Base
5
5
  @@user_columns = [:name, :email, :login, :created_at, :password, :notes]
6
6
  cattr_accessor :user_columns
7
7
  cattr_accessor :current
8
- default_scope :order => 'name ASC'
9
-
10
- has_site if respond_to? :has_site
8
+ attr_accessor :email_field # used in blocking spam registrations
11
9
 
12
10
  acts_as_authentic do |config|
13
11
  config.validations_scope = :site_id if defined? Site
14
12
  config.transition_from_restful_authentication = true
15
13
  config.validate_email_field = false
16
14
  config.validate_login_field = false
15
+ config.validate_password_field = false
17
16
  end
18
17
 
19
18
  belongs_to :user
20
19
  belongs_to :created_by, :class_name => 'User'
21
20
  belongs_to :updated_by, :class_name => 'User'
22
-
23
21
  has_many :message_readers
24
22
  has_many :messages, :through => :message_readers
23
+ has_many :memberships
24
+ has_many :groups, :through => :memberships
25
+ accepts_nested_attributes_for :memberships
25
26
 
26
- attr_accessor :current_password # used for authentication on update
27
- attr_accessor :email_field # used in blocking spam registrations
28
-
29
27
  before_update :update_user
30
28
 
31
- validates_presence_of :name, :email, :message => 'is required'
32
- validates_uniqueness_of :login, :message => "is already in use here", :allow_blank => true
29
+ validates_presence_of :name, :email
30
+ validates_length_of :name, :maximum => 100, :allow_nil => true
31
+ validates_length_of :password, :minimum => 6, :allow_nil => false, :unless => :existing_reader_keeping_password?
32
+ # validates_format_of :password, :with => /[^A-Za-z]/, :unless => :existing_reader_keeping_password? # we have to match radiant so that users can log in both ways
33
+ validates_confirmation_of :password, :unless => :existing_reader_keeping_password?
34
+ validates_uniqueness_of :login, :allow_blank => true
33
35
  validate :email_must_not_be_in_use
34
36
 
35
37
  include RFC822
36
- validates_format_of :email, :with => RFC822_valid, :message => 'appears not to be an email address'
37
- validates_length_of :name, :maximum => 100, :allow_nil => true
38
+ validates_format_of :email, :with => RFC822_valid
38
39
 
40
+ default_scope :order => 'name ASC'
39
41
  named_scope :any
40
42
  named_scope :active, :conditions => "activated_at IS NOT NULL"
41
43
  named_scope :inactive, :conditions => "activated_at IS NULL"
@@ -49,9 +51,34 @@ class Reader < ActiveRecord::Base
49
51
  end
50
52
  }
51
53
 
54
+ named_scope :in_groups, lambda { |groups|
55
+ {
56
+ :select => "readers.*",
57
+ :joins => "INNER JOIN memberships as mm on mm.reader_id = readers.id",
58
+ :conditions => ["mm.group_id IN (#{groups.map{'?'}.join(',')})", *groups.map{|g| g.is_a?(Group) ? g.id : g}],
59
+ :group => "mm.reader_id"
60
+ }
61
+ }
62
+
52
63
  def self.find_by_login_or_email(login_or_email)
53
64
  reader = find(:first, :conditions => ["login = ? OR email = ?", login_or_email, login_or_email])
54
65
  end
66
+
67
+ def self.for_user(user)
68
+ if user.respond_to?(:site) && site = Page.current_site
69
+ reader = self.find_or_create_by_site_id_and_user_id(site.id, user.id)
70
+ else
71
+ reader = self.find_or_create_by_user_id(user.id)
72
+ end
73
+ if reader.new_record?
74
+ user_columns.each { |att| reader.send("#{att.to_s}=", user.send(att)) }
75
+ reader.crypted_password = user.password
76
+ reader.password_salt = user.salt
77
+ reader.activated_at = reader.created_at
78
+ reader.save(false)
79
+ end
80
+ reader
81
+ end
55
82
 
56
83
  def forename
57
84
  read_attribute(:forename) || name.split(/\s/).first
@@ -60,7 +87,8 @@ class Reader < ActiveRecord::Base
60
87
  def activate!
61
88
  self.activated_at = Time.now.utc
62
89
  self.save!
63
- self.send_welcome_message
90
+ send_welcome_message
91
+ send_group_welcomes
64
92
  end
65
93
 
66
94
  def activated?
@@ -81,9 +109,9 @@ class Reader < ActiveRecord::Base
81
109
  }
82
110
  end
83
111
 
84
- def send_functional_message(function)
112
+ def send_functional_message(function, group=nil)
85
113
  reset_perishable_token!
86
- message = Message.functional(function)
114
+ message = Message.functional(function, group) # returns the standard functional message if no group is supplied, or no group message exists
87
115
  raise StandardError, "No #{function} message could be found" unless message
88
116
  message.deliver_to(self)
89
117
  end
@@ -99,46 +127,54 @@ class Reader < ActiveRecord::Base
99
127
  def is_admin?
100
128
  is_user? && self.user.admin?
101
129
  end
102
-
103
- def self.find_or_create_for_user(user)
104
- if user.respond_to?(:site) && site = Page.current_site
105
- reader = self.find_or_create_by_site_id_and_user_id(site.id, user.id)
106
- else
107
- reader = self.find_or_create_by_user_id(user.id)
108
- end
109
- if reader.new_record?
110
- user_columns.each { |att| reader.send("#{att.to_s}=", user.send(att)) }
111
- reader.crypted_password = user.password
112
- reader.password_salt = user.salt
113
- reader.activated_at = reader.created_at
114
- reader.save(false)
115
- end
116
- reader
117
- end
118
-
130
+
119
131
  def create_password!
120
132
  self.clear_password = self.randomize_password # randomize_password is provided by authlogic
121
133
  self.save! unless self.new_record?
122
134
  self.clear_password
123
135
  end
124
136
 
137
+ def find_homepage
138
+ if homegroup = groups.with_home_page.first
139
+ homegroup.homepage
140
+ end
141
+ end
142
+
143
+ def can_see? (this)
144
+ permitted_groups = this.permitted_groups
145
+ permitted_groups.empty? or in_any_of_these_groups?(permitted_groups)
146
+ end
147
+
148
+ def in_any_of_these_groups? (grouplist)
149
+ (grouplist & groups).any?
150
+ end
151
+
152
+ def is_in? (group)
153
+ groups.include?(group)
154
+ end
155
+
156
+ # has_group? is ambiguous: with no argument it means 'is this reader grouped at all?'.
157
+ def has_group?(group=nil)
158
+ group.nil? ? groups.any? : is_in?(group)
159
+ end
160
+
125
161
  private
126
162
 
127
163
  def email_must_not_be_in_use
128
164
  reader = Reader.find_by_email(self.email) # the finds will be site-scoped if appropriate
129
165
  user = User.find_by_email(self.email)
130
166
  if user && user != self.user
131
- errors.add(:email, "belongs to an author already known here")
167
+ errors.add :value, :taken_by_author
132
168
  elsif reader && reader != self
133
- errors.add(:email, "is already registered here")
169
+ errors.add :value, :taken
134
170
  else
135
171
  return true
136
172
  end
137
173
  return false
138
174
  end
139
175
 
140
- def validate_length_of_password?
141
- new_record? or not password.to_s.empty?
176
+ def existing_reader_keeping_password?
177
+ !new_record? && !password_changed?
142
178
  end
143
179
 
144
180
  def update_user
@@ -148,5 +184,13 @@ private
148
184
  self.user.save! if self.user.changed?
149
185
  end
150
186
  end
187
+
188
+ def send_group_welcomes
189
+ groups.each { |g| g.send_welcome_to(self) }
190
+ end
191
+
192
+ def send_group_invitation_message(group=nil)
193
+ send_functional_message('invitation', group)
194
+ end
151
195
 
152
196
  end
@@ -23,6 +23,7 @@ class ReaderNotifier < ActionMailer::Base
23
23
  :host => host,
24
24
  :title => message.subject,
25
25
  :message => message.filtered_body,
26
+ :group => message.group,
26
27
  :sender => sender,
27
28
  :reply_to => sender_address,
28
29
  :reader => reader,
@@ -0,0 +1,31 @@
1
+ - include_stylesheet('admin/reader_group')
2
+
3
+ %h1
4
+ = t('reader_extension.invite_into_group', :name => @group.name)
5
+
6
+ - if message = @group.messages.for_function('invitation').first
7
+
8
+ - form_for :group, @group, :url => admin_group_group_invitations_url(@group), :html => {:id => 'preview_form', :method => 'post'} do
9
+ .form-area
10
+ %p
11
+ %label{:for => "readerlist"}
12
+ List of people
13
+ %span.formnote
14
+ - if Radiant::Config['reader.use_honorifics?']
15
+ title or rank,
16
+ name, email, [login], [phone]
17
+ = text_area_tag "readerlist", params[:readerlist], :class => "textarea", :style => "width: 100%; height: 240px;"
18
+
19
+ %p.buttons
20
+ = submit_tag 'preview import', :class => 'button'
21
+ or
22
+ = link_to "Cancel", admin_group_url(@group)
23
+
24
+ %p
25
+ = t('reader_extension.invitation_instructions')
26
+
27
+ - else
28
+ %p
29
+ = t('reader_extension.no_invitation_message')
30
+ = link_to image('plus') + ' ' + t("reader_extension.create_invitation_message"), new_admin_group_message_url(@group, :function => 'invitation'), :class => "action"
31
+
@@ -0,0 +1,58 @@
1
+ - include_stylesheet('admin/reader_group')
2
+
3
+ %h1
4
+ = t('reader_extension.check_invitation_list', :name => @group.name)
5
+
6
+ - form_for :group, @group, :url => admin_group_group_invitations_url(@group), :html => {:id => 'confirmation_form', :method => 'post'} do
7
+ %p
8
+ = t('reader_extension.invitation_preview_instructions')
9
+
10
+ %table#import
11
+ %thead
12
+ %tr
13
+ %th
14
+ - if Radiant::Config['reader.use_honorifics?']
15
+ %th= t('reader_extension.honorific')
16
+ %th= t('reader_extension.full_name')
17
+ %th= t('reader_extension.email')
18
+ %th= t('reader_extension.optional_login')
19
+ %th= t('reader_extension.optional_phone')
20
+ %tbody
21
+ - i = 0
22
+ - @readers.each do |reader|
23
+ - if reader.new_record?
24
+ %tr.import
25
+ %td
26
+ = check_box_tag "import_reader[]", i, reader.valid?
27
+ - if Radiant::Config['reader.use_honorifics?']
28
+ %td
29
+ = text_field_tag "reader_#{i}[honorific]", reader.honorific, :class => "preview#{ ' with_error' if reader.errors.on(:honorific)}", :title => reader.errors.on(:honorific)
30
+ %td
31
+ = text_field_tag "reader_#{i}[name]", reader.name, :class => "preview#{ ' with_error' if reader.errors.on(:name)}", :title => reader.errors.on(:name)
32
+ %td
33
+ = text_field_tag "reader_#{i}[email]", reader.email, :class => "preview#{ ' with_error' if reader.errors.on(:email)}", :title => reader.errors.on(:email)
34
+ %td
35
+ = text_field_tag "reader_#{i}[login]", reader.login, :class => "preview#{ ' with_error' if reader.errors.on(:login)}", :title => reader.errors.on(:login)
36
+ %td
37
+ = text_field_tag "reader_#{i}[phone]", reader.phone, :class => "preview#{ ' with_error' if reader.errors.on(:phone)}", :title => reader.errors.on(:phone)
38
+ - else
39
+ %tr.invite
40
+ %td
41
+ = check_box_tag "invite_reader[]", reader.id, {:checked => true}
42
+ - if Radiant::Config['reader.use_honorifics?']
43
+ %td
44
+ = text_field_tag "reader_#{i}[honorific]", reader.honorific, :class => "preview#{ ' with_error' if reader.errors.on(:honorific)}", :title => reader.errors.on(:honorific), :disabled => true
45
+ %td
46
+ = text_field_tag "reader_#{i}[name]", reader.name, :class => "preview#{ ' with_error' if reader.errors.on(:name)}", :title => reader.errors.on(:name), :disabled => true
47
+ %td
48
+ = text_field_tag "reader_#{i}[email]", reader.email, :class => "preview#{ ' with_error' if reader.errors.on(:email)}", :title => reader.errors.on(:email), :disabled => true
49
+ %td
50
+ = text_field_tag "reader_#{i}[login]", reader.login, :class => "preview#{ ' with_error' if reader.errors.on(:login)}", :title => reader.errors.on(:login), :disabled => true
51
+ %td
52
+ = text_field_tag "reader_#{i}[phone]", reader.phone, :class => "preview#{ ' with_error' if reader.errors.on(:phone)}", :title => reader.errors.on(:phone)
53
+ - i = i + 1
54
+
55
+ %p.buttons
56
+ = submit_tag 'Invite these people into the group', :name => 'confirm', :class => 'button'
57
+ or
58
+ = link_to 'start again', new_admin_group_group_invitation_url(@group)
File without changes
@@ -0,0 +1,26 @@
1
+ - form_for [:admin, @group] do |f|
2
+ .form-area
3
+ = render_region :form_top
4
+ = hidden_field 'group', 'lock_version'
5
+
6
+ - render_region :form do |form|
7
+ - form.edit_group do
8
+ #group
9
+ %p.title
10
+ = f.label :name
11
+ = f.text_field 'name', :maxlength => 100, :class => "textbox"
12
+ %p.description
13
+ = f.label :description
14
+ = f.text_area 'description'
15
+ %p.homepage
16
+ = f.label :homepage_id, "Group home page:"
17
+ = choose_page 'group', 'homepage_id', {:selected =>@group.homepage_id}
18
+
19
+ - form.edit_timestamp do
20
+ = updated_stamp @group
21
+ - form.edit_buttons do
22
+ %p.buttons
23
+ = save_model_button @group
24
+ = save_model_and_continue_editing_button @group
25
+ or
26
+ = link_to "Cancel", admin_groups_url
@@ -0,0 +1,12 @@
1
+ %tr
2
+ - render_region :thead do |thead|
3
+ - thead.name_header do
4
+ %th.group Name
5
+ - thead.home_header do
6
+ %th.home Home page
7
+ - thead.members_header do
8
+ %th.members Members
9
+ - thead.pages_header do
10
+ %th.pages Pages
11
+ - thead.modify_header do
12
+ %th.modify Modify
@@ -0,0 +1,9 @@
1
+ - include_stylesheet('admin/reader_group')
2
+
3
+ - render_region :main do |main|
4
+ - main.edit_header do
5
+ %h1
6
+ Edit group
7
+
8
+ - main.edit_form do
9
+ = render :partial => 'form'
@@ -0,0 +1,44 @@
1
+ - include_stylesheet('admin/reader_group')
2
+
3
+ = render_region :top
4
+
5
+ #groups_table.outset
6
+ %table#groups.index{:cellspacing=>"0", :border=>"0", :cellpadding=>"0"}
7
+ %thead
8
+ = render :partial => 'list_head'
9
+ %tbody
10
+ - @groups.each do |group|
11
+ %tr
12
+ - render_region :tbody do |tbody|
13
+ - tbody.name_cell do
14
+ %td.name
15
+ %p
16
+ = link_to group.name, admin_group_url(group)
17
+ %br
18
+ %span.notes
19
+ = group.description
20
+ - tbody.home_cell do
21
+ %td.home
22
+ - if group.homepage
23
+ = link_to group.homepage.title, edit_admin_page_url(group.homepage)
24
+ - else
25
+ = t('reader_extension.none')
26
+ - tbody.members_cell do
27
+ %td.members
28
+ = group.readers.count
29
+ - tbody.pages_cell do
30
+ %td.pages
31
+ = group.pages.count
32
+ - tbody.modify_cell do
33
+ %td.actions
34
+ = link_to_unless_current image('plus') + ' ' + t('reader_extension.add_members'), new_admin_group_group_invitation_url(group), :class => 'action'
35
+ = link_to_unless_current image('delta') + ' ' + t('reader_extension.edit_group'), edit_admin_group_url(group), :class => 'action'
36
+ = link_to_unless_current image('minus') + ' ' + t('reader_extension.delete_group'), admin_group_url(group), :method => 'delete', :confirm => t("reader_extension.really_delete_group", :name => group.name), :class => 'action'
37
+
38
+ - render_region :bottom do |bottom|
39
+ - bottom.buttons do
40
+ #actions
41
+ = pagination_for @readers
42
+ %ul
43
+ %li
44
+ = link_to image('plus') + " " + t("reader_extension.new_group"), new_admin_group_url
@@ -0,0 +1,7 @@
1
+ - include_stylesheet('admin/reader_group')
2
+
3
+ - render_region :main do |main|
4
+ - main.edit_header do
5
+ %h1 New group
6
+ - main.edit_form do
7
+ = render :partial => "form"