georgia_mailer 0.1.0
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.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +45 -0
- data/Rakefile +25 -0
- data/app/assets/javascripts/georgia_mailer/application.js +1 -0
- data/app/assets/javascripts/georgia_mailer/components/message_table.js.coffee +59 -0
- data/app/assets/stylesheets/georgia_mailer/application.css.scss +2 -0
- data/app/assets/stylesheets/georgia_mailer/modules/_message.scss +53 -0
- data/app/assets/stylesheets/georgia_mailer/settings/_color.scss +2 -0
- data/app/controllers/georgia/messages_controller.rb +75 -0
- data/app/controllers/georgia_mailer/messages_controller.rb +35 -0
- data/app/decorators/georgia_mailer/message_decorator.rb +21 -0
- data/app/helpers/georgia/messages_helper.rb +9 -0
- data/app/mailers/georgia_mailer/notifier.rb +18 -0
- data/app/models/georgia/extensions/solr/georgia_mailer_message.rb +32 -0
- data/app/models/georgia_mailer/concerns/solr_georgia_mailer_message_extension.rb +42 -0
- data/app/models/georgia_mailer/concerns/tire_georgia_mailer_message_extension.rb +41 -0
- data/app/models/georgia_mailer/message.rb +34 -0
- data/app/presenters/georgia_mailer/message_actions_presenter.rb +53 -0
- data/app/uploaders/georgia_mailer/attachment_uploader.rb +16 -0
- data/app/views/georgia/georgia_mailer/messages/_message.html.erb +26 -0
- data/app/views/georgia/header/_messages.html.erb +3 -0
- data/app/views/georgia/messages/destroy.js.erb +3 -0
- data/app/views/georgia/messages/search.html.erb +63 -0
- data/app/views/georgia/messages/show.html.erb +51 -0
- data/app/views/georgia_mailer/messages/create.js.erb +18 -0
- data/app/views/georgia_mailer/notifier/new_message_notification.html.erb +211 -0
- data/app/workers/georgia_mailer/spam_worker.rb +19 -0
- data/config/routes.rb +21 -0
- data/db/migrate/001_create_georgia_mailer_messages.rb +21 -0
- data/lib/georgia_mailer/engine.rb +10 -0
- data/lib/georgia_mailer/version.rb +3 -0
- data/lib/georgia_mailer.rb +12 -0
- data/lib/tasks/georgia_mailer_tasks.rake +4 -0
- metadata +134 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 9b39fdbd73795a29fb6f1f300d92c333666742a2
|
|
4
|
+
data.tar.gz: b247a8adc76637db9504b5cb62e9f2667ce577ec
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: c53ae62db423a6e55a004c5fb658e19106529de50b24570b2cc16d1c842cf497a7a384f6179eaabcbd3c5d3c09c3c95d879451f4141e142ba0b907da77b1e798
|
|
7
|
+
data.tar.gz: ae2b705af810ac7ffeac666f26182699d831b06804a22048252dd0b5dcc62fd8fec91173403f253c3a078f638587f0587fa4bfb0eee920e5ec99aa4f247bf80d
|
data/MIT-LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright (c) 2012-2014 Motion Eleven
|
|
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.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
## Georgia Mailer
|
|
2
|
+
|
|
3
|
+
Extension to Georgia CMS to store and send messages. Messages will be treated for spam before being sent to you.
|
|
4
|
+
|
|
5
|
+
Make sure you have a working Georgia installation before proceding with these instructions.
|
|
6
|
+
|
|
7
|
+
### Installation
|
|
8
|
+
|
|
9
|
+
Add migrations and migrate
|
|
10
|
+
|
|
11
|
+
rake railties:install:migrations
|
|
12
|
+
rake db:migrate
|
|
13
|
+
|
|
14
|
+
If you selected ElasticSearch as your indexer, create the GeorgiaMailer::Message index:
|
|
15
|
+
|
|
16
|
+
rake environment tire:import CLASS=GeorgiaMailer::Message FORCE=true
|
|
17
|
+
|
|
18
|
+
### Heroku
|
|
19
|
+
|
|
20
|
+
Add Redis addon for Sidekiq
|
|
21
|
+
|
|
22
|
+
heroku addons:add redistogo
|
|
23
|
+
|
|
24
|
+
Generate GeorgiaMailer::Message index:
|
|
25
|
+
|
|
26
|
+
heroku run rake environment tire:import CLASS=GeorgiaMailer::Message FORCE=true
|
|
27
|
+
|
|
28
|
+
Add sidekiq to your Procfile:
|
|
29
|
+
|
|
30
|
+
worker: bundle exec sidekiq
|
|
31
|
+
|
|
32
|
+
### Spam filtering
|
|
33
|
+
|
|
34
|
+
To avoid pesky spammers, Georgia Mailer uses Akismet system to filter spam messages.
|
|
35
|
+
|
|
36
|
+
1. Sign up on [http://akismet.com/signup/](http://akismet.com/signup/)
|
|
37
|
+
2. Configure rakismet `key` and `url` in an initializer:
|
|
38
|
+
|
|
39
|
+
```ruby
|
|
40
|
+
# config/initializers/rakismet.rb
|
|
41
|
+
Example::Application.config.rakismet.key = '123456789XYZ'
|
|
42
|
+
Example::Application.config.rakismet.url = 'https://www.example.com/'
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
For more information or alternatives, please read on the [rakismet](https://github.com/joshfrench/rakismet) gem.
|
data/Rakefile
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/usr/bin/env rake
|
|
2
|
+
begin
|
|
3
|
+
require 'bundler/setup'
|
|
4
|
+
rescue LoadError
|
|
5
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
|
6
|
+
end
|
|
7
|
+
begin
|
|
8
|
+
require 'rdoc/task'
|
|
9
|
+
rescue LoadError
|
|
10
|
+
require 'rdoc/rdoc'
|
|
11
|
+
require 'rake/rdoctask'
|
|
12
|
+
RDoc::Task = Rake::RDocTask
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
|
16
|
+
rdoc.rdoc_dir = 'rdoc'
|
|
17
|
+
rdoc.title = 'GeorgiaMailer'
|
|
18
|
+
rdoc.options << '--line-numbers'
|
|
19
|
+
rdoc.rdoc_files.include('README.rdoc')
|
|
20
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
Bundler::GemHelper.install_tasks
|
|
24
|
+
APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
|
|
25
|
+
load 'rails/tasks/engine.rake'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//= require_tree ./components
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
class @MessagesTable
|
|
2
|
+
|
|
3
|
+
constructor: (element) ->
|
|
4
|
+
@element = $(element)
|
|
5
|
+
@checkboxes = @element.find("input:checkbox")
|
|
6
|
+
@deleteBtn = @element.find('.js-delete')
|
|
7
|
+
@spamBtn = @element.find('.js-spam')
|
|
8
|
+
@hamBtn = @element.find('.js-ham')
|
|
9
|
+
@setBindings()
|
|
10
|
+
|
|
11
|
+
setBindings: () =>
|
|
12
|
+
@checkboxes.on('change', @update)
|
|
13
|
+
@deleteBtn.on('click', @delete)
|
|
14
|
+
@spamBtn.on('click', @spam)
|
|
15
|
+
@hamBtn.on('click', @ham)
|
|
16
|
+
|
|
17
|
+
update: () =>
|
|
18
|
+
if @getChecked().length then @enableActions() else @disableActions()
|
|
19
|
+
|
|
20
|
+
delete: (event) =>
|
|
21
|
+
@stopEvent(event)
|
|
22
|
+
$.ajax(
|
|
23
|
+
url: "/admin/messages/#{@getIds()}"
|
|
24
|
+
type: 'DELETE'
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
spam: (event) ->
|
|
28
|
+
@stopEvent(event)
|
|
29
|
+
# TODO: send event to controller
|
|
30
|
+
|
|
31
|
+
ham: (event) ->
|
|
32
|
+
@stopEvent(event)
|
|
33
|
+
# TODO: send event to controller
|
|
34
|
+
|
|
35
|
+
enableActions: () =>
|
|
36
|
+
@spamBtn.removeClass('disabled').addClass('btn-warning')
|
|
37
|
+
@hamBtn.removeClass('disabled').addClass('btn-warning')
|
|
38
|
+
@deleteBtn.removeClass('disabled').addClass('btn-danger')
|
|
39
|
+
|
|
40
|
+
disableActions: () =>
|
|
41
|
+
@spamBtn.addClass('disabled').removeClass('btn-warning')
|
|
42
|
+
@hamBtn.addClass('disabled').removeClass('btn-warning')
|
|
43
|
+
@deleteBtn.addClass('disabled').removeClass('btn-danger')
|
|
44
|
+
|
|
45
|
+
stopEvent: (event) ->
|
|
46
|
+
event.stopPropagation()
|
|
47
|
+
event.preventDefault()
|
|
48
|
+
|
|
49
|
+
getChecked: () => @element.find("input:checkbox:checked[data-checkbox='child']")
|
|
50
|
+
getId: (c) => $(c).data('id')
|
|
51
|
+
getIds: () => $.map(@getChecked(), (c) => @getId(c))
|
|
52
|
+
|
|
53
|
+
$.fn.actsAsMessagesTable = () ->
|
|
54
|
+
@each ->
|
|
55
|
+
new MessagesTable($(this))
|
|
56
|
+
|
|
57
|
+
jQuery ->
|
|
58
|
+
$("table.messages.js-checkboxable").each ->
|
|
59
|
+
$(this).actsAsMessagesTable()
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
%clickable {
|
|
2
|
+
a { display: block; }
|
|
3
|
+
}
|
|
4
|
+
.message-name, .message-message, .message-attachment, .message-phone, .message-timestamp {
|
|
5
|
+
@extend %clickable;
|
|
6
|
+
}
|
|
7
|
+
.message-timestamp {
|
|
8
|
+
@extend .timestamp--long;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.message-info {
|
|
12
|
+
line-height: 30px;
|
|
13
|
+
padding: 10px 0;
|
|
14
|
+
margin-bottom: 20px;
|
|
15
|
+
border-top: 1px solid #cbcbcb;
|
|
16
|
+
border-bottom: 1px solid #cbcbcb;
|
|
17
|
+
}
|
|
18
|
+
.message-additional-info {
|
|
19
|
+
padding: 10px 0;
|
|
20
|
+
margin: 20px 0;
|
|
21
|
+
border-top: 1px solid #cbcbcb;
|
|
22
|
+
border-bottom: 1px solid #cbcbcb;
|
|
23
|
+
}
|
|
24
|
+
.message-status {
|
|
25
|
+
width: 1px;
|
|
26
|
+
border-right: 3px solid;
|
|
27
|
+
padding: 0 !important;
|
|
28
|
+
margin: 0;
|
|
29
|
+
&.spam { border-color: $red; }
|
|
30
|
+
&.clean { border-color: $green; }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.message-name span {
|
|
34
|
+
@extend .ellipsis;
|
|
35
|
+
width: 160px;
|
|
36
|
+
max-width: 160px;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.phone {
|
|
40
|
+
@extend .ellipsis;
|
|
41
|
+
width: 120px;
|
|
42
|
+
max-width: 120px;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.message-attachment {
|
|
46
|
+
@extend .tiny;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.message-message span {
|
|
50
|
+
@extend .nowrap;
|
|
51
|
+
@extend .ellipsis;
|
|
52
|
+
max-width: 800px;
|
|
53
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
module Georgia
|
|
2
|
+
class MessagesController < ::Georgia::ApplicationController
|
|
3
|
+
|
|
4
|
+
load_and_authorize_resource class: GeorgiaMailer::Message
|
|
5
|
+
|
|
6
|
+
before_filter :prepare_search, only: [:search, :show, :spam, :ham]
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def index
|
|
10
|
+
redirect_to georgia.search_messages_path
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def search
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Destroy multiple assets
|
|
17
|
+
def destroy
|
|
18
|
+
ids = params[:id].split(',')
|
|
19
|
+
if @messages = GeorgiaMailer::Message.destroy(ids)
|
|
20
|
+
respond_to do |format|
|
|
21
|
+
format.html {
|
|
22
|
+
redirect_to georgia.search_messages_path, notice: 'Messages successfully deleted.'
|
|
23
|
+
}
|
|
24
|
+
format.js { render layout: false }
|
|
25
|
+
end
|
|
26
|
+
else
|
|
27
|
+
respond_to do |format|
|
|
28
|
+
format.html {redirect_to georgia.search_messages_path, alert: 'Oups. Something went wrong.'}
|
|
29
|
+
format.js {head :internal_server_error}
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def show
|
|
35
|
+
@message = GeorgiaMailer::Message.find(params[:id]).decorate
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def spam
|
|
39
|
+
@message = GeorgiaMailer::Message.find(params[:id])
|
|
40
|
+
if @message.spam!
|
|
41
|
+
@message.update_attribute(:spam, true)
|
|
42
|
+
redirect_to :back, notice: 'Message successfully marked as spam.'
|
|
43
|
+
else
|
|
44
|
+
redirect_to :back, alert: 'Oups. Something went wrong.'
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def ham
|
|
49
|
+
@message = GeorgiaMailer::Message.find(params[:id])
|
|
50
|
+
if @message.ham! == false
|
|
51
|
+
@message.update_attribute(:spam, false)
|
|
52
|
+
redirect_to :back, notice: 'Message successfully marked as ham.'
|
|
53
|
+
else
|
|
54
|
+
redirect_to :back, alert: 'Oups. Something went wrong.'
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def resend_notification
|
|
59
|
+
@message = GeorgiaMailer::Message.find(params[:id])
|
|
60
|
+
if GeorgiaMailer::Notifier.new_message_notification(@message, message_path(@message, only_path: false)).deliver
|
|
61
|
+
redirect_to :back, notice: 'Notification successfully sent.'
|
|
62
|
+
else
|
|
63
|
+
redirect_to :back, alert: 'Oups. Something went wrong. Message could not be delivered.'
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
private
|
|
68
|
+
|
|
69
|
+
def prepare_search
|
|
70
|
+
@results = ::Georgia::Indexer.search(GeorgiaMailer::Message, params)
|
|
71
|
+
@messages = GeorgiaMailer::MessageDecorator.decorate_collection(@results)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
module GeorgiaMailer
|
|
2
|
+
class MessagesController < ::ApplicationController
|
|
3
|
+
|
|
4
|
+
# Convenient method to create and check for spam
|
|
5
|
+
def create
|
|
6
|
+
@message = GeorgiaMailer::Message.new(message_params)
|
|
7
|
+
if @message.save
|
|
8
|
+
SpamWorker.perform_async(@message.id)
|
|
9
|
+
respond_to do |format|
|
|
10
|
+
format.html { redirect_to :back, notice: 'Message delivered successfully' }
|
|
11
|
+
format.js { render layout: false }
|
|
12
|
+
end
|
|
13
|
+
else
|
|
14
|
+
respond_to do |format|
|
|
15
|
+
format.html { redirect_to :back, alert: 'Oups. Something went wrong.' }
|
|
16
|
+
format.js { render layout: false }
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def message_params
|
|
24
|
+
@message_params = {}
|
|
25
|
+
params[:message].each do |key, value|
|
|
26
|
+
@message_params[key] = value.encode('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '')
|
|
27
|
+
end
|
|
28
|
+
@message_params[:referrer] = request.referrer
|
|
29
|
+
@message_params[:user_ip] = request.remote_ip
|
|
30
|
+
@message_params[:user_agent] = request.user_agent
|
|
31
|
+
@message_params
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module GeorgiaMailer
|
|
2
|
+
class MessageDecorator < ::Georgia::ApplicationDecorator
|
|
3
|
+
|
|
4
|
+
def phone_or_none
|
|
5
|
+
phone.present? ? phone : h.content_tag(:span, 'no phone', class: 'muted')
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def subject_truncated
|
|
9
|
+
h.truncate(h.strip_tags(subject), length: 60, separator: ' ').html_safe if subject.present?
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def message_truncated
|
|
13
|
+
h.truncate(h.strip_tags(message), length: 200, separator: ' ').html_safe if message.present?
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def name_or_anonymous
|
|
17
|
+
name || 'Anonymous'
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module GeorgiaMailer
|
|
2
|
+
class Notifier < ActionMailer::Base
|
|
3
|
+
|
|
4
|
+
def new_message_notification(message)
|
|
5
|
+
@message = GeorgiaMailer::MessageDecorator.decorate(message)
|
|
6
|
+
emails_to = Georgia::User.admins.map(&:email)
|
|
7
|
+
unless emails_to.empty?
|
|
8
|
+
mail(
|
|
9
|
+
from: "georgia@motioneleven.com",
|
|
10
|
+
to: "noreply@motioneleven.com",
|
|
11
|
+
bcc: emails_to,
|
|
12
|
+
subject: "New message from #{Georgia.title}"
|
|
13
|
+
)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
require 'active_support/concern'
|
|
2
|
+
|
|
3
|
+
module Georgia
|
|
4
|
+
module Indexer
|
|
5
|
+
module Extensions
|
|
6
|
+
module Solr
|
|
7
|
+
extend ActiveSupport::Concern
|
|
8
|
+
|
|
9
|
+
included do
|
|
10
|
+
|
|
11
|
+
searchable do
|
|
12
|
+
text :name
|
|
13
|
+
text :email
|
|
14
|
+
text :message
|
|
15
|
+
text :subject
|
|
16
|
+
text :phone
|
|
17
|
+
string :spam do
|
|
18
|
+
status
|
|
19
|
+
end
|
|
20
|
+
string :name
|
|
21
|
+
string :email
|
|
22
|
+
string :phone
|
|
23
|
+
string :subject
|
|
24
|
+
string :message
|
|
25
|
+
time :created_at
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
require 'active_support/concern'
|
|
2
|
+
|
|
3
|
+
module GeorgiaMailer
|
|
4
|
+
module Concerns
|
|
5
|
+
module SolrGeorgiaMailerMessageExtension
|
|
6
|
+
extend ActiveSupport::Concern
|
|
7
|
+
|
|
8
|
+
included do
|
|
9
|
+
|
|
10
|
+
searchable do
|
|
11
|
+
text :name
|
|
12
|
+
text :email
|
|
13
|
+
text :message
|
|
14
|
+
text :subject
|
|
15
|
+
text :phone
|
|
16
|
+
string :spam do
|
|
17
|
+
status
|
|
18
|
+
end
|
|
19
|
+
string :name
|
|
20
|
+
string :email
|
|
21
|
+
string :phone
|
|
22
|
+
string :subject
|
|
23
|
+
string :message
|
|
24
|
+
time :created_at
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def self.search_index model, params
|
|
28
|
+
@search = Georgia::Message.search do
|
|
29
|
+
fulltext params[:query] do
|
|
30
|
+
fields(:name, :email, :message, :subject, :phone)
|
|
31
|
+
end
|
|
32
|
+
facet :spam
|
|
33
|
+
with(:spam, (params[:s] || 'clean'))
|
|
34
|
+
order_by (params[:o] || :created_at), (params[:dir] || :desc)
|
|
35
|
+
paginate(page: params[:page], per_page: (params[:per] || 25))
|
|
36
|
+
end.results
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
require 'active_support/concern'
|
|
2
|
+
|
|
3
|
+
module GeorgiaMailer
|
|
4
|
+
module Concerns
|
|
5
|
+
module TireGeorgiaMailerMessageExtension
|
|
6
|
+
extend ActiveSupport::Concern
|
|
7
|
+
|
|
8
|
+
included do
|
|
9
|
+
|
|
10
|
+
include ::Tire::Model::Search
|
|
11
|
+
include ::Tire::Model::Callbacks
|
|
12
|
+
|
|
13
|
+
def to_indexed_json
|
|
14
|
+
{
|
|
15
|
+
name: name,
|
|
16
|
+
email: email,
|
|
17
|
+
message: message,
|
|
18
|
+
subject: subject,
|
|
19
|
+
phone: phone,
|
|
20
|
+
status: status,
|
|
21
|
+
created_at: created_at.strftime('%F')
|
|
22
|
+
}.to_json
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.search model, params
|
|
26
|
+
model.tire.search(page: (params[:page] || 1), per_page: (params[:per] || 25)) do
|
|
27
|
+
if params[:query].present?
|
|
28
|
+
query do
|
|
29
|
+
boolean do
|
|
30
|
+
must { string params[:query], default_operator: "AND" }
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
sort { by (params[:o] || :created_at), (params[:dir] || :desc) }
|
|
34
|
+
end
|
|
35
|
+
end.results
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module GeorgiaMailer
|
|
2
|
+
class Message < ActiveRecord::Base
|
|
3
|
+
|
|
4
|
+
attr_accessible :name, :email, :subject, :message, :attachment, :phone
|
|
5
|
+
delegate :url, :current_path, :size, :content_type, :filename, to: :attachment
|
|
6
|
+
|
|
7
|
+
validates :name, presence: true
|
|
8
|
+
validates :email, format: /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i
|
|
9
|
+
validates :message, presence: true
|
|
10
|
+
|
|
11
|
+
mount_uploader :attachment, Georgia::AttachmentUploader
|
|
12
|
+
|
|
13
|
+
# Anti Spam: check https://github.com/joshfrench/rakismet for more details
|
|
14
|
+
include Rakismet::Model
|
|
15
|
+
rakismet_attrs author: :name, author_email: :email, content: :message, comment_type: 'message'
|
|
16
|
+
attr_accessible :user_ip, :user_agent, :referrer, :spam, :verified_at
|
|
17
|
+
attr_accessor :permalink, :author_url
|
|
18
|
+
|
|
19
|
+
scope :spam, where(spam: true)
|
|
20
|
+
scope :ham, where(spam: false)
|
|
21
|
+
scope :latest, order("created_at DESC")
|
|
22
|
+
|
|
23
|
+
def status
|
|
24
|
+
@status ||= spam ? 'spam' : 'clean'
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
include Georgia::Indexer::Adapter
|
|
28
|
+
is_searchable({
|
|
29
|
+
solr: GeorgiaMailer::Concerns::SolrGeorgiaMailerMessageExtension,
|
|
30
|
+
tire: GeorgiaMailer::Concerns::TireGeorgiaMailerMessageExtension
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
module GeorgiaMailer
|
|
2
|
+
class MessageActionsPresenter < ::Georgia::Presenter
|
|
3
|
+
|
|
4
|
+
def initialize view_context, message
|
|
5
|
+
@message = (message.decorated? ? message.object : message)
|
|
6
|
+
super
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def to_s
|
|
10
|
+
html = ActiveSupport::SafeBuffer.new
|
|
11
|
+
html << content_tag(:li, link_to_print)
|
|
12
|
+
if spam?
|
|
13
|
+
html << content_tag(:li, link_to_ham) if can?(:ham, @message)
|
|
14
|
+
else
|
|
15
|
+
html << content_tag(:li, link_to_spam) if can?(:spam, @message)
|
|
16
|
+
end
|
|
17
|
+
html << content_tag(:li, link_to_resend_notification)
|
|
18
|
+
html << content_tag(:li, link_to_trash) if can?(:destroy, @message)
|
|
19
|
+
html
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def link_to_print
|
|
25
|
+
link_to "#{icon_tag('print')} Print".html_safe, "javascript:window.print()", target: '_blank'
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def link_to_reply
|
|
29
|
+
link_to "#{icon_tag('reply')} Reply".html_safe, "mailto:#{@message.email}", target: '_blank'
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def link_to_ham
|
|
33
|
+
link_to "#{icon_tag('thumbs-up')} Mark as ham".html_safe, [:ham, @message]
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def link_to_spam
|
|
37
|
+
link_to "#{icon_tag('thumbs-down')} Mark as spam".html_safe, [:spam, @message]
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def link_to_resend_notification
|
|
41
|
+
link_to "#{icon_tag('envelope')} Resend notification".html_safe, [:resend_notification, @message]
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def link_to_trash
|
|
45
|
+
link_to "#{icon_tag('trash-o')} Trash".html_safe, @message, method: :delete, data: {confirm: 'Are you sure?'}
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def spam?
|
|
49
|
+
@message.spam?
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
module GeorgiaMailer
|
|
3
|
+
class AttachmentUploader < CarrierWave::Uploader::Base
|
|
4
|
+
|
|
5
|
+
storage :fog
|
|
6
|
+
|
|
7
|
+
def store_dir
|
|
8
|
+
"/attachments/#{model.id}"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def extension_white_list
|
|
12
|
+
["doc", "docx", "xls", "odt", "ods", "pdf", "rar", "zip", "tar", "tar.gz"]
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<tr id='<%= dom_id(message) %>' class='message' data-status="<%= message.status %>">
|
|
2
|
+
<td class='checkbox-cell'>
|
|
3
|
+
<%= checkboxable_tag message %>
|
|
4
|
+
</td>
|
|
5
|
+
<td class='message-name'>
|
|
6
|
+
<%= link_to message do %>
|
|
7
|
+
<span><%= message.name %></span>
|
|
8
|
+
<% end -%>
|
|
9
|
+
</td>
|
|
10
|
+
<td class='message-message'>
|
|
11
|
+
<%= link_to message do %>
|
|
12
|
+
<span><%= message.subject_truncated %> <small class='muted'><%= message.message_truncated %></small></span>
|
|
13
|
+
<% end -%>
|
|
14
|
+
</td>
|
|
15
|
+
<td class='message-phone'>
|
|
16
|
+
<%= link_to message do %>
|
|
17
|
+
<span class="phone"><%= message.phone_or_none %></span>
|
|
18
|
+
<% end -%>
|
|
19
|
+
</td>
|
|
20
|
+
<td class='message-attachment'>
|
|
21
|
+
<%= icon_tag('paper-clip') %>
|
|
22
|
+
</td>
|
|
23
|
+
<td class='message-timestamp'>
|
|
24
|
+
<%= message.created_at %>
|
|
25
|
+
</td>
|
|
26
|
+
</tr>
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
<%= content_for :head do %>
|
|
2
|
+
<%= javascript_include_tag 'georgia_mailer/application' %>
|
|
3
|
+
<%= stylesheet_link_tag 'georgia_mailer/application' %>
|
|
4
|
+
<% end -%>
|
|
5
|
+
|
|
6
|
+
<header class='header-main'>
|
|
7
|
+
<div class="header-title">
|
|
8
|
+
<%= form_for :search, url: search_messages_path, method: :get, html: {role: 'search'} do |f| %>
|
|
9
|
+
<%= hidden_field_tag :s, params[:s] if params[:s] %>
|
|
10
|
+
<div class="input-group">
|
|
11
|
+
<div class="input-group-btn">
|
|
12
|
+
<button class="btn btn-default"><%= icon_tag('search') %></button>
|
|
13
|
+
</div>
|
|
14
|
+
<%= text_field_tag :query, params[:query], class: 'form-control input-search', placeholder: 'Search' %>
|
|
15
|
+
</div>
|
|
16
|
+
<% end -%>
|
|
17
|
+
</div>
|
|
18
|
+
<div class="header-actions">
|
|
19
|
+
<%= link_to "#{icon_tag('reply')} Reply".html_safe, "mailto:#{@message.email}", class: 'btn btn-warning' if @message %>
|
|
20
|
+
</div>
|
|
21
|
+
</header>
|
|
22
|
+
|
|
23
|
+
<section class="body-main">
|
|
24
|
+
|
|
25
|
+
<table class="table table-hover table-results messages js-checkboxable">
|
|
26
|
+
<thead>
|
|
27
|
+
<th colspan='3'>
|
|
28
|
+
<div class="btn-group">
|
|
29
|
+
<div class="btn btn-default dropdown-toggle" data-toggle="dropdown">
|
|
30
|
+
<%= checkboxable_all_tag :messages %>
|
|
31
|
+
<span class="caret"></span>
|
|
32
|
+
</div>
|
|
33
|
+
<ul class="dropdown-menu" role="menu">
|
|
34
|
+
<li><%= link_to 'All', '#', class: 'js-select-all' %></li>
|
|
35
|
+
<li><%= link_to 'None', '#', class: 'js-select-none' %></li>
|
|
36
|
+
<li><%= link_to 'Spam', '#', class: 'js-select-spam' %></li>
|
|
37
|
+
</ul>
|
|
38
|
+
</div>
|
|
39
|
+
<%= link_to "#{icon_tag('thumbs-up')} ham".html_safe, '#', class: 'btn btn-default btn-xs js-ham disabled' %>
|
|
40
|
+
<%= link_to "#{icon_tag('thumbs-down')} spam".html_safe, '#', class: 'btn btn-default btn-xs js-spam disabled' %>
|
|
41
|
+
<%= link_to "#{icon_tag('trash-o')} trash".html_safe, '#', class: 'btn btn-default btn-xs js-delete disabled' %>
|
|
42
|
+
<%#= link_to '#', class: 'btn btn-warning' do %>
|
|
43
|
+
<%#= icon_tag('tags') %> <%#= icon_tag('caret-down') %>
|
|
44
|
+
<%# end -%>
|
|
45
|
+
</th>
|
|
46
|
+
<th colspan='3'>
|
|
47
|
+
<%= pagination_tag(@search) %>
|
|
48
|
+
</th>
|
|
49
|
+
</thead>
|
|
50
|
+
<tbody>
|
|
51
|
+
<% if @messages and @messages.any? %>
|
|
52
|
+
<%= render @messages %>
|
|
53
|
+
<% else %>
|
|
54
|
+
<tr>
|
|
55
|
+
<td colspan="6">
|
|
56
|
+
There are currently no messages.
|
|
57
|
+
</td>
|
|
58
|
+
</tr>
|
|
59
|
+
<% end %>
|
|
60
|
+
</tbody>
|
|
61
|
+
</table>
|
|
62
|
+
|
|
63
|
+
</section>
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
<%= content_for :head do %>
|
|
2
|
+
<%= javascript_include_tag 'georgia_mailer/application' %>
|
|
3
|
+
<%= stylesheet_link_tag 'georgia_mailer/application' %>
|
|
4
|
+
<% end -%>
|
|
5
|
+
|
|
6
|
+
<header class='header-main hidden-print'>
|
|
7
|
+
<div class="header-gutter">
|
|
8
|
+
<%= link_to_back search_messages_path %>
|
|
9
|
+
</div>
|
|
10
|
+
<div class="header-title"></div>
|
|
11
|
+
<div class="header-actions">
|
|
12
|
+
</div>
|
|
13
|
+
</header>
|
|
14
|
+
|
|
15
|
+
<section class="body-main">
|
|
16
|
+
<h2><%= @message.subject %></h2>
|
|
17
|
+
<div class="message-info">
|
|
18
|
+
<div class="btn-group pull-right hidden-print">
|
|
19
|
+
<%= link_to "#{icon_tag('reply')} Reply".html_safe, "mailto:#{@message.email}", class: 'btn btn-warning btn-sm', target: '_blank' %>
|
|
20
|
+
<button type="button" class="btn btn-warning btn-sm dropdown-toggle" data-toggle="dropdown">
|
|
21
|
+
<span class="caret"></span>
|
|
22
|
+
<span class="sr-only">Toggle Dropdown</span>
|
|
23
|
+
</button>
|
|
24
|
+
<ul class="dropdown-menu" role="menu">
|
|
25
|
+
<%= message_actions_list(@message) %>
|
|
26
|
+
</ul>
|
|
27
|
+
</div>
|
|
28
|
+
<strong>
|
|
29
|
+
<%= @message.name_or_anonymous %>
|
|
30
|
+
</strong>
|
|
31
|
+
<span><<%= @message.email %>></span>
|
|
32
|
+
<span>on <%= @message.created_at.strftime('%F %H:%M') %></span>
|
|
33
|
+
<span>sent from <%= link_to @message.referrer, @message.referrer %></span>
|
|
34
|
+
</div>
|
|
35
|
+
<div class="message-content">
|
|
36
|
+
<%= @message.message.html_safe %>
|
|
37
|
+
</div>
|
|
38
|
+
<div class="message-additional-info">
|
|
39
|
+
<p>
|
|
40
|
+
<b>Phone</b>: <%= @message.phone_or_none %>
|
|
41
|
+
</p>
|
|
42
|
+
<p>
|
|
43
|
+
<b>Attachment</b>:
|
|
44
|
+
<% if @message.attachment.try(:file) and @message.attachment.file.exists? %>
|
|
45
|
+
<%= link_to @message.attachment.file.filename, @message.attachment.url %>
|
|
46
|
+
<% else -%>
|
|
47
|
+
<span class='muted'>There were no attachment.</span>
|
|
48
|
+
<% end -%>
|
|
49
|
+
</p>
|
|
50
|
+
</div>
|
|
51
|
+
</section>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Clear previous errors and alerts
|
|
2
|
+
$('input, textarea, select').closest('.form-group').removeClass('has-error');
|
|
3
|
+
$('form#new_message .alert').remove();
|
|
4
|
+
|
|
5
|
+
<% if @message.valid? %>
|
|
6
|
+
|
|
7
|
+
$('form#new_message')[0].reset();
|
|
8
|
+
$('form#new_message').prepend("<div class='alert alert-success'>Thank you! Your message has been successfully delivered.</div>");
|
|
9
|
+
|
|
10
|
+
<% else %>
|
|
11
|
+
|
|
12
|
+
<% @message.errors.each do |error, message| %>
|
|
13
|
+
$('form#new_message #message_<%= error.to_s %>').closest('.form-group').addClass('has-error');
|
|
14
|
+
<% end %>
|
|
15
|
+
|
|
16
|
+
$('form#new_message').prepend("<div class='alert alert-danger'>Oups. Something went wrong.<br /><%= @message.errors.full_messages.join('. ') %></div>");
|
|
17
|
+
|
|
18
|
+
<% end %>
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
|
2
|
+
<html xmlns="http://www.w3.org/1999/xhtml">
|
|
3
|
+
<head>
|
|
4
|
+
<meta name="viewport" content="width=device-width" />
|
|
5
|
+
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
|
6
|
+
<title>New message from <%= Georgia.title %></title>
|
|
7
|
+
<style>
|
|
8
|
+
/* -------------------------------------
|
|
9
|
+
GLOBAL
|
|
10
|
+
------------------------------------- */
|
|
11
|
+
* {
|
|
12
|
+
margin:0;
|
|
13
|
+
padding:0;
|
|
14
|
+
font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif;
|
|
15
|
+
font-size: 100%;
|
|
16
|
+
line-height: 1.6;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
img {
|
|
20
|
+
max-width: 100%;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
body {
|
|
24
|
+
-webkit-font-smoothing:antialiased;
|
|
25
|
+
-webkit-text-size-adjust:none;
|
|
26
|
+
width: 100%!important;
|
|
27
|
+
height: 100%;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
/* -------------------------------------
|
|
32
|
+
ELEMENTS
|
|
33
|
+
------------------------------------- */
|
|
34
|
+
a {
|
|
35
|
+
color: #348eda;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.btn-primary{
|
|
39
|
+
text-decoration:none;
|
|
40
|
+
color: #FFF;
|
|
41
|
+
background-color: #348eda;
|
|
42
|
+
border:solid #348eda;
|
|
43
|
+
border-width:10px 20px;
|
|
44
|
+
line-height:2;
|
|
45
|
+
font-weight:bold;
|
|
46
|
+
margin-right:10px;
|
|
47
|
+
text-align:center;
|
|
48
|
+
cursor:pointer;
|
|
49
|
+
display: inline-block;
|
|
50
|
+
border-radius: 25px;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.btn-secondary {
|
|
54
|
+
text-decoration:none;
|
|
55
|
+
color: #FFF;
|
|
56
|
+
background-color: #aaa;
|
|
57
|
+
border:solid #aaa;
|
|
58
|
+
border-width:10px 20px;
|
|
59
|
+
line-height:2;
|
|
60
|
+
font-weight:bold;
|
|
61
|
+
margin-right:10px;
|
|
62
|
+
text-align:center;
|
|
63
|
+
cursor:pointer;
|
|
64
|
+
display: inline-block;
|
|
65
|
+
border-radius: 25px;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.last {
|
|
69
|
+
margin-bottom: 0;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.first{
|
|
73
|
+
margin-top: 0;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.padding{
|
|
77
|
+
padding:10px 0;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
/* -------------------------------------
|
|
82
|
+
BODY
|
|
83
|
+
------------------------------------- */
|
|
84
|
+
table.body-wrap {
|
|
85
|
+
width: 100%;
|
|
86
|
+
padding: 20px;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
table.body-wrap .container{
|
|
90
|
+
border: 1px solid #f0f0f0;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
/* -------------------------------------
|
|
95
|
+
FOOTER
|
|
96
|
+
------------------------------------- */
|
|
97
|
+
table.footer-wrap {
|
|
98
|
+
width: 100%;
|
|
99
|
+
clear:both!important;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.footer-wrap .container p {
|
|
103
|
+
font-size:12px;
|
|
104
|
+
color:#666;
|
|
105
|
+
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
table.footer-wrap a{
|
|
109
|
+
color: #999;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
/* -------------------------------------
|
|
114
|
+
TYPOGRAPHY
|
|
115
|
+
------------------------------------- */
|
|
116
|
+
h1,h2,h3{
|
|
117
|
+
font-family: "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif; line-height: 1.1; margin-bottom:15px; color:#000;
|
|
118
|
+
margin: 40px 0 10px;
|
|
119
|
+
line-height: 1.2;
|
|
120
|
+
font-weight:200;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
h1 {
|
|
124
|
+
font-size: 36px;
|
|
125
|
+
}
|
|
126
|
+
h2 {
|
|
127
|
+
font-size: 28px;
|
|
128
|
+
}
|
|
129
|
+
h3 {
|
|
130
|
+
font-size: 22px;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
p, ul, ol {
|
|
134
|
+
margin-bottom: 10px;
|
|
135
|
+
font-weight: normal;
|
|
136
|
+
font-size:14px;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
ul li, ol li {
|
|
140
|
+
margin-left:5px;
|
|
141
|
+
list-style-position: inside;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/* ---------------------------------------------------
|
|
145
|
+
RESPONSIVENESS
|
|
146
|
+
Nuke it from orbit. It's the only way to be sure.
|
|
147
|
+
------------------------------------------------------ */
|
|
148
|
+
|
|
149
|
+
/* Set a max-width, and make it display as block so it will automatically stretch to that width, but will also shrink down on a phone or something */
|
|
150
|
+
.container {
|
|
151
|
+
display:block!important;
|
|
152
|
+
max-width:600px!important;
|
|
153
|
+
margin:0 auto!important; /* makes it centered */
|
|
154
|
+
clear:both!important;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/* Set the padding on the td rather than the div for Outlook compatibility */
|
|
158
|
+
.body-wrap .container{
|
|
159
|
+
padding:20px;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/* This should also be a block element, so that it will fill 100% of the .container */
|
|
163
|
+
.content {
|
|
164
|
+
max-width:600px;
|
|
165
|
+
margin:0 auto;
|
|
166
|
+
display:block;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/* Let's make sure tables in the content area are 100% wide */
|
|
170
|
+
.content table {
|
|
171
|
+
width: 100%;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
</style>
|
|
175
|
+
</head>
|
|
176
|
+
|
|
177
|
+
<body bgcolor="#f6f6f6">
|
|
178
|
+
|
|
179
|
+
<!-- body -->
|
|
180
|
+
<table class="body-wrap">
|
|
181
|
+
<tr>
|
|
182
|
+
<td></td>
|
|
183
|
+
<td class="container" bgcolor="#FFFFFF">
|
|
184
|
+
|
|
185
|
+
<!-- content -->
|
|
186
|
+
<div class="content">
|
|
187
|
+
<table>
|
|
188
|
+
<tr>
|
|
189
|
+
<td>
|
|
190
|
+
<p>Hi there,</p>
|
|
191
|
+
<p>You've received a new message on Georgia CMS from <%= @message.name_or_anonymous %></p>
|
|
192
|
+
<h1><%= @message.subject %></h1>
|
|
193
|
+
<p><%= @message.message %></p>
|
|
194
|
+
<% if @message.phone.present? %>
|
|
195
|
+
<p><%= @message.phone %></p>
|
|
196
|
+
<% end -%>
|
|
197
|
+
<p>Thanks, have a lovely day.</p>
|
|
198
|
+
</td>
|
|
199
|
+
</tr>
|
|
200
|
+
</table>
|
|
201
|
+
</div>
|
|
202
|
+
<!-- /content -->
|
|
203
|
+
|
|
204
|
+
</td>
|
|
205
|
+
<td></td>
|
|
206
|
+
</tr>
|
|
207
|
+
</table>
|
|
208
|
+
<!-- /body -->
|
|
209
|
+
|
|
210
|
+
</body>
|
|
211
|
+
</html>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module GeorgiaMailer
|
|
2
|
+
class SpamWorker
|
|
3
|
+
include Sidekiq::Worker
|
|
4
|
+
|
|
5
|
+
def perform(message_id)
|
|
6
|
+
begin
|
|
7
|
+
@message = GeorgiaMailer::Message.find(message_id)
|
|
8
|
+
is_spam = @message.spam?
|
|
9
|
+
@message.update_attributes(spam: is_spam, verified_at: Time.zone.now)
|
|
10
|
+
unless @message.spam or !GeorgiaMailer.turn_on_email_notifications
|
|
11
|
+
GeorgiaMailer::Notifier.new_message_notification(@message).deliver
|
|
12
|
+
end
|
|
13
|
+
rescue ActiveRecord::RecordNotFound
|
|
14
|
+
Rails.logger.info "Message with ID #{message_id} was destroy before it could be processed"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
end
|
|
19
|
+
end
|
data/config/routes.rb
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
GeorgiaMailer::Engine.routes.draw do
|
|
2
|
+
|
|
3
|
+
resources :messages, only: :create
|
|
4
|
+
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
Georgia::Engine.routes.draw do
|
|
8
|
+
|
|
9
|
+
resources :messages do
|
|
10
|
+
collection do
|
|
11
|
+
get :search
|
|
12
|
+
get :destroy_all_spam
|
|
13
|
+
end
|
|
14
|
+
member do
|
|
15
|
+
get :spam
|
|
16
|
+
get :ham
|
|
17
|
+
get :resend_notification
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
class CreateGeorgiaMailerMessages < ActiveRecord::Migration
|
|
2
|
+
|
|
3
|
+
def change
|
|
4
|
+
create_table :georgia_mailer_messages do |t|
|
|
5
|
+
t.string :name
|
|
6
|
+
t.string :email
|
|
7
|
+
t.string :phone
|
|
8
|
+
t.string :subject
|
|
9
|
+
t.string :attachment
|
|
10
|
+
t.text :message
|
|
11
|
+
t.boolean :spam, default: false
|
|
12
|
+
t.datetime :verified_at
|
|
13
|
+
t.string :permalink
|
|
14
|
+
t.string :user_ip
|
|
15
|
+
t.string :user_agent
|
|
16
|
+
t.string :referrer
|
|
17
|
+
t.timestamps
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
require "georgia_mailer/engine"
|
|
2
|
+
|
|
3
|
+
module GeorgiaMailer
|
|
4
|
+
|
|
5
|
+
# Helper to turn off email notifications
|
|
6
|
+
mattr_accessor :turn_on_email_notifications
|
|
7
|
+
@@turn_on_email_notifications = true
|
|
8
|
+
|
|
9
|
+
# Add to Georgia by default
|
|
10
|
+
Georgia.navigation += %w(messages)
|
|
11
|
+
|
|
12
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: georgia_mailer
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Mathieu Gagné
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2014-02-03 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: rails
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - '>='
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: 3.2.6
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - '>='
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: 3.2.6
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: georgia
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - '>='
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '0'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - '>='
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: rakismet
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - '>='
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '0'
|
|
48
|
+
type: :runtime
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - '>='
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '0'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: sidekiq
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - '>='
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '0'
|
|
62
|
+
type: :runtime
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - '>='
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '0'
|
|
69
|
+
description: GeorgiaMailer works essentially with Georgia CMS to provide spam-free
|
|
70
|
+
contact forms, email notifications, Sendgrid integration, and so on.
|
|
71
|
+
email:
|
|
72
|
+
- mathieu@motioneleven.com
|
|
73
|
+
executables: []
|
|
74
|
+
extensions: []
|
|
75
|
+
extra_rdoc_files: []
|
|
76
|
+
files:
|
|
77
|
+
- app/assets/javascripts/georgia_mailer/components/message_table.js.coffee
|
|
78
|
+
- app/assets/javascripts/georgia_mailer/application.js
|
|
79
|
+
- app/assets/stylesheets/georgia_mailer/settings/_color.scss
|
|
80
|
+
- app/assets/stylesheets/georgia_mailer/modules/_message.scss
|
|
81
|
+
- app/assets/stylesheets/georgia_mailer/application.css.scss
|
|
82
|
+
- app/decorators/georgia_mailer/message_decorator.rb
|
|
83
|
+
- app/models/georgia/extensions/solr/georgia_mailer_message.rb
|
|
84
|
+
- app/models/georgia_mailer/concerns/tire_georgia_mailer_message_extension.rb
|
|
85
|
+
- app/models/georgia_mailer/concerns/solr_georgia_mailer_message_extension.rb
|
|
86
|
+
- app/models/georgia_mailer/message.rb
|
|
87
|
+
- app/controllers/georgia/messages_controller.rb
|
|
88
|
+
- app/controllers/georgia_mailer/messages_controller.rb
|
|
89
|
+
- app/views/georgia/header/_messages.html.erb
|
|
90
|
+
- app/views/georgia/messages/destroy.js.erb
|
|
91
|
+
- app/views/georgia/messages/show.html.erb
|
|
92
|
+
- app/views/georgia/messages/search.html.erb
|
|
93
|
+
- app/views/georgia/georgia_mailer/messages/_message.html.erb
|
|
94
|
+
- app/views/georgia_mailer/messages/create.js.erb
|
|
95
|
+
- app/views/georgia_mailer/notifier/new_message_notification.html.erb
|
|
96
|
+
- app/workers/georgia_mailer/spam_worker.rb
|
|
97
|
+
- app/mailers/georgia_mailer/notifier.rb
|
|
98
|
+
- app/presenters/georgia_mailer/message_actions_presenter.rb
|
|
99
|
+
- app/helpers/georgia/messages_helper.rb
|
|
100
|
+
- app/uploaders/georgia_mailer/attachment_uploader.rb
|
|
101
|
+
- config/routes.rb
|
|
102
|
+
- db/migrate/001_create_georgia_mailer_messages.rb
|
|
103
|
+
- lib/tasks/georgia_mailer_tasks.rake
|
|
104
|
+
- lib/georgia_mailer.rb
|
|
105
|
+
- lib/georgia_mailer/engine.rb
|
|
106
|
+
- lib/georgia_mailer/version.rb
|
|
107
|
+
- MIT-LICENSE
|
|
108
|
+
- Rakefile
|
|
109
|
+
- README.md
|
|
110
|
+
homepage: http://www.motioneleven.com/
|
|
111
|
+
licenses:
|
|
112
|
+
- MIT
|
|
113
|
+
metadata: {}
|
|
114
|
+
post_install_message:
|
|
115
|
+
rdoc_options: []
|
|
116
|
+
require_paths:
|
|
117
|
+
- lib
|
|
118
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
119
|
+
requirements:
|
|
120
|
+
- - '>='
|
|
121
|
+
- !ruby/object:Gem::Version
|
|
122
|
+
version: '0'
|
|
123
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
124
|
+
requirements:
|
|
125
|
+
- - '>='
|
|
126
|
+
- !ruby/object:Gem::Version
|
|
127
|
+
version: '0'
|
|
128
|
+
requirements: []
|
|
129
|
+
rubyforge_project:
|
|
130
|
+
rubygems_version: 2.1.11
|
|
131
|
+
signing_key:
|
|
132
|
+
specification_version: 4
|
|
133
|
+
summary: Handles Georgia Messages, SPAM, contact forms, etc.
|
|
134
|
+
test_files: []
|