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.
Files changed (144) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +20 -0
  3. data/.ruby-gemset +1 -0
  4. data/.ruby-version +1 -0
  5. data/Gemfile +26 -0
  6. data/Gemfile.lock +199 -0
  7. data/Guardfile +24 -0
  8. data/LICENSE +22 -0
  9. data/MIT-LICENSE +20 -0
  10. data/Manifest.txt +220 -0
  11. data/Procfile +2 -0
  12. data/README +243 -0
  13. data/README.rdoc +3 -0
  14. data/Rakefile +29 -0
  15. data/app/assets/images/newsletter/.gitkeep +0 -0
  16. data/app/assets/javascripts/newsletter/application.js +15 -0
  17. data/app/assets/stylesheets/newsletter/application.css +13 -0
  18. data/app/controllers/newsletter/application_controller.rb +27 -0
  19. data/app/controllers/newsletter/areas_controller.rb +63 -0
  20. data/app/controllers/newsletter/designs_controller.rb +51 -0
  21. data/app/controllers/newsletter/elements_controller.rb +75 -0
  22. data/app/controllers/newsletter/fields_controller.rb +49 -0
  23. data/app/controllers/newsletter/newsletters_controller.rb +104 -0
  24. data/app/controllers/newsletter/pieces_controller.rb +72 -0
  25. data/app/helpers/newsletter/application_helper.rb +4 -0
  26. data/app/helpers/newsletter/areas_helper.rb +4 -0
  27. data/app/helpers/newsletter/assets_helper.rb +4 -0
  28. data/app/helpers/newsletter/designs_helper.rb +11 -0
  29. data/app/helpers/newsletter/elements_helper.rb +4 -0
  30. data/app/helpers/newsletter/field_values_helper.rb +4 -0
  31. data/app/helpers/newsletter/fields_helper.rb +4 -0
  32. data/app/helpers/newsletter/newsletters_helper.rb +43 -0
  33. data/app/helpers/newsletter/pieces_helper.rb +4 -0
  34. data/app/models/newsletter/area.rb +50 -0
  35. data/app/models/newsletter/asset.rb +31 -0
  36. data/app/models/newsletter/design.rb +160 -0
  37. data/app/models/newsletter/element.rb +169 -0
  38. data/app/models/newsletter/field/inline_asset.rb +67 -0
  39. data/app/models/newsletter/field/text.rb +15 -0
  40. data/app/models/newsletter/field/text_area.rb +15 -0
  41. data/app/models/newsletter/field.rb +115 -0
  42. data/app/models/newsletter/field_value.rb +20 -0
  43. data/app/models/newsletter/newsletter.rb +125 -0
  44. data/app/models/newsletter/piece.rb +96 -0
  45. data/app/models/newsletter.rb +11 -0
  46. data/app/uploaders/newsletter/asset_uploader.rb +51 -0
  47. data/app/views/layouts/newsletter/application.html.erb +135 -0
  48. data/app/views/newsletter/areas/_area.html.erb +49 -0
  49. data/app/views/newsletter/designs/_area_fields.html.erb +4 -0
  50. data/app/views/newsletter/designs/_form.html.erb +41 -0
  51. data/app/views/newsletter/designs/_newsletter_area.html.erb +17 -0
  52. data/app/views/newsletter/designs/edit.html.erb +14 -0
  53. data/app/views/newsletter/designs/index.html.erb +31 -0
  54. data/app/views/newsletter/designs/new.html.erb +9 -0
  55. data/app/views/newsletter/designs/show.html.erb +22 -0
  56. data/app/views/newsletter/elements/_field_fields.html.erb +27 -0
  57. data/app/views/newsletter/elements/_form.html.erb +51 -0
  58. data/app/views/newsletter/elements/_newsletter_field.html.erb +37 -0
  59. data/app/views/newsletter/elements/edit.html.erb +12 -0
  60. data/app/views/newsletter/elements/index.html.erb +32 -0
  61. data/app/views/newsletter/elements/new.html.erb +9 -0
  62. data/app/views/newsletter/elements/show.html.erb +22 -0
  63. data/app/views/newsletter/fields/_form.html.erb +15 -0
  64. data/app/views/newsletter/fields/_inline_asset.html.erb +36 -0
  65. data/app/views/newsletter/fields/_text.html.erb +4 -0
  66. data/app/views/newsletter/fields/_text_area.html.erb +7 -0
  67. data/app/views/newsletter/newsletters/_form.html.erb +21 -0
  68. data/app/views/newsletter/newsletters/_head.html.erb +48 -0
  69. data/app/views/newsletter/newsletters/_newsletter.html.erb +6 -0
  70. data/app/views/newsletter/newsletters/_newsletter_area.html.erb +30 -0
  71. data/app/views/newsletter/newsletters/archive.html.erb +9 -0
  72. data/app/views/newsletter/newsletters/edit.html.erb +37 -0
  73. data/app/views/newsletter/newsletters/index.html.erb +29 -0
  74. data/app/views/newsletter/newsletters/new.html.erb +9 -0
  75. data/app/views/newsletter/newsletters/show.html.erb +6 -0
  76. data/app/views/newsletter/pieces/_form.html.erb +19 -0
  77. data/app/views/newsletter/pieces/edit.html.erb +11 -0
  78. data/app/views/newsletter/pieces/new.html.erb +10 -0
  79. data/app/views/newsletter/pieces/show.html.erb +2 -0
  80. data/assets/docs/index.html +45 -0
  81. data/assets/docs/newsletter_templates/index.html +45 -0
  82. data/config/routes.rb +33 -0
  83. data/db/migrate/001_newsletter_initial.rb +104 -0
  84. data/db/migrate/002_carrier_wave_for_assets.rb +9 -0
  85. data/db/pre-scoped/002_newsletter_scoped.rb +61 -0
  86. data/doc/README_FOR_APP +2 -0
  87. data/doc/newsletter.wiki.txt +130 -0
  88. data/doc/start_rails_engine_with_rspec.txt +43 -0
  89. data/engine_plan.rb +13 -0
  90. data/features/step_definitions/webrat_steps.rb +115 -0
  91. data/features/support/env.rb +17 -0
  92. data/features/support/paths.rb +27 -0
  93. data/lib/deleteable.rb +50 -0
  94. data/lib/newsletter/config.rb +50 -0
  95. data/lib/newsletter/engine.rb +62 -0
  96. data/lib/newsletter/settings.rb +50 -0
  97. data/lib/newsletter/version.rb +3 -0
  98. data/lib/newsletter.rb +7 -0
  99. data/lib/tasks/newsletter.rake +84 -0
  100. data/lib/tasks/newsletter_tasks.rake +4 -0
  101. data/lib/tasks/rspec.rake +158 -0
  102. data/newsletter.gemspec +30 -0
  103. data/newsletters/exports/example-export.yaml +213 -0
  104. data/script/rails +8 -0
  105. data/spec/rcov.opts +2 -0
  106. data/spec/spec.opts +4 -0
  107. data/spec/spec_helper.rb +41 -0
  108. data/spec/test_app/README.rdoc +261 -0
  109. data/spec/test_app/Rakefile +7 -0
  110. data/spec/test_app/app/assets/javascripts/application.js +15 -0
  111. data/spec/test_app/app/assets/stylesheets/application.css +13 -0
  112. data/spec/test_app/app/controllers/application_controller.rb +3 -0
  113. data/spec/test_app/app/helpers/application_helper.rb +2 -0
  114. data/spec/test_app/app/mailers/.gitkeep +0 -0
  115. data/spec/test_app/app/models/.gitkeep +0 -0
  116. data/spec/test_app/app/views/layouts/application.html.erb +14 -0
  117. data/spec/test_app/config/application.rb +65 -0
  118. data/spec/test_app/config/boot.rb +10 -0
  119. data/spec/test_app/config/database.yml +25 -0
  120. data/spec/test_app/config/environment.rb +5 -0
  121. data/spec/test_app/config/environments/development.rb +37 -0
  122. data/spec/test_app/config/environments/production.rb +67 -0
  123. data/spec/test_app/config/environments/test.rb +37 -0
  124. data/spec/test_app/config/initializers/backtrace_silencers.rb +7 -0
  125. data/spec/test_app/config/initializers/inflections.rb +15 -0
  126. data/spec/test_app/config/initializers/mime_types.rb +5 -0
  127. data/spec/test_app/config/initializers/secret_token.rb +7 -0
  128. data/spec/test_app/config/initializers/session_store.rb +8 -0
  129. data/spec/test_app/config/initializers/wrap_parameters.rb +14 -0
  130. data/spec/test_app/config/locales/en.yml +5 -0
  131. data/spec/test_app/config/newsletter.yml +9 -0
  132. data/spec/test_app/config/routes.rb +4 -0
  133. data/spec/test_app/config.ru +4 -0
  134. data/spec/test_app/db/migrate/20131222171229_newsletter_initial.rb +104 -0
  135. data/spec/test_app/db/schema.rb +116 -0
  136. data/spec/test_app/lib/assets/.gitkeep +0 -0
  137. data/spec/test_app/log/.gitkeep +0 -0
  138. data/spec/test_app/public/404.html +26 -0
  139. data/spec/test_app/public/422.html +26 -0
  140. data/spec/test_app/public/500.html +25 -0
  141. data/spec/test_app/public/favicon.ico +0 -0
  142. data/spec/test_app/script/rails +6 -0
  143. data/zeus.json +22 -0
  144. metadata +352 -0
@@ -0,0 +1,115 @@
1
+ =begin rdoc
2
+ Author:: Chris Hauboldt (mailto:biz@lnstar.com)
3
+ Copyright:: 2009 Lone Star Internet Inc.
4
+
5
+ Defines the data behaviour of a Newsletter::Piece's fields. Tied to Newsletter::Pieces through Newsletter::Elements.
6
+
7
+ This is a base object and is never directly used, but is used to define different fields through Single Table Inheritance.
8
+
9
+ =end
10
+
11
+ module Newsletter
12
+ class Field < ActiveRecord::Base
13
+ self.table_name = "#{::Newsletter.table_prefix}fields"
14
+ belongs_to :element, :class_name => 'Newsletter::Element'
15
+ has_many :field_values, :class_name => 'Newsletter::FieldValue'
16
+ belongs_to :updated_by, :class_name => 'User'
17
+
18
+ validates_presence_of :name
19
+ =begin
20
+ FIXME: make this work with deletable or convert to auditable, and extend it to access destroyed records
21
+ validates_uniqueness_of :name, :scope => [:element_id], :unless => Proc.new { |field|
22
+ field.new_record? ? Field.find(:first,:conditions =>
23
+ ["name=? and element_id=? and deleted_at is null", field.element_id]).nil? :
24
+ Field.find(:first,:conditions =>
25
+ ["name=? and element_id=? and id!=? and deleted_at is null", field.element_id,
26
+ field.id]).nil? }
27
+ =end
28
+ attr_accessor :should_destroy
29
+
30
+ attr_protected :id
31
+
32
+ include Deleteable
33
+
34
+ # returns field data so that Newsletter::Design.export(instance) can export itself to a YAML file
35
+ def export_fields
36
+ {
37
+ :name => name,
38
+ :label => label,
39
+ :sequence => sequence,
40
+ :is_required => is_required,
41
+ :description => description,
42
+ :type => self[:type],
43
+ }
44
+ end
45
+
46
+ # returns field data so that Newsletter::Design.export(instance) can export itself to a YAML file
47
+ def self.import(element,data)
48
+ return unless data[:type]
49
+ field = data[:type].constantize.new(
50
+ :name => data[:name],
51
+ :label => data[:label],
52
+ :sequence => data[:sequence],
53
+ :is_required => data[:is_required],
54
+ :description => data[:description]
55
+ )
56
+ field.element = element
57
+ field.save
58
+ end
59
+
60
+ # returns a pieces value for a given Newsletter::Field
61
+ def value_for_piece(piece)
62
+ get_value(piece).to_s.html_safe
63
+ end
64
+
65
+ # sets a pieces value for a Newsletter::Field
66
+ def set_value_for_piece(piece,params)
67
+ params.each_pair do |key,value|
68
+ set_value(piece,key,value)
69
+ end
70
+ end
71
+
72
+ def set_value(piece,key,value)
73
+ field_value = field_values.by_piece(piece).by_key(key).first
74
+ if field_value
75
+ field_value.update_attribute(:value,value)
76
+ else
77
+ field_value = piece.field_values.build(:key => key,:value => value)
78
+ field_value.field_id = self.id
79
+ end
80
+ end
81
+
82
+ def get_value(piece,key=nil)
83
+ return field_values.by_piece(piece).by_key(key).first.try(:value) if key
84
+ field_values.by_piece(piece).first.try(:value)
85
+ end
86
+
87
+ # morph into a different Newsletter::Field type(if a user changes the type of a field)
88
+ def self.morph(field,type)
89
+ return field if field.class.name.eql?(type.to_s)
90
+ return type.constantize.new(field.attributes) if field.new_record?
91
+ field.update_attribute('type',type)
92
+ Field.find(field.id)
93
+ end
94
+
95
+ # used in figuring out whether to destroy a field as we are editing fields inside an element form
96
+ def should_destroy?
97
+ should_destroy.to_i == 1
98
+ end
99
+
100
+ # find the partial to display a form for creating a Newsletter::Piece
101
+ def form_partial_path
102
+ "newsletter/fields/#{type.to_s.gsub(/.*Newsletter::Field::/,'').gsub(/([^A-Z])([A-Z])/,'\\1_\\2').downcase}"
103
+ end
104
+
105
+ # helper for selecting type of field to use when creating/editing a Newsletter::Field.
106
+ def self.valid_types
107
+ @valid_types = ['Newsletter::Field::Text','Newsletter::Field::TextArea','Newsletter::Field::InlineAsset']
108
+ end
109
+ end
110
+ end
111
+
112
+ require 'newsletter/field/inline_asset'
113
+ require 'newsletter/field/text'
114
+ require 'newsletter/field/text_area'
115
+
@@ -0,0 +1,20 @@
1
+ =begin rdoc
2
+ Author:: Chris Hauboldt (mailto:biz@lnstar.com)
3
+ Copyright:: 2009 Lone Star Internet Inc.
4
+
5
+ Used to store key-value pairs for a NewsletterPiece's NewsletterFields
6
+
7
+ =end
8
+
9
+ module Newsletter
10
+ class FieldValue < ActiveRecord::Base
11
+ self.table_name = "#{::Newsletter.table_prefix}field_values"
12
+ belongs_to :piece, :class_name => 'Newsletter::Piece'
13
+ belongs_to :field, :class_name => 'Newsletter::Field'
14
+ scope :by_piece, lambda{|piece| where("piece_id IS NOT NULL AND piece_id=?",piece.try(:id))}
15
+ scope :by_field, lambda{|field| where("field_id IS NOT NULL AND field_id=?",field.try(:id))}
16
+ scope :by_key, lambda{|key| where(key: key)}
17
+
18
+ attr_protected :id
19
+ end
20
+ end
@@ -0,0 +1,125 @@
1
+ =begin rdoc
2
+ Author:: Chris Hauboldt (mailto:biz@lnstar.com)
3
+ Copyright:: 2009 Lone Star Internet Inc.
4
+
5
+ Newsletter ties user-added data to a Newsletter::Design through Newsletter::Pieces.
6
+
7
+ Newsletter also registers itself to be Mailable through the List Manager of Mailing if the plugin exists. (MLM isn't a nice term apparently) It is also the starting point for rendering a newsletter for both the web archive and when being sent as a mailable. A newsletter will show up on an archives page and be available to send in an email after it is "Published". Newsletters may also be Un-Published.
8
+
9
+ =end
10
+
11
+ module Newsletter
12
+ class Newsletter < ActiveRecord::Base
13
+ Rails.logger.info "Loading Newsletter Object"
14
+ self.table_name = "#{::Newsletter.table_prefix}newsletters"
15
+ belongs_to :design, :class_name => 'Newsletter::Design'
16
+ has_many :pieces, :order => 'sequence', :class_name => 'Newsletter::Piece',
17
+ :conditions => "#{::Newsletter.table_prefix}pieces.deleted_at is null"
18
+
19
+ scope :published, {:conditions => "#{::Newsletter.table_prefix}newsletters.published_at is not null",
20
+ :order => "#{::Newsletter.table_prefix}newsletters.created_at desc"}
21
+ scope :active, {:conditions => "#{::Newsletter.table_prefix}newsletters.deleted_at is null",
22
+ :order => "#{::Newsletter.table_prefix}newsletters.created_at desc"}
23
+
24
+ validates_presence_of :name
25
+
26
+ acts_as_list :column => :sequence
27
+
28
+ attr_protected :id
29
+
30
+ def headlines
31
+ pieces.select{|piece| piece.respond_to?(:headline)}
32
+ end
33
+
34
+ # returns the newsletter's content as plain text
35
+ def email_text
36
+ "Get the new Newsletter from here: " + public_url + "\n" +
37
+ '-'*30 + "\n\n" + generate_plain_text('email')
38
+ end
39
+
40
+ # returns the newsletter's content as html text with unsubscribe data(this should be encapsulated in an
41
+ # "if is_email" block in your design)
42
+ def email_html
43
+ generate('email')
44
+ end
45
+
46
+ # Currently using lynx to generate newsletter as text
47
+ def generate_plain_text(mode='')
48
+ IO.popen('lynx -stdin --dump','w+') do |lynx|
49
+ lynx.write generate(mode)
50
+ lynx.close_write
51
+ lynx.readlines.join
52
+ end
53
+ end
54
+
55
+ def published?
56
+ !published_at.nil?
57
+ end
58
+
59
+ # sets published_at to current time, so it will be shown in the archives page
60
+ def publish
61
+ update_attribute(:published_at, Time.now)
62
+ end
63
+
64
+ # clears published_at so that it is removed from the archives page
65
+ def unpublish
66
+ update_attribute(:published_at, nil)
67
+ end
68
+
69
+ # generates a public url for the newsletter
70
+ def public_url(mode='')
71
+ "#{::Newsletter.site_url}/newsletters/#{self[:id]}#{mode.blank? ? '' : "/#{mode}"}"
72
+ end
73
+
74
+ # used to generate the newsletter from a model or someplace other than the web stack
75
+ # FIXME: There has to be a better way, where railsy stuff works ... erb doesn't seem
76
+ # to be enough, so we're just using the web app
77
+ # Parameters:
78
+ # mode: if set to 'email', is_email? will be true as a helper in the designs, for
79
+ # useage to not include javascript, show/hide subscription links, etc.
80
+ # substitutions: data to substitute out of the content such as "UNSUBSCRIBE_URL"
81
+ def generate(mode='')
82
+ fetch(public_url(mode))
83
+ end
84
+
85
+ # retrieve a newsletter area by name
86
+ def area(name)
87
+ design.areas.by_name(name).first
88
+ end
89
+
90
+ # retrieve a list of locals to send to the main layout of the newsletter design view
91
+ # this is so that we can just "<%= area %>" without putting anything fancy in the view
92
+ def locals
93
+ variables = Hash.new
94
+ design.areas.each do |area|
95
+ variables[area.name.to_sym] = area
96
+ end
97
+ variables
98
+ end
99
+
100
+ def piece_attributes=(piece_attributes)
101
+ piece_attributes.each do |attributes|
102
+ pieces.build(attributes)
103
+ end
104
+ end
105
+
106
+ def areas
107
+ design.try(:areas).to_a
108
+ end
109
+
110
+ def fetch(uri_str, limit = 10)
111
+ # You should choose better exception.
112
+ Rails.logger.debug "Fetching #{uri_str}"
113
+ raise ArgumentError, 'HTTP redirect too deep' if limit == 0
114
+
115
+ response = Net::HTTP.get_response(URI.parse(uri_str))
116
+ case response
117
+ when Net::HTTPSuccess then response.body
118
+ when Net::HTTPRedirection then fetch(response['location'], limit - 1)
119
+ else
120
+ response.error!
121
+ end
122
+ end
123
+
124
+ end
125
+ end
@@ -0,0 +1,96 @@
1
+ =begin rdoc
2
+ Author:: Chris Hauboldt (mailto:biz@lnstar.com)
3
+ Copyright:: 2009 Lone Star Internet Inc.
4
+
5
+ NewsletterPieces are the glue that tie data together to form a piece in an area of a design.
6
+
7
+ =end
8
+
9
+ module Newsletter
10
+ class Piece < ActiveRecord::Base
11
+ self.table_name = "#{::Newsletter.table_prefix}pieces"
12
+ belongs_to :newsletter, :class_name => 'Newsletter::Newsletter'
13
+ belongs_to :area, :class_name => 'Newsletter::Area'
14
+ belongs_to :element, :class_name => 'Newsletter::Element'
15
+ # has_many :fields, :through => :element, :class_name => 'Newsletter::Field'
16
+ has_many :field_values, :class_name => 'Newsletter::FieldValue'
17
+ has_many :assets, :dependent => :destroy, :class_name => 'Newsletter::Asset'
18
+
19
+ acts_as_list :scope => :newsletter, :column => :sequence
20
+
21
+ scope :active, where(:deleted_at => nil)
22
+ scope :by_area, lambda {|area| {:conditions => {:area_id => area.id, :deleted_at => nil}}}
23
+ scope :by_newsletter, lambda {|newsletter| {:conditions => {:newsletter_id => newsletter.id, :deleted_at => nil}}}
24
+
25
+ # accepts_nested_attributes_for :field_values
26
+ attr_accessor :field_values_attributes
27
+
28
+ attr_protected :id
29
+
30
+ # returns locals to be used in its Newsletter::Element design
31
+ def locals
32
+ return @locals if @locals.present?
33
+ @locals = Hash.new
34
+ fields.each do |field|
35
+ @locals[field.name.to_sym] = field.value_for_piece(self)
36
+ define_getter_for_locals(field.name.to_sym) unless respond_to?(field.name.to_sym)
37
+ end
38
+ @locals
39
+ end
40
+
41
+ def fields
42
+ element.try(:fields).try(:uniq) || []
43
+ end
44
+
45
+ def newsletter
46
+ @newsletter ||= ::Newsletter::Newsletter.find(newsletter_id)
47
+ end
48
+
49
+ # used to save out a piece's fields, since they are inline in a piece's form
50
+ def field_values_attributes=(values)
51
+ @field_values_attributes = values
52
+ end
53
+
54
+ def set_field_values
55
+ return unless defined?(@field_values_attributes) && @field_values_attributes.present?
56
+ @field_values_attributes.each_pair do |field_id,key_value_pairs|
57
+ field = Field.find(field_id)
58
+ field.set_value_for_piece(self,key_value_pairs)
59
+ end
60
+ end
61
+
62
+ def respond_to?(my_method,use_private=false)
63
+ return true if super
64
+ if locals.keys.include?(my_method.to_sym)
65
+ define_getter_for_locals(my_method)
66
+ true
67
+ else
68
+ false
69
+ end
70
+ end
71
+
72
+ def method_missing(*args)
73
+ if args.length == 1 and locals.has_key?(args[0].to_sym)
74
+ define_getter_for_locals(args[0])
75
+ else
76
+ super
77
+ end
78
+ end
79
+
80
+ def save(*args)
81
+ transaction do
82
+ set_field_values
83
+ super
84
+ end
85
+ end
86
+
87
+ private
88
+ def define_getter_for_locals(key)
89
+ class_eval %Q|
90
+ def #{key}
91
+ locals[:#{key}]
92
+ end|
93
+ eval key.to_s
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,11 @@
1
+ module Newsletter
2
+ end
3
+
4
+ require 'newsletter/area'
5
+ require 'newsletter/asset'
6
+ require 'newsletter/element'
7
+ require 'newsletter/field'
8
+ require 'newsletter/field_value'
9
+ require 'newsletter/newsletter'
10
+ require 'newsletter/piece'
11
+ require 'newsletter/design'
@@ -0,0 +1,51 @@
1
+ # encoding: utf-8
2
+
3
+ class Newsletter::AssetUploader < CarrierWave::Uploader::Base
4
+
5
+ # Include RMagick or MiniMagick support:
6
+ # include CarrierWave::RMagick
7
+ # include CarrierWave::MiniMagick
8
+
9
+ # Choose what kind of storage to use for this uploader:
10
+ storage :file
11
+ # storage :fog
12
+
13
+ # Override the directory where uploaded files will be stored.
14
+ # This is a sensible default for uploaders that are meant to be mounted:
15
+ def store_dir
16
+ Newsletter::Asset.build_public_dirname(model.id)
17
+ end
18
+
19
+ # Provide a default URL as a default if there hasn't been a file uploaded:
20
+ # def default_url
21
+ # # For Rails 3.1+ asset pipeline compatibility:
22
+ # # ActionController::Base.helpers.asset_path("fallback/" + [version_name, "default.png"].compact.join('_'))
23
+ #
24
+ # "/images/fallback/" + [version_name, "default.png"].compact.join('_')
25
+ # end
26
+
27
+ # Process files as they are uploaded:
28
+ # process :scale => [200, 300]
29
+ #
30
+ # def scale(width, height)
31
+ # # do something
32
+ # end
33
+
34
+ # Create different versions of your uploaded files:
35
+ # version :thumb do
36
+ # process :scale => [50, 50]
37
+ # end
38
+
39
+ # Add a white list of extensions which are allowed to be uploaded.
40
+ # For images you might use something like this:
41
+ # def extension_white_list
42
+ # %w(jpg jpeg gif png)
43
+ # end
44
+
45
+ # Override the filename of the uploaded files:
46
+ # Avoid using model.id or version_name here, see uploader/store.rb for details.
47
+ # def filename
48
+ # "something.jpg" if original_filename
49
+ # end
50
+
51
+ end
@@ -0,0 +1,135 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <!-- iReach template -->
5
+ <title><%= h(yield(:title) || "iReach Newsletter Manager") %></title>
6
+ <link rel="stylesheet" href="https://code.jquery.com/ui/1.10.3/themes/le-frog/jquery-ui.css">
7
+ <script src="/javascripts/jquery.js" type="text/javascript"></script>
8
+ <script src="/javascripts/jquery_ujs.js" type="text/javascript"></script>
9
+ <script src="/javascripts/jquery-ui-1.10.3.custom.js" type="text/javascript"></script>
10
+ <%= csrf_meta_tags %>
11
+
12
+ <link href="/stylesheets/nav.css" media="screen" rel="stylesheet" type="text/css" />
13
+ <link href="/stylesheets/admin.css" media="screen" rel="stylesheet" type="text/css" />
14
+
15
+ <% begin %>
16
+ <% if admin_nav_menu.present? %>
17
+ <script>
18
+ function showMenu(id, id2) {
19
+ <% for menuitem in admin_nav_menu %>
20
+ <% if menuitem.admin_menu && menuitem.admin_menu.length > 0 && menuitem.is_visible %>
21
+ elem = document.getElementById("menu_<%= menuitem.id %>")
22
+ if (elem) elem.style.display = "none";
23
+ <% for submenuitem in menuitem.admin_menu.order("menu_order ASC") %>
24
+ <% if submenuitem.admin_menu && submenuitem.admin_menu.length > 0 && submenuitem.is_visible %>
25
+ elem = document.getElementById("menu_<%= submenuitem.id %>")
26
+ if (elem) elem.style.display = "none";
27
+ <% end %>
28
+ <% end %>
29
+ <% end %>
30
+ <% end %>
31
+ document.getElementById("menu_"+id).style.display = "inline";
32
+ if (id2) document.getElementById("menu_"+id2).style.display = "inline";
33
+ }
34
+ </script>
35
+ <% end %>
36
+ <% rescue => e %>
37
+ <!-- Error building JS Menu <%= e.message %> <%= e.backtrace.join("\n") %> -->
38
+ <% end %>
39
+
40
+ <%= content_for :head %>
41
+ </head>
42
+ <body>
43
+
44
+
45
+
46
+ <table width="100%" border="0" cellspacing="0" cellpadding="0">
47
+ <tr>
48
+ <td><img src="/images/1topLeft.gif" width="269" height="85" /></td>
49
+ <td width="100%" align="left" background="/images/topMid.gif"><img src="/images/1topMidR.gif" width="435" height="85" /></td>
50
+ <td align="right"><img src="/images/1topRight.gif" width="62" height="85" /></td>
51
+ </tr>
52
+ </table>
53
+
54
+
55
+ <table width="100%" border="0" cellspacing="0" cellpadding="0">
56
+
57
+ <tr>
58
+ <td valign="top" bgcolor="#FFFFFF">
59
+ <table border="0" cellspacing="0" cellpadding="0" class="menu">
60
+ <tr>
61
+ <td valign="top"><img src="/images/menuMainLeft.gif" width="7" height="257" /></td>
62
+ <td valign="top" class="table-bk">
63
+ <!-- Start Admin Menu <%= @admin_nav_menu.inspect %> -->
64
+ <% begin %>
65
+ <% if admin_nav_menu.present? %>
66
+ <% for menuitem in admin_nav_menu %>
67
+ <% if menuitem.admin_menu && menuitem.admin_menu.length > 0 && menuitem.is_visible && menuitem.authorized(session[:admin][:user_id]) %>
68
+ <span class="category" onClick="javascript:showMenu(<%= menuitem.id %>, 0)" style="cursor:pointer"><%= menuitem.description %></span><br/>
69
+ <div id="menu_<%= menuitem.id %>" style="display:<%= (params[:menu_id].to_i == menuitem.id) ? 'inline' : 'none' %>">
70
+ <% for submenuitem in menuitem.admin_menu %>
71
+ <% if submenuitem.is_visible and submenuitem.authorized(session[:admin][:user_id]) %>
72
+ <% if submenuitem.admin_menu && submenuitem.admin_menu.length > 0 && submenuitem.is_visible && submenuitem.authorized(session[:admin][:user_id]) %>
73
+ &nbsp;&nbsp;&nbsp;<span class="menuSM" onClick="javascript:showMenu(<%= menuitem.id %>, <%= submenuitem.id %>)" style="cursor:pointer"><%= submenuitem.description %></span><br/>
74
+ <div id="menu_<%= submenuitem.id %>" style="display:<%= (params[:submenu_id].to_i == submenuitem.id) ? 'inline' : 'none' %>">
75
+ <% for subsubmenuitem in submenuitem.admin_menu %>
76
+ <% if subsubmenuitem.is_visible and subsubmenuitem.authorized(session[:admin][:user_id]) %>
77
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="menuSM"><%= link_to menuitem.description, "/#{submenuitem.path}?menu_id=#{submenuitem.id}" %></span><br/>
78
+ <% end %>
79
+ <% end %>
80
+ </div>
81
+ <% elsif submenuitem.is_visible and submenuitem.authorized(session[:admin][:user_id]) %>
82
+ &nbsp;&nbsp;&nbsp;<span class="menuSM"><%= link_to submenuitem.description, "/#{submenuitem.path}?menu_id=#{menuitem.id}" %></span><br/>
83
+ <% end %>
84
+ <% end %>
85
+ <% end %>
86
+ </div>
87
+ <hr class="line" />
88
+ <% elsif menuitem.is_visible and menuitem.authorized(session[:admin][:user_id]) %>
89
+ <%= link_to menuitem.description, "/#{menuitem.path}" %><br/>
90
+ <hr class="line" />
91
+ <% end %>
92
+ <% end %>
93
+ <% end %>
94
+ <% rescue => e %>
95
+ <!-- Error building Menu <%= e.message %> <%= e.backtrace.join("\n") %> -->
96
+ <% end %>
97
+ </td>
98
+ </tr>
99
+ <tr>
100
+ <td><img src="/images/menuBotLeft.gif" width="7" height="8" /></td>
101
+ <td><img src="/images/menuBot.gif" width="176" height="8" /></td></tr>
102
+ </table>
103
+
104
+
105
+
106
+
107
+ </td>
108
+ <td width="100%" valign="top" bgcolor="#FFFFFF" class="main">
109
+
110
+ <% flash.each do |name, msg| -%>
111
+ <%= content_tag :div, msg, :id => "flash_#{name}" %>
112
+ <% end -%>
113
+
114
+ <% if show_title? -%>
115
+ <h1><%=h yield(:title) %></h1>
116
+ <% end -%>
117
+
118
+ <%= yield %>
119
+
120
+ <%= yield :page_navigation %>
121
+ </td>
122
+
123
+ <td background="/images/1midRight.gif"><img src="/images/1midRight.gif" width="62" height="19"/></td>
124
+ </tr>
125
+ </table>
126
+
127
+ <table width="100%" border="0" cellspacing="0" cellpadding="0">
128
+ <tr>
129
+ <td width="100%" background="/images/1botMid.gif"><img src="/images/1botMid.gif" width="13" height="39" /></td>
130
+ <td align="right"><img src="/images/1botRight.gif" width="62" height="39" /></td>
131
+ </tr>
132
+ </table>
133
+
134
+ </body>
135
+ </html>
@@ -0,0 +1,49 @@
1
+ <% unless params[:editor] %>
2
+ <% area.pieces.by_newsletter(@newsletter).each do |piece| %>
3
+ <% begin %>
4
+ <%= render :inline => piece.element.html_text, :locals => piece.locals %>
5
+ <% rescue => e %>
6
+ <!-- Error rendering piece: <%= piece.element.html_text %>/ <%= piece.id %> with data <%=h piece.locals.inspect %> and template: <%=h piece.element.html_text %> <br/> <%= e.message %><br/> <%= e.backtrace.join(",") %> -->
7
+ <% end %>
8
+ <% end %>
9
+ <% else %>
10
+ <%= div_for area do %>
11
+ <% area.pieces.by_newsletter(@newsletter).each do |piece| %>
12
+ <%= div_for piece, {:onMouseOver => "\$('#modify_piece_#{piece.id}').show()",
13
+ :onMouseOut => "\$('#modify_piece_#{piece.id}').hide()",
14
+ :style => "position: relative;"} do %>
15
+ <!--Element Template - <%= piece.element.view_path %> -->
16
+ <%= content_tag :div, link_to("Delete", piece, :confirm => 'Are you sure?',
17
+ :method => :delete, :class=> "delete_link") +
18
+ " | " +
19
+ link_to("Edit", edit_piece_path(piece), :target => '_top', :class=> "edit_link"),
20
+ :id=>"modify_piece_#{piece.id}",:style => "display:none", :class => "piece_tool" %>
21
+ <% begin %>
22
+ <%= render :inline => piece.element.html_text, :locals => piece.locals %>
23
+ <% rescue => e %>
24
+ Error rendering piece: <%= piece.element.html_text %>/ <%= piece.id %> with data <%=h piece.locals.inspect %> and template: <%=h piece.element.html_text %> <br/> <%= e.message %><br/> <%= e.backtrace.join(",") %>
25
+ <% end %>
26
+ <% end %>
27
+ <% end %>
28
+ <script>
29
+ jQuery(document).ready(function() {
30
+ jQuery("#area_<%= area.id %>").sortable({
31
+ axis: 'y',
32
+ stop: function (event, ui) {
33
+ var data = $(this).sortable('serialize');
34
+
35
+ // POST to server using $.post or $.ajax
36
+ $.ajax({
37
+ data: data,
38
+ type: 'POST',
39
+ url: '<%= sort_area_path(newsletter_id: @newsletter.id, id: area.id) %>'
40
+ });
41
+ }
42
+ });
43
+ });
44
+ </script>
45
+
46
+ <!--%= sortable_element("area_#{area.id}", :tag=>:div,
47
+ :constraint => :vertical, :url => sort_area_path(:id=>area.id,:newsletter_id=>@newsletter.id)) %-->
48
+ <% end %>
49
+ <% end %>
@@ -0,0 +1,4 @@
1
+ <%= div_for f.object do %>
2
+ <%= f.text_field :name %>
3
+ <%= f.link_to_remove "Remove", {class: 'button'} %><div class='linkbuttonEnding'></div>
4
+ <% end %>
@@ -0,0 +1,41 @@
1
+ <%= nested_form_for @design do |f| %>
2
+ <%= f.error_messages %>
3
+ <table cellspacing='1' cellpadding='0' border='0'>
4
+ <tr>
5
+ <td width='150'><%= f.label :name %></td>
6
+ <td><%= f.text_field :name %></td>
7
+ </tr>
8
+ <tr>
9
+ <td width='150'><%= f.label :description %></td>
10
+ <td><%= f.text_field :description %></td>
11
+ </tr>
12
+ <% unless @design.name.blank? %>
13
+ <tr>
14
+ <td width='150'><label>Design Filename</label></td>
15
+ <td><%= @design.view_path.gsub(/^#{::Newsletter.designs_path}/,'') %></td>
16
+ </tr>
17
+ <% end %>
18
+ <tr valign='top'>
19
+ <td width='150'><%= f.label :html_text %></td>
20
+ <td><%= f.text_area :html_text %></td>
21
+ </tr>
22
+ <tr valign='top'>
23
+ <td colspan='2'>
24
+ <div class='newsletter_parts_group'>
25
+ <h2 style='padding-bottom: 0; margin-bottom: 0; font-size: 10pt; '>Areas</h2>
26
+ <div id="areas">
27
+ <%= f.fields_for :areas %>
28
+ </div>
29
+ <%= f.link_to_add('Add Area', :areas, {class: 'button'}) %><div class='linkbuttonEnding'></div>
30
+ </div>
31
+ </td>
32
+ </tr>
33
+ <tr>
34
+ <td align="right" colspan="2"><%= f.submit "Submit", :class => 'button' %><div class="buttonEnding"></div></td>
35
+ </tr>
36
+ </table>
37
+
38
+ <% end %>
39
+ <% content_for :head do %>
40
+ <%= javascript_include_tag '/javascripts/nested_form.js' %>
41
+ <% end %>
@@ -0,0 +1,17 @@
1
+ <% div_for area do %>
2
+ <% fields_for 'design[area_attributes][]', area do |naf| %>
3
+ <%= naf.text_field :name, :index => nil %>
4
+ <% if area.new_record? %>
5
+ <%= link_to_function "Remove", "$(this).up('.area').remove()", :class => 'button' %><div class='linkbuttonEnding' style='margin-top: 7px;'></div><br/>
6
+ <% else %>
7
+ <%= link_to_function "Remove", "$(this).next('.should_destroy').value = 1; $(this).hide();"+
8
+ "$('area_restore_#{area.id}').show()", :class => 'button',
9
+ :id => "area_remove_#{area.id}" %>
10
+ <%= link_to_function "Restore", "$(this).next('.should_destroy').value = 0; $(this).hide();"+
11
+ "$('area_remove_#{area.id}').show()", :class => 'button',
12
+ :id => "area_restore_#{area.id}", :style => 'display:none' %><div class='linkbuttonEnding' style='margin-top: 5px;'></div><br/>
13
+ <%= naf.hidden_field :id, :index => nil %>
14
+ <%= naf.hidden_field :should_destroy, :index => nil, :class => 'should_destroy' %>
15
+ <% end %>
16
+ <% end %>
17
+ <% end %>