radiant-mailer-extension 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/LICENSE +20 -0
- data/README.rdoc +95 -0
- data/Rakefile +45 -0
- data/VERSION +1 -0
- data/app/controllers/mail_controller.rb +37 -0
- data/app/models/mail.rb +121 -0
- data/app/models/mailer.rb +19 -0
- data/lib/mailer_process.rb +36 -0
- data/lib/mailer_tags.rb +331 -0
- data/lib/radiant-mailer-extension.rb +0 -0
- data/lib/tasks/mailer_extension_tasks.rake +16 -0
- data/mailer_extension.rb +16 -0
- data/radiant-mailer-extension.gemspec +71 -0
- data/spec/controllers/mail_controller_spec.rb +48 -0
- data/spec/dataset/mailer_dataset.rb +17 -0
- data/spec/lib/mailer_process_spec.rb +80 -0
- data/spec/lib/mailer_tags_spec.rb +326 -0
- data/spec/models/mail_spec.rb +212 -0
- data/spec/models/mailer_spec.rb +76 -0
- data/spec/spec.opts +6 -0
- data/spec/spec_helper.rb +36 -0
- metadata +103 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 Nathaniel Talbott, Sean Cribbs, Matt McCray
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
= Mailer Extension for Radiant
|
2
|
+
|
3
|
+
The Mailer extension enables form mail on a page.
|
4
|
+
|
5
|
+
|
6
|
+
== Usage
|
7
|
+
|
8
|
+
You can define email templates using pages parts (email, and/or email_html).
|
9
|
+
You configure the recipients and other Mailer settings in a "mailer" part:
|
10
|
+
|
11
|
+
subject: From the website of Whatever
|
12
|
+
from: noreply@example.com
|
13
|
+
redirect_to: /contact/thank-you
|
14
|
+
recipients:
|
15
|
+
- one@one.com
|
16
|
+
- two@two.com
|
17
|
+
|
18
|
+
The following tags are available to help you build the form:
|
19
|
+
|
20
|
+
<r:mailer:form name=""> ... </r:mailer:form>
|
21
|
+
<r:mailer:text name="" />
|
22
|
+
<r:mailer:checkbox name="" />
|
23
|
+
<r:mailer:radio name="" />
|
24
|
+
<r:mailer:radiogroup name=""> ... </r:mailer:radiogroup>
|
25
|
+
<r:mailer:select name=""> ... </r:mailer:select>
|
26
|
+
<r:mailer:date_select name=""/>
|
27
|
+
<r:mailer:textarea name=""> ... </r:mailer:textarea>
|
28
|
+
<r:mailer:option name="" />
|
29
|
+
|
30
|
+
When processing the form (in email and email_html), the following tags are
|
31
|
+
available:
|
32
|
+
|
33
|
+
<r:mailer:get name="" />
|
34
|
+
<r:mailer:get_each name=""> ... </r:mailer:get_each>
|
35
|
+
<r:mailer:index />
|
36
|
+
|
37
|
+
Simple example of a form:
|
38
|
+
|
39
|
+
<r:mailer:form>
|
40
|
+
<r:mailer:hidden name="subject" value="Email from my Radiant site!" /> <br/>
|
41
|
+
Name:<br/>
|
42
|
+
<r:mailer:text name="name" /> <br/>
|
43
|
+
Message:<br/>
|
44
|
+
<r:mailer:textarea name="message" /> <br/>
|
45
|
+
<input type="submit" value="Send" />
|
46
|
+
</r:mailer:form>
|
47
|
+
|
48
|
+
|
49
|
+
== User-provided Configuration
|
50
|
+
|
51
|
+
Sometimes, rather than explicitly configuring the recipients and such in the mailer part, you'd rather have them passed in by the person submitting the form. Mailer supports this by allowing you to specify a form field to pull the value from:
|
52
|
+
|
53
|
+
from_field: my_form_field_that_contains_the_from_email_address
|
54
|
+
|
55
|
+
Then you just have to add that field to your mailer form and you're all set.
|
56
|
+
|
57
|
+
This is supported for the from (from_field), recipients (recipients_field) and reply_to (reply_to_field) properties.
|
58
|
+
|
59
|
+
|
60
|
+
== Enabling action_mailer
|
61
|
+
|
62
|
+
In environment.rb you'll probably need to change:
|
63
|
+
|
64
|
+
config.frameworks -= [ :action_mailer ]
|
65
|
+
|
66
|
+
to:
|
67
|
+
|
68
|
+
config.frameworks -= []
|
69
|
+
|
70
|
+
|
71
|
+
== Caveats
|
72
|
+
|
73
|
+
Relative urls will almost certainly not work if the mailer fails validation. Solution? Only use absolute urls.
|
74
|
+
|
75
|
+
|
76
|
+
== History
|
77
|
+
|
78
|
+
Created by: M@ McCray - mattmccray.com
|
79
|
+
Version: 0.2.1
|
80
|
+
Contact: mmccray@elucidata.net
|
81
|
+
|
82
|
+
Ported to 'mental' by: Sean Cribbs - seancribbs.com
|
83
|
+
Version: 0.1
|
84
|
+
Contact: seancribbs@gmail.com
|
85
|
+
|
86
|
+
Seriously restructured by: Nathaniel Talbott - terralien.com
|
87
|
+
Version: 0.2
|
88
|
+
Contact: nathaniel@terralien.com
|
89
|
+
Work sponsored by: Ignite Social Media, http://ignitesocialmedia.com/
|
90
|
+
|
91
|
+
|
92
|
+
== Todo
|
93
|
+
|
94
|
+
* Support for file attachments to emails
|
95
|
+
* Tests
|
data/Rakefile
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "radiant-mailer-extension"
|
8
|
+
gem.summary = %Q{Enables form mail on a page.}
|
9
|
+
gem.description = %Q{An extension for Radiant CMS that allows you to create 'contact us' and other mail-bound forms.}
|
10
|
+
gem.email = "radiant@radiantcms.org"
|
11
|
+
gem.homepage = "http://github.com/radiant/radiant-mailer-extension"
|
12
|
+
gem.authors = ["Nathaniel Talbott", "Sean Cribbs", "Matt McCray"]
|
13
|
+
gem.add_development_dependency "rspec"
|
14
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
15
|
+
end
|
16
|
+
Jeweler::GemcutterTasks.new
|
17
|
+
rescue LoadError
|
18
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
19
|
+
end
|
20
|
+
|
21
|
+
require 'spec/rake/spectask'
|
22
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
23
|
+
spec.libs << 'lib' << 'spec'
|
24
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
25
|
+
end
|
26
|
+
|
27
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
28
|
+
spec.libs << 'lib' << 'spec'
|
29
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
30
|
+
spec.rcov = true
|
31
|
+
end
|
32
|
+
|
33
|
+
task :spec => :check_dependencies
|
34
|
+
|
35
|
+
task :default => :spec
|
36
|
+
|
37
|
+
require 'rake/rdoctask'
|
38
|
+
Rake::RDocTask.new do |rdoc|
|
39
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
40
|
+
|
41
|
+
rdoc.rdoc_dir = 'rdoc'
|
42
|
+
rdoc.title = "radiant-mailer-extension #{version}"
|
43
|
+
rdoc.rdoc_files.include('README*')
|
44
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
45
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.0.0
|
@@ -0,0 +1,37 @@
|
|
1
|
+
class MailController < ApplicationController
|
2
|
+
|
3
|
+
no_login_required
|
4
|
+
skip_before_filter :verify_authenticity_token
|
5
|
+
|
6
|
+
def create
|
7
|
+
@page = Page.find(params[:page_id])
|
8
|
+
@page.request, @page.response = request, response
|
9
|
+
|
10
|
+
config, part_page = config_and_page(@page)
|
11
|
+
|
12
|
+
mail = Mail.new(part_page, config, params[:mailer])
|
13
|
+
@page.last_mail = part_page.last_mail = mail
|
14
|
+
process_mail(mail, config)
|
15
|
+
|
16
|
+
if mail.send
|
17
|
+
redirect_to (config[:redirect_to] || "#{@page.url}#mail_sent")
|
18
|
+
else
|
19
|
+
render :text => @page.render
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
# Hook here to do additional things, like check a CAPTCHA
|
26
|
+
def process_mail(mail, config)
|
27
|
+
end
|
28
|
+
|
29
|
+
def config_and_page(page)
|
30
|
+
until page.part(:mailer) or (not page.parent)
|
31
|
+
page = page.parent
|
32
|
+
end
|
33
|
+
string = page.render_part(:mailer)
|
34
|
+
[(string.empty? ? {} : YAML::load(string).symbolize_keys), page]
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
data/app/models/mail.rb
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
class Mail
|
2
|
+
attr_reader :page, :config, :data, :errors
|
3
|
+
def initialize(page, config, data)
|
4
|
+
@page, @config, @data = page, config.with_indifferent_access, data
|
5
|
+
@required = @data.delete(:required)
|
6
|
+
@errors = {}
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.valid_config?(config)
|
10
|
+
return false if config['recipients'].blank? and config['recipients_field'].blank?
|
11
|
+
return false if config['from'].blank? and config['from_field'].blank?
|
12
|
+
true
|
13
|
+
end
|
14
|
+
|
15
|
+
def valid?
|
16
|
+
unless defined?(@valid)
|
17
|
+
@valid = true
|
18
|
+
if recipients.blank? and !is_required_field?(config[:recipients_field])
|
19
|
+
errors['form'] = 'Recipients are required.'
|
20
|
+
@valid = false
|
21
|
+
end
|
22
|
+
|
23
|
+
if recipients.any?{|e| !valid_email?(e)}
|
24
|
+
errors['form'] = 'Recipients are invalid.'
|
25
|
+
@valid = false
|
26
|
+
end
|
27
|
+
|
28
|
+
if from.blank? and !is_required_field?(config[:from_field])
|
29
|
+
errors['form'] = 'From is required.'
|
30
|
+
@valid = false
|
31
|
+
end
|
32
|
+
|
33
|
+
if !valid_email?(from)
|
34
|
+
errors['form'] = 'From is invalid.'
|
35
|
+
@valid = false
|
36
|
+
end
|
37
|
+
|
38
|
+
if @required
|
39
|
+
@required.each do |name, msg|
|
40
|
+
if data[name].blank?
|
41
|
+
errors[name] = ((msg.blank? || %w(1 true required).include?(msg)) ? "is required." : msg)
|
42
|
+
@valid = false
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
@valid
|
48
|
+
end
|
49
|
+
|
50
|
+
def from
|
51
|
+
config[:from] || data[config[:from_field]]
|
52
|
+
end
|
53
|
+
|
54
|
+
def recipients
|
55
|
+
config[:recipients] || data[config[:recipients_field]].split(/,/).collect{|e| e.strip}
|
56
|
+
end
|
57
|
+
|
58
|
+
def reply_to
|
59
|
+
config[:reply_to] || data[config[:reply_to_field]]
|
60
|
+
end
|
61
|
+
|
62
|
+
def sender
|
63
|
+
config[:sender]
|
64
|
+
end
|
65
|
+
|
66
|
+
def subject
|
67
|
+
data[:subject] || config[:subject] || "Form Mail from #{page.request.host}"
|
68
|
+
end
|
69
|
+
|
70
|
+
def cc
|
71
|
+
data[config[:cc_field]] || config[:cc] || ""
|
72
|
+
end
|
73
|
+
|
74
|
+
def send
|
75
|
+
return false if not valid?
|
76
|
+
|
77
|
+
plain_body = (page.part( :email ) ? page.render_part( :email ) : page.render_part( :email_plain ))
|
78
|
+
html_body = page.render_part( :email_html ) || nil
|
79
|
+
|
80
|
+
if plain_body.blank? and html_body.blank?
|
81
|
+
plain_body = <<-EMAIL
|
82
|
+
The following information was posted:
|
83
|
+
#{data.to_hash.to_yaml}
|
84
|
+
EMAIL
|
85
|
+
end
|
86
|
+
|
87
|
+
headers = { 'Reply-To' => reply_to || from }
|
88
|
+
if sender
|
89
|
+
headers['Return-Path'] = sender
|
90
|
+
headers['Sender'] = sender
|
91
|
+
end
|
92
|
+
|
93
|
+
Mailer.deliver_generic_mail(
|
94
|
+
:recipients => recipients,
|
95
|
+
:from => from,
|
96
|
+
:subject => subject,
|
97
|
+
:plain_body => plain_body,
|
98
|
+
:html_body => html_body,
|
99
|
+
:cc => cc,
|
100
|
+
:headers => headers
|
101
|
+
)
|
102
|
+
@sent = true
|
103
|
+
rescue Exception => e
|
104
|
+
errors['base'] = e.message
|
105
|
+
@sent = false
|
106
|
+
end
|
107
|
+
|
108
|
+
def sent?
|
109
|
+
@sent
|
110
|
+
end
|
111
|
+
|
112
|
+
protected
|
113
|
+
|
114
|
+
def valid_email?(email)
|
115
|
+
(email.blank? ? true : email =~ /.@.+\../)
|
116
|
+
end
|
117
|
+
|
118
|
+
def is_required_field?(field_name)
|
119
|
+
@required && @required.any? {|name,_| name == field_name}
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class Mailer < ActionMailer::Base
|
2
|
+
def generic_mail(options)
|
3
|
+
@recipients = options[:recipients]
|
4
|
+
@from = options[:from] || ""
|
5
|
+
@cc = options[:cc] || ""
|
6
|
+
@bcc = options[:bcc] || ""
|
7
|
+
@subject = options[:subject] || ""
|
8
|
+
@headers = options[:headers] || {}
|
9
|
+
# Not sure that charset works, can see no effect in tests
|
10
|
+
@charset = options[:charset] || "utf-8"
|
11
|
+
@content_type = "multipart/alternative"
|
12
|
+
if options.has_key? :plain_body
|
13
|
+
part :content_type => "text/plain", :body => (options[:plain_body] || "")
|
14
|
+
end
|
15
|
+
if options.has_key? :html_body and !options[:html_body].blank?
|
16
|
+
part :content_type => "text/html", :body => (options[:html_body] || "")
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module MailerProcess
|
2
|
+
def self.included(base)
|
3
|
+
base.class_eval {
|
4
|
+
alias_method_chain :process, :mailer
|
5
|
+
attr_accessor :last_mail
|
6
|
+
}
|
7
|
+
end
|
8
|
+
|
9
|
+
def process_with_mailer(request, response)
|
10
|
+
# If configured to do so, receive and process the mailer POST
|
11
|
+
if Radiant::Config['mailer.post_to_page?'] && request.post? && request.parameters[:mailer]
|
12
|
+
config, part_page = mailer_config_and_page
|
13
|
+
|
14
|
+
mail = Mail.new(part_page, config, request.parameters[:mailer])
|
15
|
+
self.last_mail = part_page.last_mail = mail
|
16
|
+
|
17
|
+
if mail.send
|
18
|
+
response.redirect(config[:redirect_to], "302 Found") and return if config[:redirect_to]
|
19
|
+
# Clear out the data and errors so the form isn't populated, but keep the success status around.
|
20
|
+
self.last_mail.data.delete_if { true }
|
21
|
+
self.last_mail.errors.delete_if { true }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
process_without_mailer(request, response)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
def mailer_config_and_page
|
29
|
+
page = self
|
30
|
+
until page.part(:mailer) or (not page.parent)
|
31
|
+
page = page.parent
|
32
|
+
end
|
33
|
+
string = page.render_part(:mailer)
|
34
|
+
[(string.empty? ? {} : YAML::load(string).symbolize_keys), page]
|
35
|
+
end
|
36
|
+
end
|