chimpmunk 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d8f65e72ccad48b7480ebab56fc5b64d47db5433
4
+ data.tar.gz: 65dcf7c040d7b56806733af6230f8b2040000e14
5
+ SHA512:
6
+ metadata.gz: abb985ee363fead96538780e32aa4279ec985f2d6de6cd6b4dc575ea38402d9bd3de80cd6d406c9c5f71dc2831c225a11499ad125f5c8700cd75124814dceba6
7
+ data.tar.gz: 2654c63e2b08158f0566d9cd699e2c91cd631289a58241a752e1118441cd21fd3beddc2cacd15b6a0a30eac4ea2f8e961af3cbc9ed64078b68f017aba94fca0a
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in chimpmunk.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Nick Franken
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,43 @@
1
+ # Chimpmunk
2
+
3
+ Mailchimp list administration and Campaign builder. ( in development )
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'chimpmunk'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Install with (make sure you have followed all rails_admin install steps first)
16
+
17
+ $ rails g chimpmunk:install
18
+
19
+ ## Post Installation
20
+
21
+ Install migrations
22
+
23
+ $ rake chimpmunk_engine:install:migrations
24
+
25
+ Run migrations
26
+
27
+ $ rake db:migrate
28
+
29
+ Set your mailchimp api key:
30
+
31
+ Gibbon::API.api_key = "your_api_key"
32
+
33
+ ## Usage
34
+
35
+ TODO: Write usage instructions here
36
+
37
+ ## Contributing
38
+
39
+ 1. Fork it ( https://github.com/[my-github-username]/chimpmunk/fork )
40
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
41
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
42
+ 4. Push to the branch (`git push origin my-new-feature`)
43
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
data/chimpmunk.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'chimpmunk/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "chimpmunk"
8
+ spec.version = Chimpmunk::VERSION
9
+ spec.authors = ["Nick Franken"]
10
+ spec.email = ["nf@the42.com"]
11
+ spec.summary = %q{Mailchimp list administration and Campaign builder.}
12
+ spec.description = %q{Mailchimp list administration and Campaign builder.}
13
+ spec.homepage = "https://bitbucket.org/42dev/chimpmunk"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency 'activerecord', ['>= 3.2', '< 5']
22
+ spec.add_dependency 'gibbon', '>= 1.1'
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.6"
25
+ spec.add_development_dependency "rake"
26
+ end
@@ -0,0 +1,53 @@
1
+ en:
2
+ admin:
3
+ actions:
4
+ send_test_email:
5
+ title: 'Send Test Email'
6
+ menu: 'Send Test Email'
7
+ breadcrumb: 'Send Test Email'
8
+ link: 'Send Test Email'
9
+ email_management:
10
+ title: 'Email Management'
11
+ menu: 'Email Management'
12
+ breadcrumb: 'Email Management'
13
+ link: 'Email Management'
14
+ email_list_new:
15
+ title: 'New Email List'
16
+ menu: 'New Email List'
17
+ breadcrumb: 'New Email List'
18
+ link: 'New Email List'
19
+ duplicate_email_campaign:
20
+ title: 'Duplicate Email Campaign'
21
+ menu: 'Duplicate Email Campaign'
22
+ breadcrumb: 'Duplicate Email Campaign'
23
+ link: 'Duplicate Email Campaign'
24
+ send_email_campaign:
25
+ title: 'Send Email Campaign'
26
+ menu: 'Send Email Campaign'
27
+ breadcrumb: 'Send Email Campaign'
28
+ link: 'Send Email Campaign'
29
+ schedule_email_campaign:
30
+ title: 'Schedule Email Campaign'
31
+ menu: 'Schedule Email Campaign'
32
+ breadcrumb: 'Schedule Email Campaign'
33
+ link: 'Schedule Email Campaign'
34
+ un_schedule_email_campaign:
35
+ title: 'Un-Schedule Email Campaign'
36
+ menu: 'Un-Schedule Email Campaign'
37
+ breadcrumb: 'Un-Schedule Email Campaign'
38
+ link: 'Un-Schedule Email Campaign'
39
+ refresh_email_campaigns:
40
+ title: 'Refresh Email Campaigns'
41
+ menu: 'Refresh Email Campaigns'
42
+ breadcrumb: 'Refresh Email Campaigns'
43
+ link: 'Refresh Email Campaigns'
44
+ refresh_email_campaign_stats:
45
+ title: 'Refresh Email Campaigns Stats'
46
+ menu: 'Refresh Email Campaigns Stats'
47
+ breadcrumb: 'Refresh Email Campaigns Stats'
48
+ link: 'Refresh Email Campaigns Stats'
49
+ refresh_email_lists:
50
+ title: 'Refresh Email Lists'
51
+ menu: 'Refresh Email Lists'
52
+ breadcrumb: 'Refresh Email Lists'
53
+ link: 'Refresh Email Lists'
@@ -0,0 +1,102 @@
1
+ class ChimpmunkMigration < ActiveRecord::Migration
2
+ def self.up
3
+
4
+ # create email lists
5
+ create_table :email_lists do |t|
6
+ t.string :default_from_email
7
+ t.string :default_from_name
8
+ t.string :default_subject
9
+ t.string :mailchimp_id
10
+ t.string :name
11
+
12
+ t.timestamps
13
+ end
14
+
15
+ # create email campaigns
16
+ create_table :email_campaigns do |t|
17
+ t.string :title
18
+ t.string :from_email
19
+ t.string :from_name
20
+ t.references :email_list, index: true
21
+ t.string :subject
22
+ t.references :email_template, index: true
23
+ t.string :mailchimp_list_id
24
+ t.datetime :send_date
25
+ t.string :mailchimp_campaign_id
26
+ t.string :mailchimp_list_id
27
+ t.integer :emails_sent
28
+ t.integer :clicked
29
+ t.integer :opened
30
+ t.string :status
31
+ t.integer :hard_bounces
32
+ t.integer :soft_bounces
33
+ t.integer :unsubscribes
34
+ t.integer :forwards
35
+ t.integer :forwards_opens
36
+ t.integer :opens
37
+ t.datetime :last_open
38
+ t.integer :unique_opens
39
+ t.integer :non_unique_clicks
40
+ t.integer :users_who_clicked
41
+ t.integer :unique_likes
42
+ t.integer :facebook_likes
43
+ t.string :campaign_kind
44
+ t.integer :template_id
45
+
46
+ t.timestamps
47
+ end
48
+
49
+ # create email list groups
50
+ create_table :email_list_groups do |t|
51
+ t.string :name
52
+ t.references :grouping, index: true
53
+
54
+ t.timestamps
55
+ end
56
+
57
+ # create email templates
58
+ create_table :email_templates do |t|
59
+ t.string :mailchimp_id
60
+ t.string :name
61
+ t.string :layout
62
+ t.string :preview_image
63
+
64
+ t.timestamps
65
+ end
66
+
67
+ # create email list groupings
68
+ create_table :email_list_groupings do |t|
69
+ t.string :name
70
+ t.references :email_list, index: true
71
+ t.integer :mailchimp_grouping_id
72
+
73
+ t.timestamps
74
+ end
75
+
76
+ # ceate email subscribers
77
+ create_table :email_subscribers do |t|
78
+ t.string :mailchimp_id
79
+ t.string :first_name
80
+ t.string :last_name
81
+ t.string :email
82
+ t.boolean :subscribed
83
+ t.references :email_list, index: true
84
+
85
+ t.timestamps
86
+ end
87
+
88
+ # create email list group subscribers join table
89
+ create_table :email_list_groups_subscribers, id: false do |t|
90
+ t.integer :email_list_group_id
91
+ t.integer :email_subscriber_id
92
+ end
93
+ end
94
+
95
+ def self.down
96
+ drop_table :email_lists
97
+ drop_table :email_campaigns
98
+ drop_table :email_list_groups
99
+ drop_table :email_templates
100
+ drop_table :email_list_groupings
101
+ end
102
+ end
data/lib/chimpmunk.rb ADDED
@@ -0,0 +1,21 @@
1
+ require 'active_record'
2
+ require 'rails_admin'
3
+ require 'gibbon'
4
+ require 'chimpmunk/engine'
5
+ require 'rails_admin/chimpmunk_custom_actions.rb'
6
+
7
+ module Chimpmunk
8
+ extend ActiveSupport::Autoload
9
+
10
+ autoload :EmailCampaign
11
+ autoload :EmailList
12
+ autoload :EmailListGroup
13
+ autoload :EmailListGrouping
14
+ autoload :EmailSubscriber
15
+ autoload :EmailTemplate
16
+ autoload :VERSION
17
+
18
+ def self.mailchimp
19
+ @mailchimp ||= ::Gibbon::API.new
20
+ end
21
+ end
@@ -0,0 +1,352 @@
1
+ # encoding: utf-8
2
+ module Chimpmunk
3
+ class EmailCampaign < ::ActiveRecord::Base
4
+ attr_accessor :skip_update_callbacks, :skip_create_callbacks
5
+
6
+ # rails 3
7
+ attr_accessible :title, :from_email, :from_name, :email_list_id, :subject, :email_template_id, :mailchimp_list_id, :send_date,
8
+ :mailchimp_campaign_id, :emails_sent, :clicked, :opened, :status, :hard_bounces, :soft_bounces, :unsubscribes, :forwards,
9
+ :forwards_opens, :opens, :last_open, :unique_opens, :non_unique_clicks, :users_who_clicked, :unique_likes, :facebook_likes,
10
+ :created_at, :updated_at, :campaign_kind, :template_id, :email_list_group_id if defined?(ActiveModel::MassAssignmentSecurity)
11
+
12
+ # Associations
13
+ belongs_to :email_list
14
+ belongs_to :email_template
15
+ belongs_to :email_list_group
16
+ belongs_to :email_template
17
+
18
+ # Validations
19
+ validates :subject, presence: true
20
+ validates :from_email, presence: true
21
+ validates :from_name, presence: true
22
+ validates :email_list, presence: true
23
+ validates :email_template, presence: true
24
+
25
+ # Callbacks
26
+ after_create :create_campaign, unless: Proc.new { |campaign| campaign.skip_create_callbacks }
27
+ before_update :update_campaign, unless: Proc.new{ |campaign| campaign.skip_update_callbacks }
28
+ before_update :update_content, if: Proc.new{|campaign| campaign.email_template_id_changed? && !campaign.skip_update_callbacks }
29
+ before_destroy :destroy_mailchimp_campaign
30
+
31
+ # TODO: figure out attachments, example below
32
+ # has_attached_file :intro_image
33
+ # validates_attachment_content_type :intro_image, :content_type => /\Aimage\/.*\Z/
34
+
35
+ # TODO: figure out association, example below
36
+ # has_many :email_campaign_trending_posts, dependent: :destroy
37
+ # has_many :trending_posts, source: :post, through: :email_campaign_trending_posts
38
+
39
+ ##
40
+ # CLASS METHODS
41
+ ##
42
+
43
+ ##
44
+ # Refreshes the list of email campaigns
45
+ def self.refresh_email_campaigns
46
+ Chimpmunk.mailchimp.campaigns.list()["data"].each do |campaign|
47
+ campaign_attributes = {
48
+ title: campaign["title"],
49
+ campaign_kind: campaign["type"],
50
+ mailchimp_list_id: campaign["list_id"],
51
+ email_list_id: Chimpmunk::EmailList.find_by_mailchimp_id(campaign["list_id"]),
52
+ subject: campaign["subject"],
53
+ from_email: campaign["from_email"],
54
+ from_name: campaign["from_name"],
55
+ template_id: campaign["template_id"],
56
+ mailchimp_campaign_id: campaign["id"]
57
+ }
58
+ unless self.where(campaign_attributes).any?
59
+ new_campaign = self.new(campaign_attributes)
60
+ new_campaign.skip_create_callbacks = true
61
+ new_campaign.save
62
+ end
63
+ end
64
+ end
65
+
66
+ ##
67
+ # Refreshes all of the email campaign stats
68
+ def self.refresh_email_campaign_stats
69
+ Chimpmunk.mailchimp.campaigns.list({status:"sent"})["data"].each do |campaign|
70
+ email_campaign = Chimpmunk::EmailCampaign.find_by_mailchimp_campaign_id(campaign["id"])
71
+ if email_campaign and campaign["summary"].any?
72
+ email_campaign.skip_update_callbacks = true
73
+ email_campaign.update_attributes(
74
+ status: "sent",
75
+ emails_sent: campaign["summary"]["emails_sent"],
76
+ opened: campaign["summary"]["unique_opens"],
77
+ clicked: campaign["summary"]["unique_clicks"],
78
+ hard_bounces: campaign["summary"]["hard_bounces"],
79
+ soft_bounces: campaign["summary"]["soft_bounces"],
80
+ unsubscribes: campaign["summary"]["unsubscribes"],
81
+ forwards: campaign["summary"]["forwards"],
82
+ forwards_opens: campaign["summary"]["forwards_opens"],
83
+ opens: campaign["summary"]["opens"],
84
+ last_open: campaign["summary"]["last_open"],
85
+ unique_opens: campaign["summary"]["unique_opens"],
86
+ non_unique_clicks: campaign["summary"]["non_unique_clicks"],
87
+ users_who_clicked: campaign["summary"]["users_who_clicked"],
88
+ unique_likes: campaign["summary"]["unique_likes"],
89
+ facebook_likes: campaign["summary"]["facebook_likes"]
90
+ )
91
+ end
92
+ end
93
+ end
94
+
95
+ ##
96
+ # INSTANCE METHODS
97
+ ##
98
+
99
+ ##
100
+ # send a mailchimp campaign
101
+ def send_campaign
102
+ response = Chimpmunk.mailchimp.campaigns.send({cid: self.mailchimp_campaign_id})
103
+ response['errors'].nil?
104
+ end
105
+
106
+ ##
107
+ # schedule a mailchimp campaign
108
+ def schedule_campaign
109
+ response = Chimpmunk.mailchimp.campaigns.schedule({
110
+ cid: self.mailchimp_campaign_id,
111
+ schedule_time: self.send_date.to_time.to_s.gsub(' UTC', '')
112
+ })
113
+ response['errors'].nil? && self.update_attribute(:scheduled, true)
114
+ end
115
+
116
+ ##
117
+ # un-schedule email campaign that has been scheduled
118
+ def unschedule_campaign
119
+ response = Chimpmunk.mailchimp.campaigns.unschedule({cid: self.mailchimp_campaign_id})
120
+ response['errors'].nil? && self.update_attribute(:scheduled, false)
121
+ end
122
+
123
+ ##
124
+ # opened percentage display as float
125
+ def opened_percentage
126
+ if opened and emails_sent
127
+ "%2.1f %" % (opened.to_f / emails_sent.to_f * 100)
128
+ end
129
+ end
130
+
131
+ ##
132
+ # clicked percentage display as float
133
+ def clicked_percentage
134
+ if clicked and emails_sent
135
+ "%2.1f %" % (clicked.to_f / emails_sent.to_f * 100)
136
+ end
137
+ end
138
+
139
+ private
140
+
141
+ def action_view
142
+ @action_view ||= ActionView::Base.new(Rails.configuration.paths["app/views"])
143
+ @action_view.class_eval do
144
+ include Rails.application.routes.url_helpers
145
+ include ApplicationHelper
146
+
147
+ def protect_against_forgery?
148
+ false
149
+ end
150
+ end
151
+ @action_view
152
+ end
153
+
154
+ ##
155
+ # destroy mailchimp campaign before db destroy
156
+ def destroy_mailchimp_campaign
157
+ # don't care if it fails (probably was deleted from mailchimp web before deleted here)
158
+ begin
159
+ Chimpmunk.mailchimp.campaigns.delete({cid: self.mailchimp_campaign_id})
160
+ rescue Gibbon::MailChimpError
161
+ true
162
+ end
163
+ end
164
+
165
+ ##
166
+ # updates the campaign content on mailchimp before db update
167
+ def update_content
168
+ response = Chimpmunk.mailchimp.campaigns.update(
169
+ cid: self.mailchimp_campaign_id,
170
+ name: "content",
171
+ value: get_content
172
+ )
173
+
174
+ !response['errors'].any?
175
+ end
176
+
177
+ ##
178
+ # updates the campaign on mailchimp before db update
179
+ def update_campaign
180
+ begin
181
+ response = Chimpmunk.mailchimp.campaigns.update({
182
+ cid: self.mailchimp_campaign_id,
183
+ name: "options",
184
+ value: campaign_update_hash
185
+ })
186
+ rescue Gibbon::MailChimpError => e
187
+ self.errors.add(:base, e.message)
188
+ return false
189
+ end
190
+
191
+ if response["errors"].empty?
192
+ data = response["data"]
193
+ self.mailchimp_list_id = data["list_id"]
194
+ self.title = data["title"]
195
+ self.subject = data["subject"]
196
+ self.from_name = data["from_name"]
197
+ self.from_email = data["from_email"]
198
+ true
199
+ else
200
+ false
201
+ end
202
+ end
203
+
204
+ ##
205
+ # returns the update hash needed for the mailchimp request
206
+ def campaign_update_hash
207
+ {
208
+ list_id: email_list.mailchimp_id,
209
+ title: title,
210
+ subject: subject,
211
+ from_name: from_name,
212
+ from_email: from_email
213
+ }.reject{|k, v| v.nil?}
214
+ end
215
+
216
+ ##
217
+ # create campaign on mailchimp before db creation
218
+ def create_campaign
219
+ opts = {
220
+ type: "regular",
221
+ options: {
222
+ subject: self.subject,
223
+ from_email: self.from_email,
224
+ from_name: self.from_name,
225
+ template_id: self.email_template.try(:mailchimp_id),
226
+ inline_css: true
227
+ },
228
+ content: get_content
229
+ }
230
+
231
+ # list or segment(group)
232
+ if group = self.email_list_group
233
+ opts[:segment_opts] = {
234
+ match: "all",
235
+ conditions: [
236
+ {
237
+ field: "interests-#{group.email_list_grouping.mailchimp_grouping_id}",
238
+ value: group.name,
239
+ op: 'one'
240
+ }
241
+ ]
242
+ }
243
+ opts[:options][:list_id] = group.email_list_grouping.email_list.mailchimp_id
244
+ else
245
+ opts[:options][:list_id] = self.email_list.try(:mailchimp_id)
246
+ end
247
+
248
+ response = Chimpmunk.mailchimp.campaigns.create(opts)
249
+ if response["errors"].nil?
250
+ self.skip_update_callbacks = true
251
+ self.update_attribute(:mailchimp_campaign_id, response["id"])
252
+ else
253
+ false
254
+ end
255
+ end
256
+
257
+ ##
258
+ # TODO: figure out how to do this
259
+ # returns the html content for the new campaign
260
+ def get_content
261
+ content = {sections: {}}
262
+
263
+ # insert intro section if it exists
264
+ unless intro_section_blank?
265
+ content[:sections][:intro_section] = action_view.render 'eflyers/newsletter/intro', email_campaign: self
266
+ end
267
+
268
+ # insert divider if both intro sections and featured post exist
269
+ unless intro_section_blank? || featured_post.nil?
270
+ content[:sections][:divider] = action_view.render 'eflyers/newsletter/divider'
271
+ end
272
+
273
+ # insert featured post if it exists
274
+ unless featured_post.nil?
275
+ content[:sections][:featured_article] = action_view.render 'eflyers/newsletter/featured_article', email_campaign: self
276
+ end
277
+
278
+ # insert tending posts
279
+ content[:sections][:trending_section] = ''
280
+ trending_posts.in_groups_of(2) do |first_post, second_post|
281
+ content[:sections][:trending_section] += action_view.render 'eflyers/newsletter/trending', first_post: first_post, second_post: second_post
282
+ end
283
+
284
+ # insert sponsored section if it exsists
285
+ unless sponsored_section_blank?
286
+ content[:sections][:sponsored_section] = action_view.render 'eflyers/newsletter/sponsored', email_campaign: self
287
+ end
288
+
289
+ content[:sections][:directory_section] = action_view.render 'eflyers/newsletter/directory',
290
+ teacher_1: self.directory_teachers.first,
291
+ teacher_2: self.directory_teachers.second,
292
+ event_1: self.directory_events.first,
293
+ event_2: self.directory_events.second,
294
+ studio_1: self.directory_studios.first,
295
+ studio_2: self.directory_studios.second
296
+
297
+ unless teacher_tip_post.nil?
298
+ content[:sections][:teacher_tip] = action_view.render 'eflyers/newsletter/teacher_tip', email_campaign: self
299
+ end
300
+
301
+ content
302
+ end
303
+
304
+ if defined?(RailsAdmin)
305
+
306
+ rails_admin do
307
+ navigation_label 'Mailchimp'
308
+
309
+ configure :background_color, :color
310
+ configure :foreground_color, :color
311
+ configure :send_date, :datetime
312
+
313
+ list do
314
+ field :title
315
+ field :email_list
316
+ field :status
317
+ field :emails_sent
318
+ field :opened_percentage do
319
+ label "Opened"
320
+ end
321
+ field :clicked_percentage do
322
+ label "Clicked"
323
+ end
324
+ field :subject
325
+ field :from_email
326
+ field :from_name
327
+ end
328
+
329
+ edit do
330
+ field :title
331
+ field :email_list do
332
+ inline_add false
333
+ inline_edit false
334
+ end
335
+ field :email_list_group
336
+ field :email_template do
337
+ inline_add false
338
+ inline_edit false
339
+ end
340
+ field :subject
341
+ field :from_name do
342
+ default_value 'FIXME'
343
+ end
344
+ field :from_email do
345
+ default_value 'FIXME'
346
+ end
347
+ field :send_date
348
+ end
349
+ end
350
+ end
351
+ end
352
+ end