newsletter 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +15 -0
- data/.gitignore +20 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/Gemfile +26 -0
- data/Gemfile.lock +199 -0
- data/Guardfile +24 -0
- data/LICENSE +22 -0
- data/MIT-LICENSE +20 -0
- data/Manifest.txt +220 -0
- data/Procfile +2 -0
- data/README +243 -0
- data/README.rdoc +3 -0
- data/Rakefile +29 -0
- data/app/assets/images/newsletter/.gitkeep +0 -0
- data/app/assets/javascripts/newsletter/application.js +15 -0
- data/app/assets/stylesheets/newsletter/application.css +13 -0
- data/app/controllers/newsletter/application_controller.rb +27 -0
- data/app/controllers/newsletter/areas_controller.rb +63 -0
- data/app/controllers/newsletter/designs_controller.rb +51 -0
- data/app/controllers/newsletter/elements_controller.rb +75 -0
- data/app/controllers/newsletter/fields_controller.rb +49 -0
- data/app/controllers/newsletter/newsletters_controller.rb +104 -0
- data/app/controllers/newsletter/pieces_controller.rb +72 -0
- data/app/helpers/newsletter/application_helper.rb +4 -0
- data/app/helpers/newsletter/areas_helper.rb +4 -0
- data/app/helpers/newsletter/assets_helper.rb +4 -0
- data/app/helpers/newsletter/designs_helper.rb +11 -0
- data/app/helpers/newsletter/elements_helper.rb +4 -0
- data/app/helpers/newsletter/field_values_helper.rb +4 -0
- data/app/helpers/newsletter/fields_helper.rb +4 -0
- data/app/helpers/newsletter/newsletters_helper.rb +43 -0
- data/app/helpers/newsletter/pieces_helper.rb +4 -0
- data/app/models/newsletter/area.rb +50 -0
- data/app/models/newsletter/asset.rb +31 -0
- data/app/models/newsletter/design.rb +160 -0
- data/app/models/newsletter/element.rb +169 -0
- data/app/models/newsletter/field/inline_asset.rb +67 -0
- data/app/models/newsletter/field/text.rb +15 -0
- data/app/models/newsletter/field/text_area.rb +15 -0
- data/app/models/newsletter/field.rb +115 -0
- data/app/models/newsletter/field_value.rb +20 -0
- data/app/models/newsletter/newsletter.rb +125 -0
- data/app/models/newsletter/piece.rb +96 -0
- data/app/models/newsletter.rb +11 -0
- data/app/uploaders/newsletter/asset_uploader.rb +51 -0
- data/app/views/layouts/newsletter/application.html.erb +135 -0
- data/app/views/newsletter/areas/_area.html.erb +49 -0
- data/app/views/newsletter/designs/_area_fields.html.erb +4 -0
- data/app/views/newsletter/designs/_form.html.erb +41 -0
- data/app/views/newsletter/designs/_newsletter_area.html.erb +17 -0
- data/app/views/newsletter/designs/edit.html.erb +14 -0
- data/app/views/newsletter/designs/index.html.erb +31 -0
- data/app/views/newsletter/designs/new.html.erb +9 -0
- data/app/views/newsletter/designs/show.html.erb +22 -0
- data/app/views/newsletter/elements/_field_fields.html.erb +27 -0
- data/app/views/newsletter/elements/_form.html.erb +51 -0
- data/app/views/newsletter/elements/_newsletter_field.html.erb +37 -0
- data/app/views/newsletter/elements/edit.html.erb +12 -0
- data/app/views/newsletter/elements/index.html.erb +32 -0
- data/app/views/newsletter/elements/new.html.erb +9 -0
- data/app/views/newsletter/elements/show.html.erb +22 -0
- data/app/views/newsletter/fields/_form.html.erb +15 -0
- data/app/views/newsletter/fields/_inline_asset.html.erb +36 -0
- data/app/views/newsletter/fields/_text.html.erb +4 -0
- data/app/views/newsletter/fields/_text_area.html.erb +7 -0
- data/app/views/newsletter/newsletters/_form.html.erb +21 -0
- data/app/views/newsletter/newsletters/_head.html.erb +48 -0
- data/app/views/newsletter/newsletters/_newsletter.html.erb +6 -0
- data/app/views/newsletter/newsletters/_newsletter_area.html.erb +30 -0
- data/app/views/newsletter/newsletters/archive.html.erb +9 -0
- data/app/views/newsletter/newsletters/edit.html.erb +37 -0
- data/app/views/newsletter/newsletters/index.html.erb +29 -0
- data/app/views/newsletter/newsletters/new.html.erb +9 -0
- data/app/views/newsletter/newsletters/show.html.erb +6 -0
- data/app/views/newsletter/pieces/_form.html.erb +19 -0
- data/app/views/newsletter/pieces/edit.html.erb +11 -0
- data/app/views/newsletter/pieces/new.html.erb +10 -0
- data/app/views/newsletter/pieces/show.html.erb +2 -0
- data/assets/docs/index.html +45 -0
- data/assets/docs/newsletter_templates/index.html +45 -0
- data/config/routes.rb +33 -0
- data/db/migrate/001_newsletter_initial.rb +104 -0
- data/db/migrate/002_carrier_wave_for_assets.rb +9 -0
- data/db/pre-scoped/002_newsletter_scoped.rb +61 -0
- data/doc/README_FOR_APP +2 -0
- data/doc/newsletter.wiki.txt +130 -0
- data/doc/start_rails_engine_with_rspec.txt +43 -0
- data/engine_plan.rb +13 -0
- data/features/step_definitions/webrat_steps.rb +115 -0
- data/features/support/env.rb +17 -0
- data/features/support/paths.rb +27 -0
- data/lib/deleteable.rb +50 -0
- data/lib/newsletter/config.rb +50 -0
- data/lib/newsletter/engine.rb +62 -0
- data/lib/newsletter/settings.rb +50 -0
- data/lib/newsletter/version.rb +3 -0
- data/lib/newsletter.rb +7 -0
- data/lib/tasks/newsletter.rake +84 -0
- data/lib/tasks/newsletter_tasks.rake +4 -0
- data/lib/tasks/rspec.rake +158 -0
- data/newsletter.gemspec +30 -0
- data/newsletters/exports/example-export.yaml +213 -0
- data/script/rails +8 -0
- data/spec/rcov.opts +2 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +41 -0
- data/spec/test_app/README.rdoc +261 -0
- data/spec/test_app/Rakefile +7 -0
- data/spec/test_app/app/assets/javascripts/application.js +15 -0
- data/spec/test_app/app/assets/stylesheets/application.css +13 -0
- data/spec/test_app/app/controllers/application_controller.rb +3 -0
- data/spec/test_app/app/helpers/application_helper.rb +2 -0
- data/spec/test_app/app/mailers/.gitkeep +0 -0
- data/spec/test_app/app/models/.gitkeep +0 -0
- data/spec/test_app/app/views/layouts/application.html.erb +14 -0
- data/spec/test_app/config/application.rb +65 -0
- data/spec/test_app/config/boot.rb +10 -0
- data/spec/test_app/config/database.yml +25 -0
- data/spec/test_app/config/environment.rb +5 -0
- data/spec/test_app/config/environments/development.rb +37 -0
- data/spec/test_app/config/environments/production.rb +67 -0
- data/spec/test_app/config/environments/test.rb +37 -0
- data/spec/test_app/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/test_app/config/initializers/inflections.rb +15 -0
- data/spec/test_app/config/initializers/mime_types.rb +5 -0
- data/spec/test_app/config/initializers/secret_token.rb +7 -0
- data/spec/test_app/config/initializers/session_store.rb +8 -0
- data/spec/test_app/config/initializers/wrap_parameters.rb +14 -0
- data/spec/test_app/config/locales/en.yml +5 -0
- data/spec/test_app/config/newsletter.yml +9 -0
- data/spec/test_app/config/routes.rb +4 -0
- data/spec/test_app/config.ru +4 -0
- data/spec/test_app/db/migrate/20131222171229_newsletter_initial.rb +104 -0
- data/spec/test_app/db/schema.rb +116 -0
- data/spec/test_app/lib/assets/.gitkeep +0 -0
- data/spec/test_app/log/.gitkeep +0 -0
- data/spec/test_app/public/404.html +26 -0
- data/spec/test_app/public/422.html +26 -0
- data/spec/test_app/public/500.html +25 -0
- data/spec/test_app/public/favicon.ico +0 -0
- data/spec/test_app/script/rails +6 -0
- data/zeus.json +22 -0
- metadata +352 -0
@@ -0,0 +1,72 @@
|
|
1
|
+
module Newsletter
|
2
|
+
class PiecesController < ::Newsletter::ApplicationController
|
3
|
+
layout 'admin'
|
4
|
+
before_filter :find_piece, :except => [:new,:create,:index]
|
5
|
+
before_filter :find_newsletter
|
6
|
+
before_filter :find_element
|
7
|
+
before_filter :find_area
|
8
|
+
|
9
|
+
def index
|
10
|
+
@pieces = @newsletter.pieces.active
|
11
|
+
end
|
12
|
+
|
13
|
+
def show
|
14
|
+
end
|
15
|
+
|
16
|
+
def new
|
17
|
+
@piece = Piece.new({
|
18
|
+
:area_id => @area.id,
|
19
|
+
:element_id => @element.id,
|
20
|
+
:newsletter_id => @newsletter.id
|
21
|
+
})
|
22
|
+
end
|
23
|
+
|
24
|
+
def edit
|
25
|
+
end
|
26
|
+
|
27
|
+
def create
|
28
|
+
@piece = Piece.new(params[:piece])
|
29
|
+
if @piece.save
|
30
|
+
flash[:notice] = 'Piece was successfully created.'
|
31
|
+
redirect_to(edit_newsletter_path(@newsletter))
|
32
|
+
else
|
33
|
+
render :action => "new"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def update
|
38
|
+
if @piece.update_attributes(params[:piece])
|
39
|
+
flash[:notice] = 'Piece was successfully updated.'
|
40
|
+
redirect_to(edit_newsletter_path(@newsletter))
|
41
|
+
else
|
42
|
+
render :action => "edit"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def destroy
|
47
|
+
@piece.destroy
|
48
|
+
redirect_to(newsletter_path(@newsletter,:editor=>1))
|
49
|
+
end
|
50
|
+
|
51
|
+
protected
|
52
|
+
|
53
|
+
def find_piece
|
54
|
+
@piece = Piece.find_by_id(params[:id])
|
55
|
+
end
|
56
|
+
|
57
|
+
def find_newsletter
|
58
|
+
return @newsletter = @piece.newsletter unless @piece.nil?
|
59
|
+
@newsletter = ::Newsletter::Newsletter.find(params[:newsletter_id] || params[:piece][:newsletter_id])
|
60
|
+
end
|
61
|
+
|
62
|
+
def find_element
|
63
|
+
return @element = @piece.element unless @piece.nil?
|
64
|
+
@element = Element.find(params[:element_id] || params[:piece][:element_id])
|
65
|
+
end
|
66
|
+
|
67
|
+
def find_area
|
68
|
+
return @area = @piece.area unless @piece.nil?
|
69
|
+
@area = Area.find(params[:area_id] || params[:piece][:area_id])
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Newsletter
|
2
|
+
module DesignsHelper
|
3
|
+
#left 'newsletter' in function name for helper scoping
|
4
|
+
def add_newsletter_area_link(name)
|
5
|
+
link_to_function name, {:class => "button"} do |page|
|
6
|
+
page.insert_html :bottom, :areas, :partial => 'newsletter_area',
|
7
|
+
:object => Area.new
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Newsletter
|
2
|
+
module NewslettersHelper
|
3
|
+
def render_area
|
4
|
+
render @newsletter.design.areas.by_newsletter(@newsletter)
|
5
|
+
end
|
6
|
+
|
7
|
+
def is_email?
|
8
|
+
params[:mode].eql?('email')
|
9
|
+
end
|
10
|
+
|
11
|
+
def filter(text)
|
12
|
+
new_text = filter_eols_to_brs(text,text)
|
13
|
+
new_text = filter_email_addresses_to_mailtos(new_text,text)
|
14
|
+
new_text = filter_urls_to_links(new_text,text)
|
15
|
+
new_text.html_safe
|
16
|
+
end
|
17
|
+
|
18
|
+
protected
|
19
|
+
def filter_eols_to_brs(text,orig_text)
|
20
|
+
return '' if text.blank?
|
21
|
+
return '' if orig_text =~ /<br\s*\/>/i
|
22
|
+
text.gsub(/(\r\n|\r|\n)/,'<br/>')
|
23
|
+
end
|
24
|
+
|
25
|
+
def filter_email_addresses_to_mailtos(text,orig_text)
|
26
|
+
return '' if text.blank?
|
27
|
+
return text if orig_text =~ /<a\s+href/i
|
28
|
+
text.gsub(/([a-z0-9!#\$%&'*+\/=?^_`{|}~-]+(?:\.[a-z0-9!#\$%&'*+\/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+(?:[A-Z]{2}|com|org|net|gov|mil|biz|info|mobi|name|aero|jobs|museum)\b)/mi,%Q|<a href="mailto:\\1" target="_blank">\\1</a>|)
|
29
|
+
end
|
30
|
+
|
31
|
+
def filter_urls_to_links(text,orig_text)
|
32
|
+
return '' if text.blank?
|
33
|
+
return text if orig_text =~ /<a\s+href/i
|
34
|
+
text.gsub(/(\b(?:(?:https?|ftp|file):\/\/|www\.|ftp\.)(?:\([-A-Z0-9+&@#\/%=~_|$?!:,.]*\)|[-A-Z0-9+&@#\/%=~_|$?!:,.])*(?:\([-A-Z0-9+&@#\/%=~_|$?!:,.]*\)|[A-Z0-9+&@#\/%=~_|$]))/mi) do |match|
|
35
|
+
if match.to_s.include?('://')
|
36
|
+
%Q|<a href="#{match}" target="_blank">#{match}</a>|
|
37
|
+
else
|
38
|
+
%Q|<a href="http://#{match}" target="_blank">#{match}</a>|
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
=begin rdoc
|
2
|
+
Author:: Chris Hauboldt (mailto:biz@lnstar.com)
|
3
|
+
Copyright:: 2009 Lone Star Internet Inc.
|
4
|
+
|
5
|
+
Areas define what elements can exist in a part of a newleter design. Also used to group and order actual Pieces in a filled in Newsletter.
|
6
|
+
|
7
|
+
=end
|
8
|
+
|
9
|
+
module Newsletter
|
10
|
+
class Area < ActiveRecord::Base
|
11
|
+
self.table_name = "#{::Newsletter.table_prefix}areas"
|
12
|
+
belongs_to :design, :class_name => 'Newsletter::Design'
|
13
|
+
has_and_belongs_to_many :elements, :order => 'name', :join_table =>
|
14
|
+
"#{::Newsletter.table_prefix}areas_#{::Newsletter.table_prefix}elements",
|
15
|
+
:class_name => 'Newsletter::Element'
|
16
|
+
has_many :pieces, :order => 'sequence', :class_name => "Newsletter::Piece"
|
17
|
+
belongs_to :updated_by, :class_name => 'User'
|
18
|
+
|
19
|
+
attr_accessor :should_destroy
|
20
|
+
|
21
|
+
attr_protected :id
|
22
|
+
|
23
|
+
validates_presence_of :name
|
24
|
+
#FIXME: make this work with deletable or convert to auditable, and extend it to access destroyed records
|
25
|
+
#validates_uniqueness_of :name, :scope => :design_id
|
26
|
+
|
27
|
+
scope :active, :conditions => {:deleted_at => nil}
|
28
|
+
scope :by_name, lambda {|name| {:conditions => {:name => name}}}
|
29
|
+
|
30
|
+
include Deleteable
|
31
|
+
|
32
|
+
# returns field data so that Newsletter::Design.export(instance) can export itself to a YAML file
|
33
|
+
def export_fields
|
34
|
+
{ :name => name,
|
35
|
+
:description => description
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
# builds areas from data pulled out of an exported YAML file by Newsletter::Design.import(class)
|
40
|
+
def self.import(design,data)
|
41
|
+
area = Area.create(:name => data[:name], :description => data[:description])
|
42
|
+
area.design = design
|
43
|
+
area.save
|
44
|
+
end
|
45
|
+
|
46
|
+
def should_destroy?
|
47
|
+
should_destroy.to_i == 1
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
=begin rdoc
|
2
|
+
Author:: Chris Hauboldt (mailto:biz@lnstar.com)
|
3
|
+
Copyright:: 2009 Lone Star Internet Inc.
|
4
|
+
|
5
|
+
Wrapper for attachment_fu files plugin, and is used by NewsletterPieces to save assets.
|
6
|
+
|
7
|
+
=end
|
8
|
+
|
9
|
+
module Newsletter
|
10
|
+
class Asset < ActiveRecord::Base
|
11
|
+
self.table_name = "#{::Newsletter.table_prefix}assets"
|
12
|
+
belongs_to :field, :conditions => {:type => 'Newsletter::Field::InlineAsset'},
|
13
|
+
:class_name => 'Newsletter::Field::InlineAsset'
|
14
|
+
belongs_to :piece, :class_name => 'Newsletter::Piece'
|
15
|
+
|
16
|
+
mount_uploader :image, AssetUploader
|
17
|
+
|
18
|
+
attr_protected :id
|
19
|
+
|
20
|
+
scope :by_piece, lambda{|piece| where("piece_id IS NOT NULL AND piece_id=?", piece.try(:id)) }
|
21
|
+
|
22
|
+
def public_filename
|
23
|
+
return File.join(::Newsletter::Asset.build_public_dirname(id),File.basename(self[:image])) if self[:image].present?
|
24
|
+
nil
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.build_public_dirname(id)
|
28
|
+
"#{::Newsletter.asset_path}/#{("%08d" %id)[-8,4]}/#{("%08d" %id)[-4,4]}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,160 @@
|
|
1
|
+
=begin rdoc
|
2
|
+
Author:: Chris Hauboldt (mailto:biz@lnstar.com)
|
3
|
+
Copyright:: 2009 Lone Star Internet Inc.
|
4
|
+
|
5
|
+
Newsletter::Designs define a main layout, with areas to group Elements/Pieces.
|
6
|
+
|
7
|
+
|
8
|
+
=end
|
9
|
+
|
10
|
+
module Newsletter
|
11
|
+
class Design < ActiveRecord::Base
|
12
|
+
self.table_name = "#{::Newsletter.table_prefix}designs"
|
13
|
+
has_many :areas, :order => :name, :class_name => 'Newsletter::Area'
|
14
|
+
has_many :elements, :order => :name, :class_name => 'Newsletter::Element'
|
15
|
+
belongs_to :updated_by, :class_name => 'User'
|
16
|
+
after_create :write_design
|
17
|
+
|
18
|
+
accepts_nested_attributes_for :areas
|
19
|
+
|
20
|
+
attr_accessible :name, :description, :html_text, :areas_attributes
|
21
|
+
|
22
|
+
scope :active, :conditions => {:deleted_at => nil}
|
23
|
+
|
24
|
+
validates_presence_of :name
|
25
|
+
|
26
|
+
# attr_protected :id
|
27
|
+
#FIXME: make this work with deletable or convert to auditable, and extend it to access destroyed records
|
28
|
+
#validates_uniqueness_of :name
|
29
|
+
|
30
|
+
# Export a design's data to a YAML file.
|
31
|
+
def export(filename=nil)
|
32
|
+
filename = "#{::Newsletter.designs_path}/exports/#{name_as_path}-export.yaml" unless filename
|
33
|
+
FileUtils.mkdir_p(File.dirname(filename))
|
34
|
+
File.open(filename,'w') do |file|
|
35
|
+
YAML.dump( {
|
36
|
+
:name => name,
|
37
|
+
:html_text => html_text,
|
38
|
+
:description => description,
|
39
|
+
:areas => areas.collect{|area| area.export_fields},
|
40
|
+
:elements => elements.collect{|element| element.export_fields}
|
41
|
+
},file)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Import a design from a YAML file,
|
46
|
+
# Parameters:
|
47
|
+
# filename - path/name of file on filesystem
|
48
|
+
# design_name => rename design if already taken
|
49
|
+
def self.import(filename,design_name=nil)
|
50
|
+
raise "You must give a filename to import!" unless filename
|
51
|
+
data = YAML.load_file(filename)
|
52
|
+
transaction do
|
53
|
+
data[:name] = design_name if design_name
|
54
|
+
design = Design.create!(:name => data[:name],
|
55
|
+
:html_text => data[:html_text],
|
56
|
+
:description => data[:description])
|
57
|
+
data[:areas].each do |area_data|
|
58
|
+
Area.import(design,area_data)
|
59
|
+
end
|
60
|
+
data[:elements].each do |element_data|
|
61
|
+
Element.import(design,element_data)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# returns path to newsletter design for use in views and is the same for actual file
|
67
|
+
def view_path(this_name=nil)
|
68
|
+
"#{base_design_path(this_name)}/layout.html.erb"
|
69
|
+
end
|
70
|
+
|
71
|
+
#
|
72
|
+
def base_design_path(this_name=nil)
|
73
|
+
"#{::Newsletter.designs_path}/designs/#{name_as_path(this_name)}"
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
|
78
|
+
def html_text
|
79
|
+
return @html_text if @html_text
|
80
|
+
@html_text = read_design
|
81
|
+
end
|
82
|
+
|
83
|
+
def html_text=(text)
|
84
|
+
@html_text = text
|
85
|
+
end
|
86
|
+
|
87
|
+
# def area_attributes=(area_attributes)
|
88
|
+
# area_attributes.each do |attributes|
|
89
|
+
# if attributes[:id].blank?
|
90
|
+
# Rails.logger.debug "Building Area : #{attributes.inspect}"
|
91
|
+
# areas.build(attributes)
|
92
|
+
# else
|
93
|
+
# Rails.logger.debug "Setting Area data: #{attributes.inspect}"
|
94
|
+
# area = areas.detect{|area| area.id == attributes[:id].to_i}
|
95
|
+
# area.attributes = attributes
|
96
|
+
# end
|
97
|
+
# end
|
98
|
+
# end
|
99
|
+
|
100
|
+
|
101
|
+
def name=(new_name)
|
102
|
+
return if self[:name].eql?(new_name)
|
103
|
+
@old_name = self[:name] unless @old_name
|
104
|
+
self[:name] = new_name
|
105
|
+
end
|
106
|
+
|
107
|
+
def save(*args)
|
108
|
+
transaction do
|
109
|
+
move_design_on_name_change
|
110
|
+
write_design
|
111
|
+
super
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# returns a version of name that is nice for filesytem use
|
116
|
+
def name_as_path(this_name=nil)
|
117
|
+
this_name = name unless this_name
|
118
|
+
this_name.gsub(/[^a-zA-Z0-9-]/,'_')
|
119
|
+
end
|
120
|
+
|
121
|
+
include Deleteable
|
122
|
+
protected
|
123
|
+
def read_design
|
124
|
+
File.readlines(view_path).join
|
125
|
+
rescue => e
|
126
|
+
#flash[:warning] << "Couldn't open design for element '#{name}' which should exist at: #{file_path} #{e.message}"
|
127
|
+
""
|
128
|
+
end
|
129
|
+
|
130
|
+
def write_all_designs
|
131
|
+
areas.collect{|area| area.elements}.flatten.uniq.each{|element| element.send(:write_design)}
|
132
|
+
write_design
|
133
|
+
end
|
134
|
+
|
135
|
+
def move_design_on_name_change
|
136
|
+
return unless @old_name and File.exists?(view_path(@old_name))
|
137
|
+
FileUtils.mv(base_design_path(@old_name),base_design_path)
|
138
|
+
end
|
139
|
+
|
140
|
+
def write_design
|
141
|
+
FileUtils.mkdir_p(File.dirname(view_path)) unless File.exists?(File.dirname(view_path))
|
142
|
+
File.open(view_path,File::WRONLY|File::TRUNC|File::CREAT) do |file|
|
143
|
+
file.write html_text
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
|
148
|
+
# def save_areas
|
149
|
+
# areas.each do |area|
|
150
|
+
# if area.should_destroy?
|
151
|
+
# Rails.logger.debug "Destroying newsletter area: #{area.inspect}"
|
152
|
+
# area.destroy
|
153
|
+
# else
|
154
|
+
# Rails.logger.debug "Saving newsletter area: #{area.inspect}"
|
155
|
+
# area.save
|
156
|
+
# end
|
157
|
+
# end
|
158
|
+
# end
|
159
|
+
end
|
160
|
+
end
|
@@ -0,0 +1,169 @@
|
|
1
|
+
=begin rdoc
|
2
|
+
Author:: Chris Hauboldt (mailto:biz@lnstar.com)
|
3
|
+
Copyright:: 2009 Lone Star Internet Inc.
|
4
|
+
|
5
|
+
Newsletter::Elements define the way a Newsletter::Piece looks with an erb design and the data it can hold with Newsletter::Fields. They can exist in multiple Newsletter::Areas, but currently belong to only 1 design.
|
6
|
+
|
7
|
+
=end
|
8
|
+
|
9
|
+
module Newsletter
|
10
|
+
class Element < ActiveRecord::Base
|
11
|
+
self.table_name = "#{::Newsletter.table_prefix}elements"
|
12
|
+
has_and_belongs_to_many :areas, :class_name => 'Newsletter::Area',
|
13
|
+
:join_table => "#{::Newsletter.table_prefix}areas_#{::Newsletter.table_prefix}elements"
|
14
|
+
has_many :fields, :order => 'sequence', :class_name => 'Newsletter::Field'
|
15
|
+
has_many :pieces, :class_name => 'Newsletter::Piece'
|
16
|
+
belongs_to :design, :class_name => 'Newsletter::Design'
|
17
|
+
belongs_to :updated_by, :class_name => 'User'
|
18
|
+
|
19
|
+
scope :by_design, lambda{|design| {:conditions =>{:design_id => design.id}}}
|
20
|
+
|
21
|
+
validates_presence_of :name
|
22
|
+
|
23
|
+
accepts_nested_attributes_for :fields
|
24
|
+
|
25
|
+
attr_protected :id
|
26
|
+
#FIXME: make this work with deletable or convert to auditable, and extend it to access destroyed records
|
27
|
+
#validates_uniqueness_of :name, :scope => :design_id
|
28
|
+
|
29
|
+
# defines the design path for the element as used in a render :partial => (without '_')
|
30
|
+
def view_path(this_name=nil)
|
31
|
+
this_name = self[:name] unless this_name
|
32
|
+
"#{::Newsletter.designs_path}/designs/#{design.name.gsub(/[^a-zA-Z0-9-]/,'_')}/elements/#{name_as_path(this_name)}.html.erb"
|
33
|
+
end
|
34
|
+
|
35
|
+
# returns a version of name that is nice for filesytem use
|
36
|
+
def name_as_path(this_name=nil)
|
37
|
+
this_name = name unless this_name
|
38
|
+
this_name.gsub(/[^a-zA-Z0-9-]/,'_').downcase
|
39
|
+
end
|
40
|
+
|
41
|
+
# defines where the file is in the filesystem
|
42
|
+
def file_path(this_name=nil)
|
43
|
+
File.dirname(view_path(this_name)) + '/_' + File.basename(view_path(this_name))
|
44
|
+
end
|
45
|
+
|
46
|
+
# used to record old name of an element such that the design can be moved to the new name
|
47
|
+
def name=(new_name)
|
48
|
+
return if self[:name].eql?(new_name)
|
49
|
+
@old_name = self[:name] unless @old_name
|
50
|
+
self[:name] = new_name
|
51
|
+
end
|
52
|
+
|
53
|
+
# retrieves the html erb design from the file system
|
54
|
+
def html_text
|
55
|
+
return @html_text if @html_text
|
56
|
+
@html_text = read_design
|
57
|
+
end
|
58
|
+
|
59
|
+
# sets and saves the html erb design to update the file after a successful save
|
60
|
+
def html_text=(text)
|
61
|
+
@html_text = text
|
62
|
+
end
|
63
|
+
|
64
|
+
def update_attributes(params={})
|
65
|
+
transaction do
|
66
|
+
super
|
67
|
+
end
|
68
|
+
end
|
69
|
+
# {"0"=>{"name"=>"asdfas", "label"=>"asdfasd", "description"=>"fasdfasdf",
|
70
|
+
# "type"=>"Newsletter::Field::TextArea", "_destroy"=>"false", "id"=>"22"},
|
71
|
+
# "1"=>{"name"=>"asdfasdf", "label"=>"asdfasdf", "description"=>"asdfasdf",
|
72
|
+
# "type"=>"Newsletter::Field::InlineAsset", "_destroy"=>"false", "id"=>"23"}}
|
73
|
+
# # # used to modify Newsletter::Fields in-form
|
74
|
+
def fields_attributes=(fields_attributes)
|
75
|
+
@fields_attributes = fields_attributes
|
76
|
+
end
|
77
|
+
|
78
|
+
def save_fields
|
79
|
+
@fields_attributes.each_pair do |index,attributes|
|
80
|
+
should_destroy = ['true','1'].include?attributes.delete(:_destroy)
|
81
|
+
if attributes[:id].blank?
|
82
|
+
next if should_destroy
|
83
|
+
attributes.delete(:id)
|
84
|
+
klass = attributes.delete(:type)
|
85
|
+
fields << klass.constantize.new(attributes)
|
86
|
+
else
|
87
|
+
id = attributes.delete(:id).to_i
|
88
|
+
if should_destroy
|
89
|
+
fields.where(id: id).limit(1).each(&:destroy)
|
90
|
+
else
|
91
|
+
type = attributes.delete(:type)
|
92
|
+
field = fields.detect{|field| field.id == id}
|
93
|
+
field.update_attributes(attributes)
|
94
|
+
field = Field.morph(field,type) unless field.class.name.eql?(type)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# returns field data so that Newsletter::Design.export(instance) can export itself to a YAML file
|
101
|
+
def export_fields
|
102
|
+
{ :name => name,
|
103
|
+
:description => description,
|
104
|
+
:html_text => html_text,
|
105
|
+
:areas => areas.collect{|area| area.export_fields},
|
106
|
+
:fields => fields.collect{|field| field.export_fields}
|
107
|
+
}
|
108
|
+
end
|
109
|
+
|
110
|
+
# builds areas from data pulled out of an exported YAML file by Newsletter::Design.import(class)
|
111
|
+
def self.import(design,data)
|
112
|
+
element = Element.new(:name => data[:name],
|
113
|
+
:html_text => data[:html_text],
|
114
|
+
:description => data[:description])
|
115
|
+
element.design = design
|
116
|
+
element.save
|
117
|
+
data[:areas].each do |area_data|
|
118
|
+
element.areas <<
|
119
|
+
Area.find_all_by_design_id_and_name(design.id,area_data[:name])
|
120
|
+
end
|
121
|
+
data[:fields].each do |field_data|
|
122
|
+
Field.import(element,field_data)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def save(*args)
|
127
|
+
transaction do
|
128
|
+
move_design_on_name_change
|
129
|
+
write_design
|
130
|
+
super
|
131
|
+
save_fields
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
include Deleteable
|
136
|
+
protected
|
137
|
+
def read_design
|
138
|
+
File.readlines(file_path).join
|
139
|
+
rescue => e
|
140
|
+
#send back an empty string if no file exists yet for the design... don't raise an error
|
141
|
+
""
|
142
|
+
end
|
143
|
+
|
144
|
+
def write_design
|
145
|
+
FileUtils.mkdir_p(File.dirname(file_path)) unless File.exists?(File.dirname(file_path))
|
146
|
+
File.open(file_path,File::WRONLY|File::TRUNC|File::CREAT) do |file|
|
147
|
+
file.write html_text
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def move_design_on_name_change
|
152
|
+
return unless @old_name and File.exists?(file_path(@old_name))
|
153
|
+
FileUtils.mv(file_path(@old_name),file_path)
|
154
|
+
end
|
155
|
+
|
156
|
+
# def save_fields
|
157
|
+
# Rails.logger.warn "Fields: #{fields.inspect}"
|
158
|
+
# fields.each do |field|
|
159
|
+
# if field.should_destroy?
|
160
|
+
# Rails.logger.warn "Destroy Field: #{field.inspect}"
|
161
|
+
# field.delete
|
162
|
+
# else
|
163
|
+
# Rails.logger.warn "Save Field: #{field.inspect}"
|
164
|
+
# field.save!
|
165
|
+
# end
|
166
|
+
# end
|
167
|
+
# end
|
168
|
+
end
|
169
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
=begin rdoc
|
2
|
+
Author:: Chris Hauboldt (mailto:biz@lnstar.com)
|
3
|
+
Copyright:: 2009 Lone Star Internet Inc.
|
4
|
+
|
5
|
+
InlineAsset is a Newsletter::Field that allows either a url or an uploaded asset(image/document) to be used in a Newsletter.
|
6
|
+
|
7
|
+
=end
|
8
|
+
|
9
|
+
module Newsletter
|
10
|
+
class Field::InlineAsset < Field
|
11
|
+
has_many :assets, :foreign_key => :field_id,
|
12
|
+
:class_name => 'Newsletter::Asset'
|
13
|
+
|
14
|
+
attr_protected :id
|
15
|
+
|
16
|
+
# overridden from main class to choose between a Newsletter::Asset or a given URL
|
17
|
+
def value_for_piece(piece)
|
18
|
+
Value.new(:url => url_for_piece(piece), :text => get_value(piece,:text), :asset => asset(piece))
|
19
|
+
end
|
20
|
+
|
21
|
+
# overridden from main class to choose between a Newsletter::Asset or a given URL
|
22
|
+
def set_value_for_piece(piece,params)
|
23
|
+
if params[:url]
|
24
|
+
unless url_for_piece(piece).eql?(params[:url])
|
25
|
+
asset(piece).destroy if asset(piece)
|
26
|
+
set_value(piece,:url,params[:url])
|
27
|
+
end
|
28
|
+
end
|
29
|
+
set_value(piece,:text,params[:text]) if params[:text]
|
30
|
+
if params[:uploaded_data]
|
31
|
+
asset = asset(piece)
|
32
|
+
if asset && params[:asset_id] == asset.id.to_s
|
33
|
+
asset.image = params[:uploaded_data]
|
34
|
+
else
|
35
|
+
asset = assets.build
|
36
|
+
piece.assets << asset
|
37
|
+
asset.image = params[:uploaded_data]
|
38
|
+
end
|
39
|
+
asset.save
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# uniformly get URL so we can know whether it has been modified and delete
|
44
|
+
# any asset uploaded
|
45
|
+
def url_for_piece(piece)
|
46
|
+
return "#{::Newsletter.site_url}/#{asset(piece).public_filename}" unless asset(piece).try(:public_filename).nil?
|
47
|
+
get_value(piece,:url)
|
48
|
+
end
|
49
|
+
|
50
|
+
def asset(piece)
|
51
|
+
assets.by_piece(piece).first
|
52
|
+
end
|
53
|
+
|
54
|
+
# create nicer accessors for use in design since this is more than one value, unlike other
|
55
|
+
# fields
|
56
|
+
class Value
|
57
|
+
attr_accessor :url, :text, :asset, :asset_id
|
58
|
+
def initialize(params)
|
59
|
+
@url = params[:url]
|
60
|
+
@text = params[:text]
|
61
|
+
@asset = params[:asset]
|
62
|
+
@asset_id = @asset.nil? ? nil : @asset.id
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
=begin rdoc
|
2
|
+
Author:: Chris Hauboldt (mailto:biz@lnstar.com)
|
3
|
+
Copyright:: 2009 Lone Star Internet Inc.
|
4
|
+
|
5
|
+
Dumb text field, created so that we can choose for them to have text areas and text fields.
|
6
|
+
|
7
|
+
=end
|
8
|
+
|
9
|
+
module Newsletter
|
10
|
+
class Field::Text < Field
|
11
|
+
def keys
|
12
|
+
['text']
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
=begin rdoc
|
2
|
+
Author:: Chris Hauboldt (mailto:biz@lnstar.com)
|
3
|
+
Copyright:: 2009 Lone Star Internet Inc.
|
4
|
+
|
5
|
+
Dumb text area, created so that we can choose for them to have text areas and text fields.
|
6
|
+
|
7
|
+
=end
|
8
|
+
|
9
|
+
module Newsletter
|
10
|
+
class Field::TextArea < Field
|
11
|
+
def keys
|
12
|
+
['text_area']
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|