mail_form 1.2.0 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +1 -5
- data/lib/mail_form.rb +2 -21
- data/lib/mail_form/base.rb +7 -5
- data/lib/mail_form/delivery.rb +149 -147
- data/lib/mail_form/notifier.rb +25 -0
- data/lib/mail_form/shim.rb +46 -44
- data/lib/mail_form/version.rb +1 -1
- data/lib/{views → mail_form/views}/mail_form/contact.erb +0 -0
- data/test/mail_form_test.rb +3 -3
- metadata +14 -6
data/README.rdoc
CHANGED
@@ -168,9 +168,7 @@ request object responds to.
|
|
168
168
|
== I18n
|
169
169
|
|
170
170
|
I18n in MailForm works like in ActiveRecord, so all models, attributes and messages
|
171
|
-
can be used with localized.
|
172
|
-
requires on I18n >= 0.2.0 since it uses the ability to symlink translations. Below
|
173
|
-
is an I18n example file:
|
171
|
+
can be used with localized. Below is an I18n file example file:
|
174
172
|
|
175
173
|
mail_form:
|
176
174
|
models:
|
@@ -180,8 +178,6 @@ is an I18n example file:
|
|
180
178
|
email: "E-mail"
|
181
179
|
telephone: "Telephone number"
|
182
180
|
message: "Sent message"
|
183
|
-
errors:
|
184
|
-
messages: :"activerecord.errors.messages"
|
185
181
|
request:
|
186
182
|
title: "Technical information about the user"
|
187
183
|
remote_ip: "IP Address"
|
data/lib/mail_form.rb
CHANGED
@@ -1,26 +1,7 @@
|
|
1
|
-
|
1
|
+
module MailForm
|
2
2
|
autoload :Base, 'mail_form/base'
|
3
3
|
autoload :Callbacks, 'mail_form/callbacks'
|
4
4
|
autoload :Delivery, 'mail_form/delivery'
|
5
|
+
autoload :Notifier, 'mail_form/notifier'
|
5
6
|
autoload :Shim, 'mail_form/shim'
|
6
|
-
|
7
|
-
append_view_path File.expand_path('../views', __FILE__)
|
8
|
-
|
9
|
-
def contact(resource)
|
10
|
-
if resource.request.nil? && resource.class.mail_appendable.any?
|
11
|
-
raise ScriptError, "You set :append values but forgot to give me the request object"
|
12
|
-
end
|
13
|
-
|
14
|
-
@resource = @form = resource
|
15
|
-
|
16
|
-
resource.class.mail_attachments.each do |attribute|
|
17
|
-
value = resource.send(attribute)
|
18
|
-
next unless value.respond_to?(:read)
|
19
|
-
attachments[value.original_filename] = value.read
|
20
|
-
end
|
21
|
-
|
22
|
-
headers = resource.headers
|
23
|
-
headers[:from] ||= resource.email
|
24
|
-
mail(headers)
|
25
|
-
end
|
26
7
|
end
|
data/lib/mail_form/base.rb
CHANGED
@@ -1,8 +1,10 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
module MailForm
|
2
|
+
class Base
|
3
|
+
include MailForm::Shim
|
4
|
+
include MailForm::Delivery
|
4
5
|
|
5
|
-
|
6
|
-
|
6
|
+
def self.lookup_ancestors
|
7
|
+
super - [MailForm::Base]
|
8
|
+
end
|
7
9
|
end
|
8
10
|
end
|
data/lib/mail_form/delivery.rb
CHANGED
@@ -1,175 +1,177 @@
|
|
1
|
-
module MailForm
|
2
|
-
|
1
|
+
module MailForm
|
2
|
+
module Delivery
|
3
|
+
extend ActiveSupport::Concern
|
3
4
|
|
4
|
-
|
5
|
-
|
6
|
-
|
5
|
+
included do
|
6
|
+
class_attribute :mail_attributes
|
7
|
+
self.mail_attributes = []
|
7
8
|
|
8
|
-
|
9
|
-
|
9
|
+
class_attribute :mail_captcha
|
10
|
+
self.mail_captcha = []
|
10
11
|
|
11
|
-
|
12
|
-
|
12
|
+
class_attribute :mail_attachments
|
13
|
+
self.mail_attachments = []
|
13
14
|
|
14
|
-
|
15
|
-
|
15
|
+
class_attribute :mail_appendable
|
16
|
+
self.mail_appendable = []
|
16
17
|
|
17
|
-
|
18
|
-
|
18
|
+
before_create :not_spam?
|
19
|
+
after_create :deliver!
|
19
20
|
|
20
|
-
|
21
|
-
|
21
|
+
attr_accessor :request
|
22
|
+
alias :deliver :create
|
22
23
|
|
23
|
-
|
24
|
-
end
|
25
|
-
|
26
|
-
module Deprecated
|
27
|
-
def subject(duck=nil, &block)
|
28
|
-
ActiveSupport::Deprecation.warn "subject is deprecated. Please define a headers method " <<
|
29
|
-
"in your instance which returns a hash with :subject as key instead.", caller
|
24
|
+
extend Deprecated
|
30
25
|
end
|
31
26
|
|
32
|
-
|
33
|
-
|
34
|
-
"
|
35
|
-
|
36
|
-
|
27
|
+
module Deprecated
|
28
|
+
def subject(duck=nil, &block)
|
29
|
+
ActiveSupport::Deprecation.warn "subject is deprecated. Please define a headers method " <<
|
30
|
+
"in your instance which returns a hash with :subject as key instead.", caller
|
31
|
+
end
|
37
32
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
33
|
+
def sender(duck=nil, &block)
|
34
|
+
ActiveSupport::Deprecation.warn "from/sender is deprecated. Please define a headers method " <<
|
35
|
+
"in your instance which returns a hash with :from as key instead.", caller
|
36
|
+
end
|
37
|
+
alias :from :sender
|
43
38
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
39
|
+
def recipients(duck=nil, &block)
|
40
|
+
ActiveSupport::Deprecation.warn "to/recipients is deprecated. Please define a headers method " <<
|
41
|
+
"in your instance which returns a hash with :to as key instead.", caller
|
42
|
+
end
|
43
|
+
alias :to :recipients
|
48
44
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
end
|
45
|
+
def headers(hash)
|
46
|
+
ActiveSupport::Deprecation.warn "headers is deprecated. Please define a headers method " <<
|
47
|
+
"in your instance which returns the desired headers instead.", caller
|
48
|
+
end
|
54
49
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
#
|
59
|
-
# == Options
|
60
|
-
#
|
61
|
-
# * :validate - A hook to validates_*_of. When true is given, validates the
|
62
|
-
# presence of the attribute. When a regexp, validates format. When array,
|
63
|
-
# validates the inclusion of the attribute in the array.
|
64
|
-
#
|
65
|
-
# Whenever :validate is given, the presence is automatically checked. Give
|
66
|
-
# :allow_blank => true to override.
|
67
|
-
#
|
68
|
-
# Finally, when :validate is a symbol, the method given as symbol will be
|
69
|
-
# called. Then you can add validations as you do in ActiveRecord (errors.add).
|
70
|
-
#
|
71
|
-
# * <tt>:attachment</tt> - When given, expects a file to be sent and attaches
|
72
|
-
# it to the e-mail. Don't forget to set your form to multitype.
|
73
|
-
#
|
74
|
-
# * <tt>:captcha</tt> - When true, validates the attributes must be blank
|
75
|
-
# This is a simple way to avoid spam
|
76
|
-
#
|
77
|
-
# == Examples
|
78
|
-
#
|
79
|
-
# class ContactForm < MailForm
|
80
|
-
# attributes :name, :validate => true
|
81
|
-
# attributes :email, :validate => /^([^@]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i
|
82
|
-
# attributes :type, :validate => ["General", "Interface bug"]
|
83
|
-
# attributes :message
|
84
|
-
# attributes :screenshot, :attachment => true, :validate => :interface_bug?
|
85
|
-
# attributes :nickname, :captcha => true
|
86
|
-
#
|
87
|
-
# def interface_bug?
|
88
|
-
# if type == 'Interface bug' && screenshot.nil?
|
89
|
-
# self.errors.add(:screenshot, "can't be blank when you are reporting an interface bug")
|
90
|
-
# end
|
91
|
-
# end
|
92
|
-
# end
|
93
|
-
#
|
94
|
-
def attribute(*accessors)
|
95
|
-
options = accessors.extract_options!
|
96
|
-
attr_accessor *(accessors - instance_methods.map(&:to_sym))
|
97
|
-
|
98
|
-
if options[:attachment]
|
99
|
-
self.mail_attachments += accessors
|
100
|
-
elsif options[:captcha]
|
101
|
-
self.mail_captcha += accessors
|
102
|
-
else
|
103
|
-
self.mail_attributes += accessors
|
50
|
+
def template(new_template)
|
51
|
+
ActiveSupport::Deprecation.warn "template is deprecated. Please define a headers method " <<
|
52
|
+
"in your instance which returns a hash with :template_name as key instead.", caller
|
104
53
|
end
|
54
|
+
end
|
105
55
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
56
|
+
module ClassMethods
|
57
|
+
# Declare your form attributes. All attributes declared here will be appended
|
58
|
+
# to the e-mail, except the ones captcha is true.
|
59
|
+
#
|
60
|
+
# == Options
|
61
|
+
#
|
62
|
+
# * :validate - A hook to validates_*_of. When true is given, validates the
|
63
|
+
# presence of the attribute. When a regexp, validates format. When array,
|
64
|
+
# validates the inclusion of the attribute in the array.
|
65
|
+
#
|
66
|
+
# Whenever :validate is given, the presence is automatically checked. Give
|
67
|
+
# :allow_blank => true to override.
|
68
|
+
#
|
69
|
+
# Finally, when :validate is a symbol, the method given as symbol will be
|
70
|
+
# called. Then you can add validations as you do in ActiveRecord (errors.add).
|
71
|
+
#
|
72
|
+
# * <tt>:attachment</tt> - When given, expects a file to be sent and attaches
|
73
|
+
# it to the e-mail. Don't forget to set your form to multitype.
|
74
|
+
#
|
75
|
+
# * <tt>:captcha</tt> - When true, validates the attributes must be blank
|
76
|
+
# This is a simple way to avoid spam
|
77
|
+
#
|
78
|
+
# == Examples
|
79
|
+
#
|
80
|
+
# class ContactForm < MailForm
|
81
|
+
# attributes :name, :validate => true
|
82
|
+
# attributes :email, :validate => /^([^@]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i
|
83
|
+
# attributes :type, :validate => ["General", "Interface bug"]
|
84
|
+
# attributes :message
|
85
|
+
# attributes :screenshot, :attachment => true, :validate => :interface_bug?
|
86
|
+
# attributes :nickname, :captcha => true
|
87
|
+
#
|
88
|
+
# def interface_bug?
|
89
|
+
# if type == 'Interface bug' && screenshot.nil?
|
90
|
+
# self.errors.add(:screenshot, "can't be blank when you are reporting an interface bug")
|
91
|
+
# end
|
92
|
+
# end
|
93
|
+
# end
|
94
|
+
#
|
95
|
+
def attribute(*accessors)
|
96
|
+
options = accessors.extract_options!
|
97
|
+
attr_accessor *(accessors - instance_methods.map(&:to_sym))
|
98
|
+
|
99
|
+
if options[:attachment]
|
100
|
+
self.mail_attachments += accessors
|
101
|
+
elsif options[:captcha]
|
102
|
+
self.mail_captcha += accessors
|
103
|
+
else
|
104
|
+
self.mail_attributes += accessors
|
120
105
|
end
|
121
106
|
|
122
|
-
|
107
|
+
validation = options.delete(:validate)
|
108
|
+
return unless validation
|
109
|
+
|
110
|
+
accessors.each do |accessor|
|
111
|
+
case validation
|
112
|
+
when Symbol, Class
|
113
|
+
validate validation
|
114
|
+
break
|
115
|
+
when Regexp
|
116
|
+
validates_format_of accessor, :with => validation, :allow_blank => true
|
117
|
+
when Array
|
118
|
+
validates_inclusion_of accessor, :in => validation, :allow_blank => true
|
119
|
+
when Range
|
120
|
+
validates_length_of accessor, :within => validation, :allow_blank => true
|
121
|
+
end
|
122
|
+
|
123
|
+
validates_presence_of accessor unless options[:allow_blank] == true
|
124
|
+
end
|
125
|
+
end
|
126
|
+
alias :attributes :attribute
|
127
|
+
|
128
|
+
# Values from request object to be appended to the contact form.
|
129
|
+
# Whenever used, you have to send the request object when initializing the object:
|
130
|
+
#
|
131
|
+
# @contact_form = ContactForm.new(params[:contact_form], request)
|
132
|
+
#
|
133
|
+
# You can get the values to be appended from the AbstractRequest
|
134
|
+
# documentation (http://api.rubyonrails.org/classes/ActionController/AbstractRequest.html)
|
135
|
+
#
|
136
|
+
# == Examples
|
137
|
+
#
|
138
|
+
# class ContactForm < MailForm
|
139
|
+
# append :remote_ip, :user_agent, :session, :cookies
|
140
|
+
# end
|
141
|
+
#
|
142
|
+
def append(*values)
|
143
|
+
self.mail_appendable += values
|
123
144
|
end
|
124
145
|
end
|
125
|
-
alias :attributes :attribute
|
126
146
|
|
127
|
-
#
|
128
|
-
#
|
129
|
-
#
|
130
|
-
# @contact_form = ContactForm.new(params[:contact_form], request)
|
147
|
+
# In development, raises an error if the captcha field is not blank. This is
|
148
|
+
# is good to remember that the field should be hidden with CSS and shown only
|
149
|
+
# to robots.
|
131
150
|
#
|
132
|
-
#
|
133
|
-
#
|
151
|
+
# In test and in production, it returns true if all captcha fields are blank,
|
152
|
+
# returns false otherwise.
|
134
153
|
#
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
end
|
145
|
-
|
146
|
-
# In development, raises an error if the captcha field is not blank. This is
|
147
|
-
# is good to remember that the field should be hidden with CSS and shown only
|
148
|
-
# to robots.
|
149
|
-
#
|
150
|
-
# In test and in production, it returns true if all captcha fields are blank,
|
151
|
-
# returns false otherwise.
|
152
|
-
#
|
153
|
-
def spam?
|
154
|
-
self.class.mail_captcha.each do |field|
|
155
|
-
next if send(field).blank?
|
156
|
-
|
157
|
-
if defined?(Rails) && Rails.env.development?
|
158
|
-
raise ScriptError, "The captcha field #{field} was supposed to be blank"
|
159
|
-
else
|
160
|
-
return true
|
154
|
+
def spam?
|
155
|
+
self.class.mail_captcha.each do |field|
|
156
|
+
next if send(field).blank?
|
157
|
+
|
158
|
+
if defined?(Rails) && Rails.env.development?
|
159
|
+
raise ScriptError, "The captcha field #{field} was supposed to be blank"
|
160
|
+
else
|
161
|
+
return true
|
162
|
+
end
|
161
163
|
end
|
162
|
-
end
|
163
164
|
|
164
|
-
|
165
|
-
|
165
|
+
false
|
166
|
+
end
|
166
167
|
|
167
|
-
|
168
|
-
|
169
|
-
|
168
|
+
def not_spam?
|
169
|
+
!spam?
|
170
|
+
end
|
170
171
|
|
171
|
-
|
172
|
-
|
173
|
-
|
172
|
+
# Deliver the resource without checking any condition.
|
173
|
+
def deliver!
|
174
|
+
MailForm::Notifier.contact(self).deliver
|
175
|
+
end
|
174
176
|
end
|
175
177
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module MailForm
|
2
|
+
class Notifier < ActionMailer::Base
|
3
|
+
self.mailer_name = "mail_form"
|
4
|
+
append_view_path File.expand_path('../views', __FILE__)
|
5
|
+
|
6
|
+
def contact(resource)
|
7
|
+
if resource.request.nil? && resource.class.mail_appendable.any?
|
8
|
+
raise ScriptError, "You set :append values but forgot to give me the request object"
|
9
|
+
end
|
10
|
+
|
11
|
+
@resource = @form = resource
|
12
|
+
|
13
|
+
resource.class.mail_attachments.each do |attribute|
|
14
|
+
value = resource.send(attribute)
|
15
|
+
next unless value.respond_to?(:read)
|
16
|
+
attachments[value.original_filename] = value.read
|
17
|
+
end
|
18
|
+
|
19
|
+
headers = resource.headers
|
20
|
+
headers[:from] ||= resource.email
|
21
|
+
headers[:subject] ||= resource.class.model_name.human
|
22
|
+
mail(headers)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/mail_form/shim.rb
CHANGED
@@ -1,59 +1,61 @@
|
|
1
1
|
require 'active_model'
|
2
2
|
|
3
3
|
# This the module which makes any class behave like ActiveModel.
|
4
|
-
module MailForm
|
5
|
-
|
6
|
-
base
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
4
|
+
module MailForm
|
5
|
+
module Shim
|
6
|
+
def self.included(base)
|
7
|
+
base.class_eval do
|
8
|
+
extend ActiveModel::Naming
|
9
|
+
extend ActiveModel::Translation
|
10
|
+
extend ActiveModel::Callbacks
|
11
|
+
include ActiveModel::Validations
|
12
|
+
include ActiveModel::Conversion
|
13
|
+
|
14
|
+
extend MailForm::Shim::ClassMethods
|
15
|
+
define_model_callbacks :create
|
16
|
+
end
|
15
17
|
end
|
16
|
-
end
|
17
18
|
|
18
|
-
|
19
|
-
|
20
|
-
|
19
|
+
module ClassMethods
|
20
|
+
def i18n_scope
|
21
|
+
:mail_form
|
22
|
+
end
|
21
23
|
end
|
22
|
-
end
|
23
24
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
25
|
+
# Initialize assigning the parameters given as hash.
|
26
|
+
def initialize(params={})
|
27
|
+
params.each_pair do |attr, value|
|
28
|
+
self.send(:"#{attr}=", value)
|
29
|
+
end unless params.blank?
|
30
|
+
end
|
30
31
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
32
|
+
# Returns a hash of attributes, according to the attributes existent in
|
33
|
+
# self.class.mail_attributes.
|
34
|
+
def attributes
|
35
|
+
self.class.mail_attributes.inject({}) do |hash, attr|
|
36
|
+
hash[attr.to_s] = send(attr)
|
37
|
+
hash
|
38
|
+
end
|
37
39
|
end
|
38
|
-
end
|
39
40
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
41
|
+
# Always return true so when using form_for, the default method will be post.
|
42
|
+
def new_record?
|
43
|
+
true
|
44
|
+
end
|
44
45
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
46
|
+
# Always return nil so when using form_for, the default method will be post.
|
47
|
+
def id
|
48
|
+
nil
|
49
|
+
end
|
49
50
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
51
|
+
# Create just check validity, and if so, trigger callbacks.
|
52
|
+
def create
|
53
|
+
if valid?
|
54
|
+
_run_create_callbacks { true }
|
55
|
+
else
|
56
|
+
false
|
57
|
+
end
|
56
58
|
end
|
59
|
+
alias :save :create
|
57
60
|
end
|
58
|
-
alias :save :create
|
59
61
|
end
|
data/lib/mail_form/version.rb
CHANGED
File without changes
|
data/test/mail_form_test.rb
CHANGED
@@ -21,14 +21,14 @@ class MailFormNotifierTest < ActiveSupport::TestCase
|
|
21
21
|
assert_equal 1, ActionMailer::Base.deliveries.size
|
22
22
|
end
|
23
23
|
|
24
|
-
def
|
24
|
+
def test_subject_defaults_to_human_class_name
|
25
25
|
@form.deliver
|
26
|
-
assert_equal 'Contact', first_delivery.subject
|
26
|
+
assert_equal 'Contact form', first_delivery.subject
|
27
27
|
end
|
28
28
|
|
29
29
|
def test_body_contains_subject
|
30
30
|
@form.deliver
|
31
|
-
assert_match /Contact/, first_delivery.body.to_s
|
31
|
+
assert_match /Contact form/, first_delivery.body.to_s
|
32
32
|
end
|
33
33
|
|
34
34
|
def test_body_contains_attributes_values
|
metadata
CHANGED
@@ -1,7 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mail_form
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 1
|
7
|
+
- 2
|
8
|
+
- 1
|
9
|
+
version: 1.2.1
|
5
10
|
platform: ruby
|
6
11
|
authors:
|
7
12
|
- "Jos\xC3\xA9 Valim"
|
@@ -10,7 +15,7 @@ autorequire:
|
|
10
15
|
bindir: bin
|
11
16
|
cert_chain: []
|
12
17
|
|
13
|
-
date: 2010-
|
18
|
+
date: 2010-04-03 00:00:00 +02:00
|
14
19
|
default_executable:
|
15
20
|
dependencies: []
|
16
21
|
|
@@ -30,9 +35,10 @@ files:
|
|
30
35
|
- lib/mail_form.rb
|
31
36
|
- lib/mail_form/base.rb
|
32
37
|
- lib/mail_form/delivery.rb
|
38
|
+
- lib/mail_form/notifier.rb
|
33
39
|
- lib/mail_form/shim.rb
|
34
40
|
- lib/mail_form/version.rb
|
35
|
-
- lib/views/mail_form/contact.erb
|
41
|
+
- lib/mail_form/views/mail_form/contact.erb
|
36
42
|
has_rdoc: true
|
37
43
|
homepage: http://github.com/plataformatec/mail_form
|
38
44
|
licenses: []
|
@@ -46,18 +52,20 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
46
52
|
requirements:
|
47
53
|
- - ">="
|
48
54
|
- !ruby/object:Gem::Version
|
55
|
+
segments:
|
56
|
+
- 0
|
49
57
|
version: "0"
|
50
|
-
version:
|
51
58
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
52
59
|
requirements:
|
53
60
|
- - ">="
|
54
61
|
- !ruby/object:Gem::Version
|
62
|
+
segments:
|
63
|
+
- 0
|
55
64
|
version: "0"
|
56
|
-
version:
|
57
65
|
requirements: []
|
58
66
|
|
59
67
|
rubyforge_project:
|
60
|
-
rubygems_version: 1.3.
|
68
|
+
rubygems_version: 1.3.6
|
61
69
|
signing_key:
|
62
70
|
specification_version: 3
|
63
71
|
summary: Send e-mail straight from forms in Rails with I18n, validations, attachments and request information.
|