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
@@ -0,0 +1,125 @@
1
+ module GroupedModel
2
+ def self.included(base)
3
+ base.extend ClassMethods
4
+ end
5
+
6
+ module ClassMethods
7
+ def has_groups?
8
+ false
9
+ end
10
+ alias :has_group? :has_groups?
11
+
12
+ def has_groups(options={})
13
+ return if has_groups?
14
+
15
+ class_eval {
16
+ include GroupedModel::GroupedInstanceMethods
17
+
18
+ def self.has_groups?
19
+ true
20
+ end
21
+
22
+ def self.visible
23
+ ungrouped
24
+ end
25
+ }
26
+
27
+ has_many :permissions, :as => :permitted
28
+ has_many :groups, :through => :permissions
29
+ Group.define_retrieval_methods(self.to_s)
30
+
31
+ named_scope :visible_to, lambda { |reader|
32
+ conditions = "pp.group_id IS NULL"
33
+ if reader && reader.groups.any?
34
+ ids = reader.group_ids
35
+ conditions = ["#{conditions} OR pp.group_id IS NULL OR pp.group_id IN(#{ids.map{"?"}.join(',')})", *ids]
36
+ end
37
+ {
38
+ :joins => "LEFT OUTER JOIN permissions as pp on pp.permitted_id = #{self.table_name}.id AND pp.permitted_type = '#{self.to_s}'",
39
+ :group => column_names.map { |n| self.table_name + '.' + n }.join(','),
40
+ :conditions => conditions,
41
+ :readonly => false
42
+ }
43
+ }
44
+
45
+ named_scope :ungrouped, {
46
+ :select => "#{self.table_name}.*, count(pp.id) as group_count",
47
+ :joins => "LEFT OUTER JOIN permissions as pp on pp.permitted_id = #{self.table_name}.id AND pp.permitted_type = '#{self.to_s}'",
48
+ :having => "group_count = 0",
49
+ :group => column_names.map { |n| self.table_name + '.' + n }.join(','), # postgres requires that we group by all selected (but not aggregated) columns
50
+ :readonly => false
51
+ } do
52
+ def count
53
+ length
54
+ end
55
+ end
56
+
57
+ named_scope :grouped, {
58
+ :select => "#{self.table_name}.*, count(pp.id) as group_count",
59
+ :joins => "LEFT OUTER JOIN permissions as pp on pp.permitted_id = #{self.table_name}.id AND pp.permitted_type = '#{self.to_s}'",
60
+ :having => "group_count > 0",
61
+ :group => column_names.map { |n| self.table_name + '.' + n }.join(','),
62
+ :readonly => false
63
+ } do
64
+ def count
65
+ length
66
+ end
67
+ end
68
+
69
+ named_scope :belonging_to, lambda { |group|
70
+ {
71
+ :joins => "INNER JOIN permissions as pp on pp.permitted_id = #{self.table_name}.id AND pp.permitted_type = '#{self.to_s}'",
72
+ :group => column_names.map { |n| self.table_name + '.' + n }.join(','),
73
+ :conditions => ["pp.group_id = ?", group.id],
74
+ :readonly => false
75
+ }
76
+ }
77
+
78
+ end
79
+ alias :has_group :has_groups
80
+ end
81
+
82
+ module GroupedInstanceMethods
83
+
84
+ # in GroupedPage this is chained to include inherited groups
85
+ def permitted_groups
86
+ groups
87
+ end
88
+
89
+ def visible_to?(reader)
90
+ Rails.logger.warn "grouped_model#visible_to?"
91
+ return true if self.permitted_groups.empty?
92
+ return false if reader.nil?
93
+ return true if reader.is_admin?
94
+ return (reader.groups & self.permitted_groups).any?
95
+ end
96
+
97
+ def group
98
+ if self.permitted_groups.length == 1
99
+ self.permitted_groups.first
100
+ else
101
+ nil
102
+ end
103
+ end
104
+
105
+ def visible?
106
+ permitted_groups.empty?
107
+ end
108
+
109
+ def permitted_readers
110
+ permitted_groups.any? ? Reader.in_groups(permitted_groups) : Reader.all
111
+ end
112
+
113
+ def has_group?(group)
114
+ return self.permitted_groups.include?(group)
115
+ end
116
+
117
+ def permit(group)
118
+ self.groups << group unless self.has_group?(group)
119
+ end
120
+
121
+ def group_ids=(ids)
122
+ self.groups = Group.from_list(ids)
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,39 @@
1
+ module GroupedPage
2
+
3
+ def self.included(base)
4
+ base.class_eval {
5
+ has_groups
6
+ has_one :homegroup, :foreign_key => 'homepage_id', :class_name => 'Group'
7
+ include InstanceMethods
8
+ alias_method_chain :permitted_groups, :inheritance
9
+ }
10
+ end
11
+
12
+ module InstanceMethods
13
+
14
+ attr_reader :inherited_groups
15
+ def inherited_groups
16
+ @inherited_groups ||= self.parent ? Group.attached_to(self.ancestors) : []
17
+ end
18
+
19
+ def permitted_groups_with_inheritance
20
+ permitted_groups_without_inheritance + inherited_groups
21
+ end
22
+
23
+ def cache?
24
+ self.permitted_groups.empty?
25
+ end
26
+
27
+ def has_inherited_group?(group)
28
+ return self.inherited_groups.include?(group)
29
+ end
30
+
31
+ def group_is_inherited?(group)
32
+ return self.has_inherited_group?(group) && !self.has_group?(group)
33
+ end
34
+
35
+ end
36
+
37
+ end
38
+
39
+
@@ -0,0 +1,183 @@
1
+ module MessageTags
2
+ include Radiant::Taggable
3
+ include ReaderHelper
4
+
5
+ class TagError < StandardError; end
6
+
7
+ # I can see this causing problems: will change soon
8
+
9
+ desc %{
10
+ The root 'site' tag is not meant to be called directly.
11
+ All it does is to prepare the way for eg.
12
+ <pre><code><r:site:url /></code></pre>
13
+ }
14
+ tag 'site' do |tag|
15
+ raise TagError, "r:site currently only works in email" unless @mailer_vars
16
+ raise TagError, "no site" unless tag.locals.site = @mailer_vars[:@site]
17
+ tag.expand
18
+ end
19
+ tag 'site:name' do |tag|
20
+ if defined?(Site) && tag.locals.site.is_a?(Site)
21
+ tag.locals.site.name
22
+ else
23
+ tag.locals.site[:name]
24
+ end
25
+ end
26
+ tag 'site:url' do |tag|
27
+ if defined?(Site) && tag.locals.site.is_a?(Site)
28
+ tag.locals.site.base_domain
29
+ else
30
+ tag.locals.site[:url]
31
+ end
32
+ end
33
+ tag 'site:login_url' do |tag|
34
+ reader_login_url(:host => @mailer_vars[:@host])
35
+ end
36
+
37
+ desc %{
38
+ The root 'recipient' tag is not meant to be called directly.
39
+ All it does is summon a reader object so that its fields can be displayed with eg.
40
+ <pre><code><r:recipient:name /></code></pre>
41
+ }
42
+ tag 'recipient' do |tag|
43
+ raise TagError, "r:recipient only works in email" unless @mailer_vars
44
+ raise TagError, "no recipient" unless tag.locals.recipient = @mailer_vars[:@reader]
45
+ tag.expand
46
+ end
47
+
48
+ [:name, :forename, :email, :description, :login].each do |field|
49
+ desc %{
50
+ Only for use in email messages. Displays the #{field} field of the reader currently being emailed.
51
+ <pre><code><r:recipient:#{field} /></code></pre>
52
+ }
53
+ tag "recipient:#{field}" do |tag|
54
+ tag.locals.recipient.send(field)
55
+ end
56
+ end
57
+
58
+ desc %{
59
+ Only for use in email messages. Displays the password of the reader currently being emailed, if we still have it.
60
+
61
+ (After the first successful login we forget the cleartext version of their password, so you only want to use this
62
+ tag in welcome and activation messages.)
63
+
64
+ <pre><code><r:recipient:url /></code></pre>
65
+ }
66
+ tag "recipient:password" do |tag|
67
+ tag.locals.recipient.clear_password || "<encrypted>"
68
+ end
69
+
70
+ desc %{
71
+ Only for use in email messages. Displays the me-page url of the reader currently being emailed.
72
+ <pre><code><r:recipient:url /></code></pre>
73
+ }
74
+ tag "recipient:url" do |tag|
75
+ reader_url(tag.locals.recipient, :host => @mailer_vars[:@host])
76
+ end
77
+
78
+ desc %{
79
+ Only for use in email messages. Displays the preferences url of the reader currently being emailed.
80
+ <pre><code><r:recipient:url /></code></pre>
81
+ }
82
+ tag "recipient:edit_url" do |tag|
83
+ edit_reader_url(tag.locals.recipient, :host => @mailer_vars[:@host])
84
+ end
85
+
86
+ desc %{
87
+ Only for use in email messages. Displays the address that will activate the current reader account.
88
+ <pre><code><r:recipient:activation_url /></code></pre>
89
+ }
90
+ tag "recipient:activation_url" do |tag|
91
+ activate_me_url(tag.locals.recipient, :activation_code => tag.locals.recipient.perishable_token, :host => @mailer_vars[:@host])
92
+ end
93
+
94
+ desc %{
95
+ Only for use in email messages. Displays the address that will bring up a new-password form for the current reader account.
96
+ <pre><code><r:recipient:repassword_url /></code></pre>
97
+ }
98
+ tag "recipient:repassword_url" do |tag|
99
+ repassword_me_url(tag.locals.recipient, :confirmation_code => tag.locals.recipient.perishable_token, :host => @mailer_vars[:@host])
100
+ end
101
+
102
+ desc %{
103
+ The root 'sender' tag is not meant to be called directly.
104
+ All it does is summon a sender object so that its fields can be displayed with eg.
105
+ <pre><code><r:sender:name /></code></pre>
106
+ }
107
+ tag 'sender' do |tag|
108
+ raise TagError, "r:sender only works in email" unless @mailer_vars
109
+ tag.expand
110
+ end
111
+
112
+ desc %{
113
+ Only for use in email messages. Displays the name of the email-sender (which is probably configured in `email.name`)
114
+ <pre><code><r:sender:name /></code></pre>
115
+ }
116
+ tag "sender:name" do |tag|
117
+ @mailer_vars['sender']
118
+ end
119
+
120
+ desc %{
121
+ Only for use in email messages. Displays the address of the email-sender (which is probably configured in `email.address`)
122
+ <pre><code><r:sender:address /></code></pre>
123
+ }
124
+ tag "sender:address" do |tag|
125
+ @mailer_vars['reply_to']
126
+ end
127
+
128
+ # and for referring to messages on pages
129
+ # at the moment this only works inside r:reader:messages:each or r:group:messages:each,
130
+ # both of which are defined in reader_group
131
+ # but soon there will be a conditional r:messages:each tag here too
132
+
133
+ desc %{
134
+ The root 'message' tag is not meant to be called directly.
135
+ All it does is summon a message object so that its fields can be displayed with eg.
136
+ <pre><code><r:message:subject /></code></pre>
137
+ }
138
+ tag 'message' do |tag|
139
+ raise TagError, "no message!" unless tag.locals.message
140
+ tag.expand
141
+ end
142
+
143
+ desc %{
144
+ Displays the message subject.
145
+
146
+ <pre><code><r:message:subject /></code></pre>
147
+ }
148
+ tag "message:subject" do |tag|
149
+ tag.locals.message.subject
150
+ end
151
+
152
+ desc %{
153
+ Displays the formatted message body.
154
+
155
+ <pre><code><r:message:body /></code></pre>
156
+ }
157
+ tag "message:body" do |tag|
158
+ tag.locals.message.filtered_body
159
+ end
160
+
161
+ desc %{
162
+ Returns the url of the show-message page.
163
+
164
+ <pre><code><r:message:url /></code></pre>
165
+ }
166
+ tag "message:url" do |tag|
167
+ preview_message_path(tag.locals.message)
168
+ end
169
+
170
+ desc %{
171
+ Displays a link to the show-message page.
172
+
173
+ <pre><code><r:message:link /></code></pre>
174
+ }
175
+ tag "message:link" do |tag|
176
+ options = tag.attr.dup
177
+ attributes = options.inject('') { |s, (k, v)| s << %{#{k.downcase}="#{v}" } }.strip
178
+ attributes = " #{attributes}" unless attributes.empty?
179
+ text = tag.double? ? tag.expand : tag.render('message:subject')
180
+ %{<a href="#{tag.render('message:url')}"#{attributes}>#{text}</a>}
181
+ end
182
+
183
+ end
@@ -0,0 +1,8 @@
1
+ module RadiantReaderExtension
2
+ VERSION = '2.0.0.rc4'
3
+ SUMMARY = %q{Reader/viewer/visitor registration, login and access-control for Radiant CMS}
4
+ DESCRIPTION = %q{Provides reader/member/user registration and management functions including password-reminder, group-based page access control and administrative email.}
5
+ URL = "http://radiant.spanner.org/reader"
6
+ AUTHORS = ["William Ross"]
7
+ EMAIL = ["radiant@spanner.org"]
8
+ end
@@ -3,13 +3,15 @@ module ReaderAdminUI
3
3
  def self.included(base)
4
4
  base.class_eval do
5
5
 
6
- attr_accessor :reader, :message, :reader_configuration
6
+ attr_accessor :reader, :message, :group, :reader_configuration
7
7
  alias_method :readers, :reader
8
8
  alias_method :messages, :message
9
+ alias_method :groups, :group
9
10
 
10
11
  def load_reader_extension_regions
11
12
  @reader = load_default_reader_regions
12
13
  @message = load_default_message_regions
14
+ @group = load_default_group_regions
13
15
  @reader_configuration = load_default_reader_configuration_regions
14
16
  end
15
17
 
@@ -22,10 +24,10 @@ module ReaderAdminUI
22
24
  protected
23
25
 
24
26
  def load_default_reader_regions
25
- returning OpenStruct.new do |reader|
27
+ OpenStruct.new.tap do |reader|
26
28
  reader.edit = Radiant::AdminUI::RegionSet.new do |edit|
27
29
  edit.main.concat %w{edit_header edit_form}
28
- edit.form.concat %w{edit_name edit_email edit_username edit_password edit_description edit_notes}
30
+ edit.form.concat %w{edit_name edit_email edit_username edit_password reader_groups edit_description edit_notes}
29
31
  edit.form_bottom.concat %w{edit_timestamp edit_buttons}
30
32
  end
31
33
  reader.index = Radiant::AdminUI::RegionSet.new do |index|
@@ -39,7 +41,7 @@ module ReaderAdminUI
39
41
  end
40
42
 
41
43
  def load_default_reader_configuration_regions
42
- returning OpenStruct.new do |reader_configuration|
44
+ OpenStruct.new.tap do |reader_configuration|
43
45
  reader_configuration.show = Radiant::AdminUI::RegionSet.new do |show|
44
46
  show.settings.concat %w{administration}
45
47
  show.messages.concat %w{administration}
@@ -53,10 +55,10 @@ module ReaderAdminUI
53
55
  end
54
56
 
55
57
  def load_default_message_regions
56
- returning OpenStruct.new do |message|
58
+ OpenStruct.new.tap do |message|
57
59
  message.edit = Radiant::AdminUI::RegionSet.new do |edit|
58
60
  edit.main.concat %w{edit_header edit_form edit_footer}
59
- edit.form.concat %w{edit_subject edit_body}
61
+ edit.form.concat %w{edit_subject edit_body edit_group}
60
62
  edit.form_bottom.concat %w{edit_timestamp edit_buttons}
61
63
  end
62
64
  message.index = Radiant::AdminUI::RegionSet.new do |index|
@@ -73,6 +75,27 @@ module ReaderAdminUI
73
75
  message.new = message.edit
74
76
  end
75
77
  end
78
+
79
+ def load_default_group_regions
80
+ OpenStruct.new.tap do |group|
81
+ group.edit = Radiant::AdminUI::RegionSet.new do |edit|
82
+ edit.main.concat %w{edit_header edit_form}
83
+ edit.form.concat %w{edit_group edit_timestamp edit_buttons}
84
+ end
85
+ group.show = Radiant::AdminUI::RegionSet.new do |show|
86
+ show.header.concat %w{title}
87
+ show.main.concat %w{messages pages members}
88
+ show.footer.concat %w{notes javascript}
89
+ end
90
+ group.index = Radiant::AdminUI::RegionSet.new do |index|
91
+ index.thead.concat %w{name_header home_header members_header pages_header modify_header}
92
+ index.tbody.concat %w{name_cell home_cell members_cell pages_cell modify_cell}
93
+ index.bottom.concat %w{buttons}
94
+ end
95
+ group.remove = group.index
96
+ group.new = group.edit
97
+ end
98
+ end
76
99
  end
77
100
  end
78
101
  end
data/lib/reader_tags.rb CHANGED
@@ -1,183 +1,19 @@
1
1
  module ReaderTags
2
2
  include Radiant::Taggable
3
3
  include ReaderHelper
4
+ include GroupTags
5
+ include MessageTags
4
6
 
5
7
  class TagError < StandardError; end
6
8
 
7
- # I can see this causing problems: will change soon
9
+ # I need to find a better way to do this, but this gives a starting point
8
10
 
9
- desc %{
10
- The root 'site' tag is not meant to be called directly.
11
- All it does is to prepare the way for eg.
12
- <pre><code><r:site:url /></code></pre>
13
- }
14
- tag 'site' do |tag|
15
- raise TagError, "r:site currently only works in email" unless @mailer_vars
16
- raise TagError, "no site" unless tag.locals.site = @mailer_vars[:@site]
17
- tag.expand
18
- end
19
- tag 'site:name' do |tag|
20
- if defined?(Site) && tag.locals.site.is_a?(Site)
21
- tag.locals.site.name
22
- else
23
- tag.locals.site[:name]
24
- end
25
- end
26
- tag 'site:url' do |tag|
27
- if defined?(Site) && tag.locals.site.is_a?(Site)
28
- tag.locals.site.base_domain
29
- else
30
- tag.locals.site[:url]
31
- end
32
- end
33
- tag 'site:login_url' do |tag|
34
- reader_login_url(:host => @mailer_vars[:@host])
35
- end
36
-
37
- desc %{
38
- The root 'recipient' tag is not meant to be called directly.
39
- All it does is summon a reader object so that its fields can be displayed with eg.
40
- <pre><code><r:recipient:name /></code></pre>
41
- }
42
- tag 'recipient' do |tag|
43
- raise TagError, "r:recipient only works in email" unless @mailer_vars
44
- raise TagError, "no recipient" unless tag.locals.recipient = @mailer_vars[:@reader]
45
- tag.expand
46
- end
47
-
48
- [:name, :forename, :email, :description, :login].each do |field|
49
- desc %{
50
- Only for use in email messages. Displays the #{field} field of the reader currently being emailed.
51
- <pre><code><r:recipient:#{field} /></code></pre>
52
- }
53
- tag "recipient:#{field}" do |tag|
54
- tag.locals.recipient.send(field)
55
- end
56
- end
57
-
58
- desc %{
59
- Only for use in email messages. Displays the password of the reader currently being emailed, if we still have it.
60
-
61
- (After the first successful login we forget the cleartext version of their password, so you only want to use this
62
- tag in welcome and activation messages.)
63
-
64
- <pre><code><r:recipient:url /></code></pre>
65
- }
66
- tag "recipient:password" do |tag|
67
- tag.locals.recipient.clear_password || "<encrypted>"
68
- end
69
-
70
- desc %{
71
- Only for use in email messages. Displays the me-page url of the reader currently being emailed.
72
- <pre><code><r:recipient:url /></code></pre>
73
- }
74
- tag "recipient:url" do |tag|
75
- reader_url(tag.locals.recipient, :host => @mailer_vars[:@host])
76
- end
77
-
78
- desc %{
79
- Only for use in email messages. Displays the preferences url of the reader currently being emailed.
80
- <pre><code><r:recipient:url /></code></pre>
81
- }
82
- tag "recipient:edit_url" do |tag|
83
- edit_reader_url(tag.locals.recipient, :host => @mailer_vars[:@host])
84
- end
85
-
86
- desc %{
87
- Only for use in email messages. Displays the address that will activate the current reader account.
88
- <pre><code><r:recipient:activation_url /></code></pre>
89
- }
90
- tag "recipient:activation_url" do |tag|
91
- activate_me_url(tag.locals.recipient, :activation_code => tag.locals.recipient.perishable_token, :host => @mailer_vars[:@host])
92
- end
93
-
94
- desc %{
95
- Only for use in email messages. Displays the address that will bring up a new-password form for the current reader account.
96
- <pre><code><r:recipient:repassword_url /></code></pre>
97
- }
98
- tag "recipient:repassword_url" do |tag|
99
- repassword_me_url(tag.locals.recipient, :confirmation_code => tag.locals.recipient.perishable_token, :host => @mailer_vars[:@host])
100
- end
101
-
102
- desc %{
103
- The root 'sender' tag is not meant to be called directly.
104
- All it does is summon a sender object so that its fields can be displayed with eg.
105
- <pre><code><r:sender:name /></code></pre>
106
- }
107
- tag 'sender' do |tag|
108
- raise TagError, "r:sender only works in email" unless @mailer_vars
109
- tag.expand
11
+ tag 'reader_css' do |tag|
12
+ %{<link rel="stylesheet" href="/stylesheets/reader.css" media="all" />}
110
13
  end
111
14
 
112
- desc %{
113
- Only for use in email messages. Displays the name of the email-sender (which is probably configured in `email.name`)
114
- <pre><code><r:sender:name /></code></pre>
115
- }
116
- tag "sender:name" do |tag|
117
- @mailer_vars['sender']
118
- end
119
-
120
- desc %{
121
- Only for use in email messages. Displays the address of the email-sender (which is probably configured in `email.address`)
122
- <pre><code><r:sender:address /></code></pre>
123
- }
124
- tag "sender:address" do |tag|
125
- @mailer_vars['reply_to']
126
- end
127
-
128
- # and for referring to messages on pages
129
- # at the moment this only works inside r:reader:messages:each or r:group:messages:each,
130
- # both of which are defined in reader_group
131
- # but soon there will be a conditional r:messages:each tag here too
132
-
133
- desc %{
134
- The root 'message' tag is not meant to be called directly.
135
- All it does is summon a message object so that its fields can be displayed with eg.
136
- <pre><code><r:message:subject /></code></pre>
137
- }
138
- tag 'message' do |tag|
139
- raise TagError, "no message!" unless tag.locals.message
140
- tag.expand
141
- end
142
-
143
- desc %{
144
- Displays the message subject.
145
-
146
- <pre><code><r:message:subject /></code></pre>
147
- }
148
- tag "message:subject" do |tag|
149
- tag.locals.message.subject
150
- end
151
-
152
- desc %{
153
- Displays the formatted message body.
154
-
155
- <pre><code><r:message:body /></code></pre>
156
- }
157
- tag "message:body" do |tag|
158
- tag.locals.message.filtered_body
159
- end
160
-
161
- desc %{
162
- Returns the url of the show-message page.
163
-
164
- <pre><code><r:message:url /></code></pre>
165
- }
166
- tag "message:url" do |tag|
167
- preview_message_path(tag.locals.message)
168
- end
169
-
170
- desc %{
171
- Displays a link to the show-message page.
172
-
173
- <pre><code><r:message:link /></code></pre>
174
- }
175
- tag "message:link" do |tag|
176
- options = tag.attr.dup
177
- attributes = options.inject('') { |s, (k, v)| s << %{#{k.downcase}="#{v}" } }.strip
178
- attributes = " #{attributes}" unless attributes.empty?
179
- text = tag.double? ? tag.expand : tag.render('message:subject')
180
- %{<a href="#{tag.render('message:url')}"#{attributes}>#{text}</a>}
15
+ tag 'reader_js' do |tag|
16
+ %{<script type="text/javascript" src="/javascripts/reader.js"></script>}
181
17
  end
182
18
 
183
19
  desc %{
@@ -295,16 +131,4 @@ module ReaderTags
295
131
  tag "unless_reader" do |tag|
296
132
  tag.expand unless Reader.current && !tag.locals.page.cache?
297
133
  end
298
-
299
- # work in progress
300
- tag "truncated" do |tag|
301
- limit = tag.attr['limit'] || 64
302
- omission = tag.attr['omission'] || '&hellip;'
303
- content = tag.expand
304
-
305
- Rails.logger.warn "!! truncating #{content} to #{limit}#{omission}"
306
-
307
- truncate_words(content, limit, omission)
308
- end
309
-
310
134
  end
@@ -0,0 +1,49 @@
1
+ #--
2
+ # Copyright (c) 2011 Ryan Grove <ryan@wonko.com>
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the 'Software'), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all
12
+ # copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ # SOFTWARE.
21
+ #++
22
+
23
+ class Sanitize
24
+ module Config
25
+ GENEROUS = {
26
+ :elements => %w[
27
+ a b blockquote br caption cite code dd del dl dt em h1 h2 h3 h4 h5 h6 i img li ol p pre small span strike strong sub sup table tbody td th thead tr u ul
28
+ ],
29
+
30
+ :attributes => {
31
+ :all => ['dir', 'lang', 'title', 'class'],
32
+ 'a' => ['href'],
33
+ 'blockquote' => ['cite'],
34
+ 'img' => ['align', 'alt', 'height', 'src', 'width'],
35
+ 'ol' => ['start', 'reversed', 'type'],
36
+ 'ul' => ['type'],
37
+ 'th' => ['colspan', 'rowspan'],
38
+ 'td' => ['colspan', 'rowspan']
39
+ },
40
+
41
+ :protocols => {
42
+ 'a' => {'href' => ['ftp', 'http', 'https', 'mailto', :relative]},
43
+ 'blockquote' => {'cite' => ['http', 'https', :relative]},
44
+ 'del' => {'cite' => ['http', 'https', :relative]},
45
+ 'img' => {'src' => ['http', 'https', :relative]},
46
+ }
47
+ }
48
+ end
49
+ end