georgia_mailer 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|