radiant-reader-extension 0.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. data/.gitignore +2 -0
  2. data/README.md +89 -0
  3. data/Rakefile +140 -0
  4. data/VERSION +1 -0
  5. data/app/controllers/admin/messages_controller.rb +20 -0
  6. data/app/controllers/admin/reader_settings_controller.rb +92 -0
  7. data/app/controllers/admin/readers_controller.rb +28 -0
  8. data/app/controllers/password_resets_controller.rb +64 -0
  9. data/app/controllers/reader_action_controller.rb +84 -0
  10. data/app/controllers/reader_activations_controller.rb +60 -0
  11. data/app/controllers/reader_sessions_controller.rb +56 -0
  12. data/app/controllers/readers_controller.rb +131 -0
  13. data/app/helpers/admin/reader_settings_helper.rb +36 -0
  14. data/app/models/message.rb +108 -0
  15. data/app/models/message_function.rb +37 -0
  16. data/app/models/message_reader.rb +13 -0
  17. data/app/models/reader.rb +146 -0
  18. data/app/models/reader_notifier.rb +34 -0
  19. data/app/models/reader_session.rb +3 -0
  20. data/app/views/admin/messages/_form.html.haml +29 -0
  21. data/app/views/admin/messages/_help.html.haml +41 -0
  22. data/app/views/admin/messages/_message_description.html.haml +3 -0
  23. data/app/views/admin/messages/edit.html.haml +16 -0
  24. data/app/views/admin/messages/new.html.haml +16 -0
  25. data/app/views/admin/reader_settings/_setting.html.haml +24 -0
  26. data/app/views/admin/reader_settings/edit.html.haml +10 -0
  27. data/app/views/admin/reader_settings/index.html.haml +35 -0
  28. data/app/views/admin/reader_settings/show.html.haml +1 -0
  29. data/app/views/admin/readers/_avatar.html.haml +3 -0
  30. data/app/views/admin/readers/_form.html.haml +50 -0
  31. data/app/views/admin/readers/_list_head.html.haml +9 -0
  32. data/app/views/admin/readers/_listed.html.haml +22 -0
  33. data/app/views/admin/readers/_password_fields.html.haml +18 -0
  34. data/app/views/admin/readers/edit.html.haml +8 -0
  35. data/app/views/admin/readers/index.html.haml +17 -0
  36. data/app/views/admin/readers/new.html.haml +7 -0
  37. data/app/views/admin/readers/remove.html.haml +18 -0
  38. data/app/views/admin/sites/_choose_reader_layout.html.haml +7 -0
  39. data/app/views/password_resets/create.html.haml +13 -0
  40. data/app/views/password_resets/edit.html.haml +71 -0
  41. data/app/views/password_resets/new.html.haml +31 -0
  42. data/app/views/reader_activations/_activation_required.html.haml +34 -0
  43. data/app/views/reader_activations/_on_activation.html.haml +4 -0
  44. data/app/views/reader_activations/show.html.haml +41 -0
  45. data/app/views/reader_notifier/message.html.haml +1 -0
  46. data/app/views/reader_sessions/_login_form.html.haml +59 -0
  47. data/app/views/reader_sessions/new.html.haml +38 -0
  48. data/app/views/readers/_contributions.html.haml +2 -0
  49. data/app/views/readers/_controls.html.haml +25 -0
  50. data/app/views/readers/_extra_controls.html.haml +0 -0
  51. data/app/views/readers/_flasher.html.haml +6 -0
  52. data/app/views/readers/_form.html.haml +73 -0
  53. data/app/views/readers/create.html.haml +28 -0
  54. data/app/views/readers/edit.html.haml +47 -0
  55. data/app/views/readers/index.html.haml +16 -0
  56. data/app/views/readers/login.html.haml +15 -0
  57. data/app/views/readers/new.html.haml +41 -0
  58. data/app/views/readers/permission_denied.html.haml +23 -0
  59. data/app/views/readers/show.html.haml +35 -0
  60. data/app/views/wrappers/_field_errors.html.haml +5 -0
  61. data/config/routes.rb +22 -0
  62. data/config/settings.rb +9 -0
  63. data/db/migrate/001_create_readers.rb +31 -0
  64. data/db/migrate/002_extend_sites.rb +17 -0
  65. data/db/migrate/003_reader_honorifics.rb +12 -0
  66. data/db/migrate/004_user_readers.rb +11 -0
  67. data/db/migrate/005_last_login.rb +15 -0
  68. data/db/migrate/007_adapt_for_authlogic.rb +27 -0
  69. data/db/migrate/20090921125653_reader_messages.rb +27 -0
  70. data/db/migrate/20090924164413_functional_messages.rb +9 -0
  71. data/db/migrate/20090925081225_standard_messages.rb +106 -0
  72. data/db/migrate/20091006102438_message_visibility.rb +9 -0
  73. data/db/migrate/20091010083503_registration_config.rb +10 -0
  74. data/db/migrate/20091019124021_message_functions.rb +9 -0
  75. data/db/migrate/20091020133533_forenames.rb +9 -0
  76. data/db/migrate/20091020135152_contacts.rb +23 -0
  77. data/db/migrate/20091111090819_ensure_functional_messages_visible.rb +9 -0
  78. data/db/migrate/20091119092936_messages_have_layout.rb +9 -0
  79. data/db/migrate/20100922152338_lock_versions.rb +9 -0
  80. data/db/migrate/20100927095703_default_settings.rb +14 -0
  81. data/db/migrate/20101004074945_unlock_version.rb +9 -0
  82. data/lib/config_extensions.rb +5 -0
  83. data/lib/controller_extensions.rb +77 -0
  84. data/lib/reader_admin_ui.rb +64 -0
  85. data/lib/reader_helper.rb +36 -0
  86. data/lib/reader_site.rb +10 -0
  87. data/lib/reader_tags.rb +297 -0
  88. data/lib/rfc822.rb +29 -0
  89. data/lib/tasks/reader_extension_tasks.rake +28 -0
  90. data/pkg/radiant-reader-extension-0.9.0.gem +0 -0
  91. data/public/images/admin/chk_off.png +0 -0
  92. data/public/images/admin/chk_on.png +0 -0
  93. data/public/images/admin/new-message.png +0 -0
  94. data/public/images/admin/new-reader.png +0 -0
  95. data/public/javascripts/admin/messages.js +13 -0
  96. data/public/stylesheets/sass/admin/reader.sass +95 -0
  97. data/radiant-reader-extension.gemspec +184 -0
  98. data/reader_extension.rb +55 -0
  99. data/spec/controllers/admin/messages_controller_spec.rb +38 -0
  100. data/spec/controllers/admin/readers_controller_spec.rb +14 -0
  101. data/spec/controllers/password_resets_controller_spec.rb +140 -0
  102. data/spec/controllers/reader_activations_controller_spec.rb +45 -0
  103. data/spec/controllers/readers_controller_spec.rb +193 -0
  104. data/spec/datasets/messages_dataset.rb +49 -0
  105. data/spec/datasets/reader_layouts_dataset.rb +26 -0
  106. data/spec/datasets/reader_sites_dataset.rb +10 -0
  107. data/spec/datasets/readers_dataset.rb +51 -0
  108. data/spec/lib/reader_admin_ui_spec.rb +35 -0
  109. data/spec/lib/reader_site_spec.rb +18 -0
  110. data/spec/matchers/reader_login_system_matcher.rb +35 -0
  111. data/spec/models/message_spec.rb +109 -0
  112. data/spec/models/reader_notifier_spec.rb +34 -0
  113. data/spec/models/reader_spec.rb +155 -0
  114. data/spec/spec.opts +5 -0
  115. data/spec/spec_helper.rb +48 -0
  116. metadata +267 -0
@@ -0,0 +1,84 @@
1
+ class ReaderActionController < ApplicationController
2
+ helper_method :current_site, :current_site=, :logged_in?, :logged_in_user?, :logged_in_admin?
3
+
4
+ no_login_required
5
+ before_filter :set_site_title
6
+
7
+ # reader session is normally required for modifying actions
8
+ before_filter :require_reader, :except => [:index, :show]
9
+
10
+ radiant_layout { |controller| Radiant::Config['reader.layout'] }
11
+
12
+ # authorisation helpers
13
+
14
+ def logged_in?
15
+ true if current_reader
16
+ end
17
+
18
+ def logged_in_user?
19
+ true if logged_in? && current_reader.is_user?
20
+ end
21
+
22
+ def logged_in_admin?
23
+ true if logged_in_user? && current_reader.admin?
24
+ end
25
+
26
+ def permission_denied
27
+ session[:return_to] ||= request.referer
28
+ @title = flash[:error] || "Sorry: permission denied"
29
+ render
30
+ end
31
+
32
+ protected
33
+
34
+ # context-setters
35
+
36
+ def set_site_title
37
+ if defined? Site && current_site
38
+ @site_title = current_site.name
39
+ @short_site_title = current_site.abbreviation || @site_title
40
+ @site_url = current_site.base_domain
41
+ else
42
+ @site_title = Radiant::Config['site.title']
43
+ @short_site_title = Radiant::Config['site.abbreviation'] || @site_title
44
+ @site_url = request.host
45
+ end
46
+ end
47
+
48
+ def require_reader
49
+ unless set_reader # set_reader is in ControllerExtension and sets Reader.current while checking for current_reader
50
+ store_location
51
+ respond_to do |format|
52
+ format.html { redirect_to reader_login_url }
53
+ format.js {
54
+ @inline = true
55
+ render :partial => 'reader_sessions/login_form'
56
+ }
57
+ end
58
+ false
59
+ end
60
+ end
61
+
62
+ def require_activated_reader
63
+ unless current_reader && current_reader.activated?
64
+ respond_to do |format|
65
+ format.html { redirect_to reader_activation_url }
66
+ format.js {
67
+ @inline = true
68
+ render :partial => 'reader_activations/activation_required'
69
+ }
70
+ end
71
+ false
72
+ end
73
+ end
74
+
75
+ def require_no_reader
76
+ if set_reader
77
+ store_location
78
+ flash[:notice] = "Please log out first"
79
+ redirect_back_or_to url_for(current_reader)
80
+ return false
81
+ end
82
+ end
83
+
84
+ end
@@ -0,0 +1,60 @@
1
+ class ReaderActivationsController < ReaderActionController
2
+
3
+ no_login_required
4
+ skip_before_filter :require_reader
5
+ before_filter :authenticate_reader, :only => [:update]
6
+ before_filter :check_reader_inactive
7
+
8
+ radiant_layout { |controller| Radiant::Config['reader.layout'] }
9
+
10
+ # this is just fake REST: we're actually working on the reader, not an activation object, but in a usefully restricted way:
11
+ # .show sends out an activation message if we can identify the current reader
12
+ # .update activates the reader, if the token is correct
13
+
14
+ def show
15
+ render
16
+ end
17
+
18
+ def new
19
+ if current_reader
20
+ @reader = current_reader
21
+ @reader.send_activation_message
22
+ flash[:notice] = "Account activation instructions have been emailed to you."
23
+ end
24
+ render :action => 'show'
25
+ end
26
+
27
+ def update
28
+ if @reader
29
+ @reader.activate!
30
+ self.current_reader = @reader
31
+ flash[:notice] = "Thank you! Your account has been activated."
32
+ redirect_back_or_to default_activated_url
33
+
34
+ else
35
+ @error = "Sorry: something was wrong in that link. Please check your email message."
36
+ flash[:error] = "Activation failed."
37
+ render :action => 'show'
38
+ end
39
+ end
40
+
41
+ protected
42
+
43
+ def authenticate_reader
44
+ # not using authlogic's find_using_perishable_token because I don't want the token to expire
45
+ @reader = Reader.find_by_id_and_perishable_token(params[:id], params[:activation_code])
46
+ end
47
+
48
+ def check_reader_inactive
49
+ if @reader && @reader.activated?
50
+ flash[:notice] = "Hello #{@reader.name}! Your account is already active."
51
+ redirect_back_or_to default_activated_url
52
+ false
53
+ end
54
+ end
55
+
56
+ def default_activated_url
57
+ reader_url(@reader)
58
+ end
59
+
60
+ end
@@ -0,0 +1,56 @@
1
+ class ReaderSessionsController < ReaderActionController
2
+
3
+ before_filter :require_reader, :only => :destroy
4
+ radiant_layout { |controller| Radiant::Config['reader.layout'] }
5
+
6
+ def new
7
+ @reader_session = ReaderSession.new
8
+ end
9
+
10
+ def create
11
+ @reader_session = ReaderSession.new(params[:reader_session])
12
+ if @reader_session.save
13
+ if @reader_session.reader.activated? && @reader_session.reader.clear_password
14
+ @reader_session.reader.clear_password = "" # we forget the cleartext version on the first successful login after activation
15
+ @reader_session.reader.save(false)
16
+ end
17
+ respond_to do |format|
18
+ format.html {
19
+ flash[:notice] = "Hello #{@reader_session.reader.name}. Welcome back."
20
+ redirect_back_or_to default_loggedin_url
21
+ }
22
+ format.js {
23
+ redirect_back_with_format(:js)
24
+ }
25
+ end
26
+
27
+ else
28
+ respond_to do |format|
29
+ format.html {
30
+ flash[:error] = "Sorry: that combination of login and password is not known here."
31
+ render :action => :new
32
+ }
33
+ format.js { render :action => :new, :layout => false }
34
+ end
35
+ end
36
+ end
37
+
38
+ def destroy
39
+ current_reader_session.destroy
40
+ if current_user
41
+ cookies[:session_token] = { :expires => 1.day.ago }
42
+ current_user.forget_me
43
+ session['user_id'] = nil
44
+ current_user = nil
45
+ end
46
+ flash[:notice] = "You are logged out. Bye!"
47
+ redirect_back_or_to reader_login_url
48
+ end
49
+
50
+ protected
51
+
52
+ def default_loggedin_url
53
+ reader_url(@reader_session.reader)
54
+ end
55
+
56
+ end
@@ -0,0 +1,131 @@
1
+ class ReadersController < ReaderActionController
2
+ @@extended_form_partials = []
3
+ cattr_accessor :extended_form_partials
4
+
5
+ before_filter :check_registration_allowed, :only => [:new, :create]
6
+ before_filter :initialize_form_partials, :only => [:new, :edit, :update, :create]
7
+ before_filter :i_am_me, :only => [:show]
8
+ before_filter :require_reader, :except => [:index, :new, :create, :activate]
9
+ before_filter :restrict_to_self, :only => [:edit, :update, :resend_activation]
10
+ before_filter :no_removing, :only => [:remove, :destroy]
11
+ before_filter :require_password, :only => [:update]
12
+
13
+ def index
14
+ @readers = Reader.paginate(:page => params[:page], :order => 'readers.created_at desc')
15
+ end
16
+
17
+ def show
18
+ @reader = Reader.find(params[:id])
19
+ respond_to do |format|
20
+ format.html {
21
+ if @reader.inactive? && @reader == current_reader
22
+ redirect_to reader_activation_url
23
+ else
24
+ render
25
+ end
26
+ }
27
+ format.js {
28
+ @inline = true
29
+ render :partial => 'readers/controls'
30
+ }
31
+ end
32
+
33
+ end
34
+
35
+ def new
36
+ if current_reader
37
+ flash[:error] = "You're already logged in!"
38
+ redirect_to url_for(current_reader) and return
39
+ end
40
+ @reader = Reader.new
41
+ session[:return_to] = request.referer
42
+ session[:email_field] = @email_field = @reader.generate_email_field_name
43
+ end
44
+
45
+ def edit
46
+ end
47
+
48
+ def create
49
+ @reader = Reader.new(params[:reader])
50
+ @reader.clear_password = params[:reader][:password]
51
+
52
+ unless @reader.email.blank?
53
+ flash[:error] = "Please don't fill in the spam trap field."
54
+ @reader.email = ''
55
+ @reader.errors.add(:trap, "must be empty")
56
+ render :action => 'new' and return
57
+ end
58
+
59
+ unless @email_field = session[:email_field]
60
+ flash[:error] = "Please use the registration form."
61
+ redirect_to new_reader_url and return
62
+ end
63
+
64
+ @reader.email = params[@email_field.intern]
65
+ if (@reader.valid?)
66
+ @reader.save!
67
+ @reader.send_activation_message
68
+ self.current_reader = @reader
69
+ redirect_to reader_activation_url
70
+ else
71
+ render :action => 'new'
72
+ end
73
+ end
74
+
75
+ def update
76
+ @reader.attributes = params[:reader]
77
+ @reader.clear_password = params[:reader][:password] if params[:reader][:password]
78
+ if @reader.save
79
+ flash[:notice] = "Your account has been updated"
80
+ redirect_to url_for(@reader)
81
+ else
82
+ render :action => 'edit'
83
+ end
84
+ end
85
+
86
+ protected
87
+
88
+ def i_am_me
89
+ params[:id] = current_reader.id if params[:id] == 'me' && current_reader
90
+ end
91
+
92
+ def restrict_to_self
93
+ flash[:error] = "Sorry. You are not allowed to edit other people's accounts." if params[:id] && params[:id] != current_reader.id
94
+ @reader = current_reader
95
+ end
96
+
97
+ def require_password
98
+ return true if @reader.valid_password?(params[:reader][:current_password])
99
+
100
+ # might as well get any other validation messages while we're at it
101
+ @reader.attributes = params[:reader]
102
+ @reader.valid?
103
+
104
+ flash[:error] = 'Sorry. Wrong password.'
105
+ @reader.errors.add(:current_password, "was not correct")
106
+ render :action => 'edit' and return false
107
+ end
108
+
109
+ def no_removing
110
+ flash[:error] = "You can't delete readers here. Please log in to the admin interface."
111
+ redirect_to admin_readers_url
112
+ end
113
+
114
+ def check_registration_allowed
115
+ unless Radiant::Config['reader.allow_registration?']
116
+ flash[:error] = "Sorry. This site does not allow public registration."
117
+ redirect_to reader_login_url
118
+ false
119
+ end
120
+ end
121
+
122
+ def self.add_form_partial(path)
123
+ extended_form_partials.push(path)
124
+ end
125
+
126
+ private
127
+ def initialize_form_partials
128
+ @form_partials = extended_form_partials
129
+ end
130
+
131
+ end
@@ -0,0 +1,36 @@
1
+ module Admin::ReaderSettingsHelper
2
+
3
+ def editable_setting(setting)
4
+ setting = Radiant::Config.find_by_key(setting) unless setting.is_a? Radiant::Config
5
+ domkey = setting.key.gsub('?', '_')
6
+ containerid = "set_#{domkey}"
7
+ link = link_to_remote setting.value,
8
+ :url => edit_admin_reader_setting_url(setting.id),
9
+ :method => 'get',
10
+ :update => containerid,
11
+ :loading => "$('#{containerid}').addClassName('waiting');",
12
+ :loaded => "$('#{containerid}').removeClassName('waiting');"
13
+ %{
14
+ #{link}
15
+ }
16
+ end
17
+
18
+ def checkbox_for_setting(setting, label)
19
+ setting = Radiant::Config.find_by_key(setting) unless setting.is_a? Radiant::Config
20
+ domkey = setting.key.gsub('?', '_')
21
+ containerid = "set_#{domkey}"
22
+ checkbox = check_box_tag setting.key.to_sym, 1, setting.value, :class => 'fancy', :id => domkey, :onchange => remote_function(
23
+ :url => admin_reader_setting_path(setting.id),
24
+ :with => %{'value=' + (this.checked ? 'true' : 'false')},
25
+ :method => 'put',
26
+ :loading => "$('#{containerid}').addClassName('waiting');",
27
+ :success => "$('#{containerid}').removeClassName('waiting').toggleClassName('true').toggleClassName('false');"
28
+ )
29
+ %{
30
+ #{checkbox}
31
+ <label for="#{domkey}">#{label}</label>
32
+ }
33
+
34
+ end
35
+
36
+ end
@@ -0,0 +1,108 @@
1
+ class Message < ActiveRecord::Base
2
+
3
+ is_site_scoped if defined? ActiveRecord::SiteNotFound
4
+
5
+ belongs_to :layout
6
+ belongs_to :created_by, :class_name => 'User'
7
+ belongs_to :updated_by, :class_name => 'User'
8
+
9
+ has_many :message_readers
10
+ has_many :readers, :through => :message_readers
11
+
12
+ 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
+ has_many :recipients, :through => :deliveries, :source => :reader
14
+
15
+ validates_presence_of :subject
16
+ validates_presence_of :body
17
+
18
+ object_id_attr :filter, TextFilter
19
+
20
+ default_scope :order => 'updated_at DESC, created_at DESC'
21
+ named_scope :for_function, lambda { |f| {:conditions => ["function_id = ?", f.to_s]} }
22
+ named_scope :administrative, { :conditions => "function_id IS NOT NULL" }
23
+ named_scope :ordinary, { :conditions => "function_id IS NULL" }
24
+ named_scope :published, { :conditions => "status_id >= 100" }
25
+
26
+ def filtered_body
27
+ filter.filter(body)
28
+ end
29
+
30
+ # has to return a named_scope for chainability
31
+ def possible_readers
32
+ Reader.active
33
+ end
34
+
35
+ def undelivered_readers
36
+ possible_readers - recipients
37
+ end
38
+
39
+ def inactive_readers
40
+ possible_readers.inactive
41
+ end
42
+
43
+ def active_readers
44
+ possible_readers.active
45
+ end
46
+
47
+ def delivered?
48
+ deliveries.any?
49
+ end
50
+
51
+ def delivered_to?(reader)
52
+ recipients.include?(reader)
53
+ end
54
+
55
+ def preview(reader=nil)
56
+ reader ||= possible_readers.first || Reader.find_or_create_for_user(created_by)
57
+ ReaderNotifier.create_message(reader, self)
58
+ end
59
+
60
+ def function
61
+ MessageFunction[self.function_id]
62
+ end
63
+ def self.functional(function)
64
+ for_function(MessageFunction[function]).first
65
+ end
66
+ def has_function?
67
+ !function.nil?
68
+ end
69
+ def administrative?
70
+ has_function?
71
+ end
72
+
73
+ def status
74
+ Status.find(self.status_id)
75
+ end
76
+ def status=(value)
77
+ self.status_id = value.id
78
+ end
79
+ def published?
80
+ status == Status[:published]
81
+ end
82
+ def published!
83
+ status = Status[:published]
84
+ end
85
+
86
+ def deliver(readers)
87
+ failures = []
88
+ readers.each do |reader|
89
+ failures.push(reader) unless deliver_to(reader)
90
+ end
91
+ self.published!
92
+ failures
93
+ end
94
+
95
+ def deliver_to(reader, sender=nil)
96
+ ReaderNotifier.deliver_message(reader, self, sender)
97
+ record_delivery(reader)
98
+ true
99
+ rescue => e
100
+ logger.warn "@@ delivery failed: #{e.inspect}"
101
+ raise
102
+ end
103
+
104
+ def record_delivery(reader)
105
+ MessageReader.find_or_create_by_message_id_and_reader_id(self.id, reader.id).update_attribute(:sent_at, Time.now)
106
+ end
107
+
108
+ end