radiant-reader-extension 0.9.2

Sign up to get free protection for your applications and to get access to all the features.
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
File without changes
@@ -0,0 +1,6 @@
1
+ - if flash[:notice]
2
+ .notice
3
+ %p= flash[:notice]
4
+ - if flash[:error]
5
+ .error
6
+ %p= flash[:error]
@@ -0,0 +1,73 @@
1
+ - with_error_report(@reader.errors.on(:name)) do
2
+ = f.label :name, 'Your name', :class => 'required'
3
+ %span.formnote as you would like it to appear
4
+ %br
5
+ = f.text_field :name, :class => 'standard'
6
+
7
+ - if Radiant::Config['reader.use_honorifics?']
8
+ - with_error_report(@reader.errors.on(:honorific)) do
9
+ = f.label :honorific, 'Title or rank', :class => 'optional'
10
+ %span.formnote as you would like us to display it when you are credited
11
+ %br
12
+ = f.text_field :honorific, :class => 'standard'
13
+
14
+ - if @reader.new_record? && @email_field
15
+
16
+ - with_error_report(@reader.errors.on(:email)) do
17
+ = f.label @email_field, 'Your email address', :class => 'required'
18
+ %span.formnote this has to work
19
+ %br
20
+ = text_field_tag @email_field, params[@email_field] || @reader.email, :id => "reader_#{@email_field}", :class => 'standard'
21
+
22
+ .innocuous
23
+ - with_error_report(@reader.errors.on(:trap)) do
24
+ %label{ :for => 'reader_email', :class => 'required' }
25
+ Don't fill this in!
26
+ %span.formnote
27
+ it's a spam trap and you weren't supposed to see it
28
+ %br
29
+ = text_field_tag 'reader_email', '', :name => 'reader[email]', :class => 'standard'
30
+
31
+ - else
32
+
33
+ - with_error_report(@reader.errors.on(:email)) do
34
+ = f.label :email, 'Your email address', :class => 'required'
35
+ %br
36
+ = f.text_field :email, :class => 'standard'
37
+
38
+ - with_error_report(@reader.errors.on(:description)) do
39
+ = f.label :description, 'A little about yourself', :class => 'optional'
40
+ %span.formnote
41
+ you can fill this in later.
42
+ Textile formatting
43
+ is allowed.
44
+ %br
45
+ = f.text_area :description, :class => 'standard', :rows => 8
46
+
47
+ - with_error_report(@reader.errors.on(:login)) do
48
+ = f.label :login, 'Login username', :class => 'optional'
49
+ %span.formnote leave blank to use your email address
50
+ %br
51
+ = f.text_field :login, :class => 'standard'
52
+
53
+ - with_error_report(@reader.errors.on(:password)) do
54
+ - label = @reader.new_record? ? 'Password' : 'Change password?'
55
+ - cssclass = @reader.new_record? ? 'required' : 'optional'
56
+ - help = @reader.new_record? ? 'at least four characters, please' : 'leave blank to keep present password. If changing, at least four characters.'
57
+
58
+ = f.label :password, label, :class => cssclass
59
+ %span.formnote
60
+ = help
61
+ %br
62
+ = f.password_field :password, :class => 'standard', :autocomplete => 'off'
63
+
64
+ - with_error_report(@reader.errors.on(:password_confirmation)) do
65
+ = f.label :password_confirmation, "Confirm #{'new ' unless @reader.new_record?}password", :class => @reader.new_record? ? 'required' : 'optional'
66
+ - unless @reader.new_record?
67
+ %span.formnote
68
+ leave blank to keep present password
69
+ %br
70
+ = f.password_field :password_confirmation, :class => 'standard', :autocomplete => 'off'
71
+
72
+ - @form_partials.each do |partial|
73
+ = render :partial => partial
@@ -0,0 +1,28 @@
1
+ = render :partial => 'flasher'
2
+
3
+ %div.reader_show
4
+ - if @reader == current_reader
5
+ %p
6
+ %strong
7
+ Hello
8
+ = @reader.name + '.'
9
+ Thank you for joining us. An email has been sent to
10
+ %strong
11
+ = @reader.email
12
+ with a reminder of your login and password and an activation link.
13
+ Click on that link and the registration process will be complete.
14
+
15
+ %p
16
+ We're sorry about the extra step: it's just to make sure that you are you. If the email hasn't come through yet, you can always go and
17
+ = link_to("give yourself a gravatar", "http://gravatar.com/signup") + '.'
18
+
19
+ - content_for :breadcrumbs do
20
+ = link_to 'Home', '/'
21
+ >
22
+ = link_to 'Readers', '/readers'
23
+ >
24
+ = @reader.name
25
+
26
+ - content_for :pagetitle do
27
+ Thank you. Please check your email
28
+
@@ -0,0 +1,47 @@
1
+ = render :partial => 'flasher'
2
+
3
+ %div.preferences_form
4
+ %p
5
+ Hello
6
+ = link_to "#{@reader.name}.", reader_url(@reader)
7
+ You can use this form to change your preferences and enter a bit more information about yourself.
8
+ First, just to make sure you're you, please enter your current password:
9
+
10
+ - unless @reader.errors.empty?
11
+ %p.errornote
12
+ %strong Sorry: there were problems.
13
+ Please check the form. The affected fields will be highlighted.
14
+
15
+ - form_for :reader, :url => reader_url(@reader), :html => { :method => "put", :class => 'friendly'} do |f|
16
+
17
+ - with_error_report(@reader.errors.on(:current_password)) do
18
+ = f.label :current_password, "Current password", :class => 'required'
19
+ %span.formnote
20
+ can't remember?
21
+ = link_to "Make a new one", new_password_reset_url
22
+ = f.password_field :current_password, :class => 'standard', :autocomplete => 'off'
23
+
24
+ %p
25
+ Fields with bold headings are required and will cause grumbling if left blank:
26
+
27
+ = render :partial => 'form', :locals => {:f => f}
28
+ %p
29
+ = submit_tag "Update your account"
30
+ or
31
+ = link_to 'cancel', request.referer
32
+
33
+ %p
34
+ = link_to gravatar_for(@reader, {:size => 60}, {:class => 'gravatar'}), 'http://www.gravatar.com/signup', :target => '_blank'
35
+ If you would like a picture to appear next to your messages, give yourself a
36
+ = link_to "gravatar.", "http://www.gravatar.com/signup", :target => '_blank'
37
+ This is what appears for you at the moment.
38
+
39
+ - content_for :breadcrumbs do
40
+ = link_to 'Home', '/'
41
+ >
42
+ = link_to 'You', '/readers/me'
43
+ >
44
+ Revise your account
45
+
46
+ - content_for :pagetitle do
47
+ Revise your account
@@ -0,0 +1,16 @@
1
+ = render :partial => 'flasher'
2
+
3
+ %div
4
+ #readers
5
+ - @readers.each do |reader|
6
+ = link_to gravatar_for(reader, {:size => 100}, {:class => 'big_gravatar'}), reader_url(reader), {:title => reader.name}
7
+ %div.stretcher
8
+ %p
9
+
10
+ - content_for :breadcrumbs do
11
+ = link_to 'Home', '/'
12
+ >
13
+ People
14
+
15
+ - content_for :pagetitle do
16
+ People
@@ -0,0 +1,15 @@
1
+ = render :partial => 'readers/flasher'
2
+
3
+ %div.login_form
4
+ %h1
5
+ Log in
6
+
7
+ = render :partial => 'reader_sessions/login_form', :locals => {:backto => @backto}
8
+
9
+ - content_for :breadcrumbs do
10
+ = link_to 'Home', '/'
11
+ > Log in
12
+
13
+ - content_for :pagetitle do
14
+ Log in
15
+
@@ -0,0 +1,41 @@
1
+ = render :partial => 'flasher'
2
+
3
+ %div.register_form
4
+ %p
5
+ If you've already done this, you should only need to
6
+ = link_to "log in.", reader_login_url
7
+ Otherwise, please take a moment to register an account.
8
+ Only the fields labelled in bold are required: the rest you can fill in later if you prefer.
9
+
10
+ - unless @reader.errors.empty?
11
+ %p.errornote
12
+ %strong Sorry: there were problems.
13
+ Please check the form. The affected fields will be highlighted like this.
14
+
15
+ - form_for :reader, :url => readers_url, :html => {:class => 'friendly'} do |f|
16
+ = render :partial => 'form', :locals => {:f => f}
17
+ %p
18
+ = submit_tag "Register"
19
+ or
20
+ = link_to 'cancel', request.referer
21
+
22
+ %h3
23
+ notes
24
+
25
+ %ul.friendly
26
+ %li
27
+ When you press the 'register' button we will send a confirmation email to the address you enter above.
28
+ Click on a link in that message, and you're in.
29
+ %li
30
+ If you would like a picture to appear next to your messages, give yourself a
31
+ = link_to "gravatar.", "http://www.gravatar.com"
32
+ %li
33
+ Please make sure you're happy with our
34
+ = link_to "terms and conditions", "/terms"
35
+
36
+ - content_for :breadcrumbs do
37
+ = link_to 'Home', '/'
38
+ > Register
39
+
40
+ - content_for :pagetitle do
41
+ Register here
@@ -0,0 +1,23 @@
1
+ = render :partial => 'flasher'
2
+
3
+ %div.apologetic
4
+ %p.haserror
5
+ You don't have permission to see the page you just requested.
6
+ - if current_reader
7
+ Please talk to the site administrator about your account.
8
+ - else
9
+ Please log in.
10
+
11
+ - unless current_reader
12
+ = render :partial => 'reader_sessions/login_form'
13
+
14
+ - content_for :breadcrumbs do
15
+ = link_to 'Home', '/'
16
+ >
17
+ = link_to 'Readers', '/readers'
18
+ >
19
+ Permission Denied
20
+
21
+ - content_for :pagetitle do
22
+ = @title
23
+
@@ -0,0 +1,35 @@
1
+ = render :partial => 'flasher'
2
+
3
+ %div.reader_show
4
+ - if @reader == current_reader
5
+ %p.system_message
6
+ %strong
7
+ Hello
8
+ = @reader.name + '.'
9
+ This is the page that other (logged-in) site visitors will see when they click on your name. You can change the text that appears here by
10
+ = link_to "editing your preferences", edit_reader_url(@reader)
11
+ and you can change the picture by
12
+ = link_to "giving yourself a gravatar.", "http://gravatar.com/signup"
13
+
14
+ = gravatar_for(@reader, {:size => 128}, {:class => 'big_gravatar'})
15
+
16
+ - if @reader.description.blank?
17
+ %p No description available.
18
+ - else
19
+ = clean_textilize @reader.description
20
+
21
+ = render :partial => 'contributions'
22
+
23
+ - content_for :breadhead do
24
+ = link_to 'People', '/readers'
25
+
26
+ - content_for :breadcrumbs do
27
+ = link_to 'Home', '/'
28
+ >
29
+ = link_to 'People', '/readers'
30
+ >
31
+ = @reader.name
32
+
33
+ - content_for :pagetitle do
34
+ = @reader.name
35
+
@@ -0,0 +1,5 @@
1
+ %p{ :class => errors ? 'haserror' : '' }
2
+ = yield
3
+ - if errors
4
+ This
5
+ = errors.to_a.to_sentence
data/config/routes.rb ADDED
@@ -0,0 +1,22 @@
1
+ ActionController::Routing::Routes.draw do |map|
2
+ map.namespace :admin do |admin|
3
+ admin.resources :readers, :except => [:show]
4
+ end
5
+
6
+ map.namespace :admin, :path_prefix => 'admin/readers' do |admin|
7
+ admin.resources :messages
8
+ admin.resources :reader_settings, :except => [:destroy]
9
+ end
10
+
11
+ map.resources :readers
12
+ map.resource :reader_session
13
+ map.resource :reader_activation, :only => [:show, :new]
14
+ map.resource :password_reset
15
+
16
+ map.repassword '/password_reset/:id/:confirmation_code', :controller => 'password_resets', :action => 'edit'
17
+ map.activate_me '/activate/:id/:activation_code', :controller => 'reader_activations', :action => 'update'
18
+ map.reader_register '/register', :controller => 'readers', :action => 'new'
19
+ map.reader_login '/login', :controller => 'reader_sessions', :action => 'new'
20
+ map.reader_logout '/logout', :controller => 'reader_sessions', :action => 'destroy'
21
+ map.reader_permission_denied '/permission_denied', :controller => 'readers', :action => 'permission_denied'
22
+ end
@@ -0,0 +1,9 @@
1
+ Radiant::Configuration.add do |config|
2
+ config.namespace :reader do |reader|
3
+ reader.setting :allow_registration?, :label => 'Allow visitors to register'
4
+ reader.setting :require_confirmation?, :label => 'Require confirmation of email address'
5
+ reader.setting :layout, :integer, :options => lambda { Layout.all.collect {|l| [ l.id, l.name ] } }, :label => "Layout", :notes => "Radiant layout used to render reader-administration pages"
6
+ reader.setting :mail_from_name, :validate => :present, :label => "Email name", :notes => "Name of person from whom administrative email seems to come"
7
+ reader.setting :mail_from_address, :validate => :present, :label => "Email address", :notes => "Åddress of person from whom administrative email seems to come"
8
+ end
9
+ end
@@ -0,0 +1,31 @@
1
+ class CreateReaders < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :readers, :force => true do |t|
4
+ t.column :site_id, :integer
5
+ t.column :name, :string, :limit => 100
6
+ t.column :email, :string
7
+ t.column :login, :string, :limit => 40, :default => "", :null => false
8
+ t.column :password, :string, :limit => 40
9
+ t.column :description, :text
10
+ t.column :notes, :text
11
+ t.column :trusted, :boolean, :default => true
12
+ t.column :receive_email, :boolean, :default => false
13
+ t.column :receive_essential_email, :boolean, :default => true
14
+ t.column :created_at, :datetime
15
+ t.column :updated_at, :datetime
16
+ t.column :created_by_id, :integer
17
+ t.column :updated_by_id, :integer
18
+ t.column :salt, :string
19
+ t.column :session_token, :string
20
+ t.column :activation_code, :string
21
+ t.column :provisional_password, :string
22
+ t.column :activated_at, :datetime
23
+ # t.column :lock_version, :integer, :default => 0
24
+ end
25
+ add_index :readers, ["session_token"], :name => "session_token"
26
+ end
27
+
28
+ def self.down
29
+ drop_table :readers
30
+ end
31
+ end
@@ -0,0 +1,17 @@
1
+ class ExtendSites < ActiveRecord::Migration
2
+ def self.up
3
+ if defined? Site
4
+ add_column :sites, :reader_layout_id, :integer
5
+ add_column :sites, :mail_from_name, :string
6
+ add_column :sites, :mail_from_address, :string
7
+ end
8
+ end
9
+
10
+ def self.down
11
+ if defined? Site
12
+ remove_column :sites, :reader_layout_id
13
+ remove_column :sites, :mail_from_name
14
+ remove_column :sites, :mail_from_address
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,12 @@
1
+ class ReaderHonorifics < ActiveRecord::Migration
2
+
3
+ def self.up
4
+ add_column :readers, :honorific, :string
5
+ Radiant::Config['reader.use_honorifics?'] = false
6
+ end
7
+
8
+ def self.down
9
+ remove_column :readers, :honorific
10
+ end
11
+
12
+ end
@@ -0,0 +1,11 @@
1
+ class UserReaders < ActiveRecord::Migration
2
+
3
+ def self.up
4
+ add_column :readers, :user_id, :integer
5
+ end
6
+
7
+ def self.down
8
+ remove_column :readers, :user_id
9
+ end
10
+
11
+ end
@@ -0,0 +1,15 @@
1
+ class LastLogin < ActiveRecord::Migration
2
+
3
+ def self.up
4
+ add_column :readers, :last_seen, :datetime
5
+ add_column :readers, :last_login, :datetime
6
+ add_column :readers, :previous_login, :datetime
7
+ end
8
+
9
+ def self.down
10
+ remove_column :readers, :last_seen
11
+ remove_column :readers, :last_login
12
+ remove_column :readers, :previous_login
13
+ end
14
+
15
+ end
@@ -0,0 +1,27 @@
1
+ class AdaptForAuthlogic < ActiveRecord::Migration
2
+ def self.up
3
+ add_column :readers, :persistence_token, :string, :null => false, :default => ""
4
+ add_column :readers, :single_access_token, :string, :null => false, :default => ""
5
+ add_column :readers, :perishable_token, :string, :null => false, :default => ""
6
+ add_column :readers, :login_count, :integer, :null => false, :default => 0
7
+ add_column :readers, :failed_login_count, :integer, :null => false, :default => 0
8
+ add_column :readers, :current_login_ip, :string
9
+ add_column :readers, :last_login_ip, :string
10
+ add_column :readers, :clear_password, :string
11
+
12
+ change_column :readers, :password, :string, :limit => 255
13
+ change_column :readers, :salt, :string, :limit => 255
14
+
15
+ rename_column :readers, :password, :crypted_password
16
+ rename_column :readers, :salt, :password_salt
17
+ rename_column :readers, :last_seen, :last_request_at
18
+ rename_column :readers, :last_login, :last_login_at
19
+
20
+ remove_column :readers, :activation_code
21
+ remove_column :readers, :previous_login
22
+ end
23
+
24
+ def self.down
25
+
26
+ end
27
+ end