inkcite 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +20 -0
- data/README.md +110 -0
- data/Rakefile +8 -0
- data/assets/facebook-like.css +62 -0
- data/assets/facebook-like.js +59 -0
- data/assets/init/config.yml +97 -0
- data/assets/init/helpers.tsv +31 -0
- data/assets/init/source.html +60 -0
- data/assets/init/source.txt +6 -0
- data/bin/inkcite +6 -0
- data/inkcite.gemspec +42 -0
- data/lib/inkcite.rb +32 -0
- data/lib/inkcite/cli/base.rb +128 -0
- data/lib/inkcite/cli/build.rb +130 -0
- data/lib/inkcite/cli/init.rb +58 -0
- data/lib/inkcite/cli/preview.rb +30 -0
- data/lib/inkcite/cli/server.rb +123 -0
- data/lib/inkcite/cli/test.rb +61 -0
- data/lib/inkcite/email.rb +219 -0
- data/lib/inkcite/mailer.rb +140 -0
- data/lib/inkcite/minifier.rb +151 -0
- data/lib/inkcite/parser.rb +111 -0
- data/lib/inkcite/renderer.rb +177 -0
- data/lib/inkcite/renderer/base.rb +186 -0
- data/lib/inkcite/renderer/button.rb +168 -0
- data/lib/inkcite/renderer/div.rb +29 -0
- data/lib/inkcite/renderer/element.rb +82 -0
- data/lib/inkcite/renderer/footnote.rb +132 -0
- data/lib/inkcite/renderer/google_analytics.rb +35 -0
- data/lib/inkcite/renderer/image.rb +95 -0
- data/lib/inkcite/renderer/image_base.rb +82 -0
- data/lib/inkcite/renderer/in_browser.rb +38 -0
- data/lib/inkcite/renderer/like.rb +73 -0
- data/lib/inkcite/renderer/link.rb +243 -0
- data/lib/inkcite/renderer/litmus.rb +33 -0
- data/lib/inkcite/renderer/lorem.rb +39 -0
- data/lib/inkcite/renderer/mobile_image.rb +67 -0
- data/lib/inkcite/renderer/mobile_style.rb +40 -0
- data/lib/inkcite/renderer/mobile_toggle.rb +27 -0
- data/lib/inkcite/renderer/outlook_background.rb +48 -0
- data/lib/inkcite/renderer/partial.rb +31 -0
- data/lib/inkcite/renderer/preheader.rb +22 -0
- data/lib/inkcite/renderer/property.rb +39 -0
- data/lib/inkcite/renderer/responsive.rb +334 -0
- data/lib/inkcite/renderer/span.rb +21 -0
- data/lib/inkcite/renderer/table.rb +67 -0
- data/lib/inkcite/renderer/table_base.rb +149 -0
- data/lib/inkcite/renderer/td.rb +92 -0
- data/lib/inkcite/uploader.rb +173 -0
- data/lib/inkcite/util.rb +85 -0
- data/lib/inkcite/version.rb +3 -0
- data/lib/inkcite/view.rb +745 -0
- data/lib/inkcite/view/context.rb +38 -0
- data/lib/inkcite/view/media_query.rb +60 -0
- data/lib/inkcite/view/tag_stack.rb +38 -0
- data/test/email_spec.rb +16 -0
- data/test/parser_spec.rb +72 -0
- data/test/project/config.yml +98 -0
- data/test/project/helpers.tsv +56 -0
- data/test/project/images/inkcite.jpg +0 -0
- data/test/project/source.html +58 -0
- data/test/project/source.txt +6 -0
- data/test/renderer/button_spec.rb +45 -0
- data/test/renderer/div_spec.rb +101 -0
- data/test/renderer/element_spec.rb +31 -0
- data/test/renderer/footnote_spec.rb +57 -0
- data/test/renderer/image_spec.rb +82 -0
- data/test/renderer/link_spec.rb +84 -0
- data/test/renderer/mobile_image_spec.rb +27 -0
- data/test/renderer/mobile_style_spec.rb +37 -0
- data/test/renderer/td_spec.rb +126 -0
- data/test/renderer_spec.rb +28 -0
- data/test/view_spec.rb +15 -0
- metadata +333 -0
@@ -0,0 +1,95 @@
|
|
1
|
+
module Inkcite
|
2
|
+
module Renderer
|
3
|
+
class Image < ImageBase
|
4
|
+
|
5
|
+
def render tag, opt, ctx
|
6
|
+
|
7
|
+
img = Element.new('img', { :border => 0 })
|
8
|
+
|
9
|
+
# Ensure that height and width are defined in the image's attributes.
|
10
|
+
mix_dimensions img, opt
|
11
|
+
|
12
|
+
# Get the fully-qualified URL to the image or placeholder image if it's
|
13
|
+
# missing from the images directory.
|
14
|
+
img[:src] = image_url(opt[:src], opt, ctx)
|
15
|
+
|
16
|
+
mix_background img, opt
|
17
|
+
|
18
|
+
# Check to see if there is alt text specified for this image. We are
|
19
|
+
# testing against nil because sometimes the author desires an empty
|
20
|
+
# alt-text attribute.
|
21
|
+
alt = opt[:alt]
|
22
|
+
if alt
|
23
|
+
|
24
|
+
# Ensure that the alt-tag has quotes around it.
|
25
|
+
img[:alt] = quote(alt)
|
26
|
+
|
27
|
+
# The rest of this logic is only appropriate if the alt text
|
28
|
+
# is not blank.
|
29
|
+
unless alt.blank?
|
30
|
+
|
31
|
+
# Copy the text to the title attribute if enabled for this issue
|
32
|
+
img[:title] = img[:alt] if ctx.is_enabled?(COPY_ALT_TO_TITLE)
|
33
|
+
|
34
|
+
# All images with alt text inherit small font unless otherwise specified.
|
35
|
+
opt[:font] ||= 'small'
|
36
|
+
|
37
|
+
mix_font img, opt, ctx
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
# Images default to block display to prevent unexpected margins in Gmail
|
44
|
+
# http://www.campaignmonitor.com/blog/post/3132/how-to-stop-gmail-from-adding-a-margin-to-your-images/
|
45
|
+
display = opt[:display] || BLOCK
|
46
|
+
img.style[:display] = display unless display == DEFAULT
|
47
|
+
|
48
|
+
# True if the designer wants this image to flow inline. When true it
|
49
|
+
# vertically aligns the image with the text.
|
50
|
+
inline = (display == INLINE)
|
51
|
+
|
52
|
+
align = opt[:align] || ('absmiddle' if inline)
|
53
|
+
img[:align] = align unless align.blank?
|
54
|
+
|
55
|
+
valign = opt[:valign] || ('middle' if inline)
|
56
|
+
img.style[VERTICAL_ALIGN] = valign unless valign.blank?
|
57
|
+
|
58
|
+
mobile_src = opt[:'mobile-src']
|
59
|
+
unless mobile_src.blank?
|
60
|
+
|
61
|
+
# Get a unique CSS class name that will be used to swap in the alternate
|
62
|
+
# image on mobile.
|
63
|
+
klass = klass_name(mobile_src, ctx)
|
64
|
+
|
65
|
+
# Fully-qualify the image URL.
|
66
|
+
mobile_src = image_url(mobile_src, opt, ctx)
|
67
|
+
|
68
|
+
# Add a responsive rule that replaces the image with a different source
|
69
|
+
# with the same dimensions. Warning, this isn't supported on earlier
|
70
|
+
# versions of iOS 6 and Android 4.
|
71
|
+
# http://www.emailonacid.com/forum/viewthread/404/
|
72
|
+
ctx.media_query << img.add_rule(Rule.new(tag, klass, "content: url(#{mobile_src}) !important;"))
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
mobile = opt[:mobile]
|
77
|
+
|
78
|
+
# Check to see if this image is inside of a mobile-image declaration.
|
79
|
+
# If so, the image defaults to hide on mobile.
|
80
|
+
mobile = HIDE if mobile.blank? && !ctx.parent_opts(:mobile_image).blank?
|
81
|
+
|
82
|
+
mix_responsive img, opt, ctx, mobile
|
83
|
+
|
84
|
+
img.to_s
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
# Name of the property controlling whether or not the alt text should
|
90
|
+
# be copied to the title field.
|
91
|
+
COPY_ALT_TO_TITLE = :'copy-alt-to-title'
|
92
|
+
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module Inkcite
|
2
|
+
module Renderer
|
3
|
+
class ImageBase < Responsive
|
4
|
+
|
5
|
+
protected
|
6
|
+
|
7
|
+
# Display mode constants
|
8
|
+
BLOCK = 'block'
|
9
|
+
DEFAULT = 'default'
|
10
|
+
INLINE = 'inline'
|
11
|
+
|
12
|
+
def image_url _src, opt, ctx
|
13
|
+
|
14
|
+
src = _src
|
15
|
+
|
16
|
+
# True if dimensions are missing.
|
17
|
+
missing_dimensions = missing_dimensions?(opt)
|
18
|
+
|
19
|
+
# Fully-qualify the image path for this version of the email unless it
|
20
|
+
# is already includes a full address.
|
21
|
+
unless src.include?('://')
|
22
|
+
|
23
|
+
# Verify that the image exists.
|
24
|
+
if ctx.assert_image_exists(src) || ctx.is_disabled?(Inkcite::Email::IMAGE_PLACEHOLDERS)
|
25
|
+
|
26
|
+
if missing_dimensions
|
27
|
+
# TODO read the image dimensions from the file and auto-populate
|
28
|
+
# the width and height fields.
|
29
|
+
end
|
30
|
+
|
31
|
+
# Convert the source (e.g. "cover.jpg") into a fully-qualified reference
|
32
|
+
# to the image. In development this may be images/cover.jpg but in the
|
33
|
+
# other environments this would likely be a full URL to the image where it
|
34
|
+
# is being hosted.
|
35
|
+
src = ctx.image_url(src)
|
36
|
+
|
37
|
+
elsif DIMENSIONS.all? { |dim| opt[dim].to_i > MINIMUM_DIMENSION_FOR_PLACEHOLDER }
|
38
|
+
|
39
|
+
# As a convenience, replace missing images with placehold.it as long as they
|
40
|
+
# meet the minimum dimensions. No need to spam the design with tiny, tiny
|
41
|
+
# placeholders.
|
42
|
+
src = "http://placehold.it/#{opt[:width]}x#{opt[:height]}#{File.extname(src)}"
|
43
|
+
|
44
|
+
# Check to see if the designer specified FPO text for this placeholder -
|
45
|
+
# otherwise default to the dimensions of the image.
|
46
|
+
fpo = opt[:fpo]
|
47
|
+
src << "&text=#{URI::encode(fpo)}" unless fpo.blank?
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
# Don't let an image go into production without dimensions. Using the original
|
54
|
+
# src so that we don't display the verbose qualified URL to the developer.
|
55
|
+
ctx.error('Missing image dimensions', { :src => _src }) if missing_dimensions
|
56
|
+
|
57
|
+
quote(src)
|
58
|
+
end
|
59
|
+
|
60
|
+
def klass_name src, ctx
|
61
|
+
klass = "i%02d" % ctx.unique_id(:i)
|
62
|
+
end
|
63
|
+
|
64
|
+
def missing_dimensions? att
|
65
|
+
DIMENSIONS.any? { |dim| att[dim].to_i <= 0 }
|
66
|
+
end
|
67
|
+
|
68
|
+
def mix_dimensions img, opt
|
69
|
+
DIMENSIONS.each { |dim| img[dim] = opt[dim].to_i }
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
# Both the height and width of the image must exceed this amount in order
|
75
|
+
# to get a placehold.it automatically inserted. Otherwise only an error
|
76
|
+
# is raised for missing images.
|
77
|
+
MINIMUM_DIMENSION_FOR_PLACEHOLDER = 25
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
@@ -0,0 +1,38 @@
|
|
1
|
+
|
2
|
+
module Inkcite
|
3
|
+
module Renderer
|
4
|
+
class InBrowser < Base
|
5
|
+
|
6
|
+
def render tag, opt, ctx
|
7
|
+
|
8
|
+
# You can only view in-browser if we're viewing an email.
|
9
|
+
return nil unless ctx.email?
|
10
|
+
|
11
|
+
url = ctx[Inkcite::Email::VIEW_IN_BROWSER_URL]
|
12
|
+
return nil if url.blank?
|
13
|
+
|
14
|
+
browser_view = ctx.email.view(ctx.environment, :browser, ctx.version)
|
15
|
+
|
16
|
+
# Make sure we're converting any embedded values in the host URL
|
17
|
+
url = Renderer.render(url, browser_view)
|
18
|
+
|
19
|
+
# Optional link override color.
|
20
|
+
color = opt[:color]
|
21
|
+
|
22
|
+
# Optional call-to-action override - otherwise defaults to view in browser.
|
23
|
+
cta = opt[:cta] || 'View in Browser'
|
24
|
+
|
25
|
+
id = opt[:id] || 'in-browser'
|
26
|
+
|
27
|
+
html = "{a id=\"#{id}\" href=\"#{url}\""
|
28
|
+
html << " color=\"#{color}\"" unless color.blank?
|
29
|
+
html << '}'
|
30
|
+
html << cta
|
31
|
+
html << '{/a}'
|
32
|
+
|
33
|
+
html
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module Inkcite
|
2
|
+
module Renderer
|
3
|
+
class Like < Base
|
4
|
+
|
5
|
+
def render tag, opt, ctx
|
6
|
+
|
7
|
+
# Handle the case where we're building the hosted version of the email and
|
8
|
+
# JavaScript is used to trigger the Facebook like dialog.
|
9
|
+
if ctx.browser?
|
10
|
+
|
11
|
+
return '{/a}' if tag == '/like'
|
12
|
+
|
13
|
+
page = opt[:page]
|
14
|
+
if page.blank?
|
15
|
+
ctx.error("Like tag missing 'page' attribute")
|
16
|
+
|
17
|
+
else
|
18
|
+
|
19
|
+
brand = opt[:brand] || 'Us'
|
20
|
+
|
21
|
+
# Add an externally-hosted script to the context.
|
22
|
+
ctx.scripts << URI.parse('http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js')
|
23
|
+
|
24
|
+
# Add the java script to be embedded.
|
25
|
+
ctx.scripts << URI.parse('file://facebook-like.js')
|
26
|
+
|
27
|
+
# Add the Facebook like stylesheet.
|
28
|
+
ctx.styles << URI.parse('file://facebook-like.css')
|
29
|
+
|
30
|
+
ctx.footer << <<-eos
|
31
|
+
<div id="dialog-wrap">
|
32
|
+
<div id="dialog">
|
33
|
+
<h2>Like #{brand} on Facebook</h2>
|
34
|
+
<div id="dialog-content" class='loading'>
|
35
|
+
<div class="fb-like" data-href="http://www.facebook.com/#{page}" data-send="true" data-height="100" data-width="450" data-show-faces="true"></div>
|
36
|
+
</div>
|
37
|
+
<div id="dialog-buttons">
|
38
|
+
<a href=# onclick="return closeLike();"><span>Close</span></a>
|
39
|
+
</div>
|
40
|
+
</div>
|
41
|
+
</div>
|
42
|
+
|
43
|
+
<div id="fb-root"></div>
|
44
|
+
eos
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
'{a href=# onclick="return openLike();"}'
|
49
|
+
|
50
|
+
else
|
51
|
+
|
52
|
+
url = ctx[Inkcite::Email::VIEW_IN_BROWSER_URL]
|
53
|
+
unless url.blank?
|
54
|
+
|
55
|
+
return '{/a}' if tag == '/like'
|
56
|
+
|
57
|
+
# Otherwise, link to the hosted version of the email with the like hash tag
|
58
|
+
# to trigger like automatically on arrival.
|
59
|
+
href = Inkcite::Renderer.render(ctx[Inkcite::Email::VIEW_IN_BROWSER_URL] + '#like', ctx)
|
60
|
+
|
61
|
+
id = opt[:id]
|
62
|
+
|
63
|
+
"{a id=\"#{id}\" href=\"#{href}\"}"
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
@@ -0,0 +1,243 @@
|
|
1
|
+
module Inkcite
|
2
|
+
module Renderer
|
3
|
+
class Link < Responsive
|
4
|
+
|
5
|
+
def render tag, opt, ctx
|
6
|
+
|
7
|
+
tag_stack = ctx.tag_stack(:a)
|
8
|
+
|
9
|
+
if tag == '/a'
|
10
|
+
|
11
|
+
# Grab the attributes of the opening tag.
|
12
|
+
opening = tag_stack.pop
|
13
|
+
|
14
|
+
# Nothing to do in the
|
15
|
+
return '' if ctx.text?
|
16
|
+
|
17
|
+
html = '</a>'
|
18
|
+
|
19
|
+
# Check to see if the declaration has been marked as a block
|
20
|
+
# element and if so, close the div.
|
21
|
+
html << '</div>' if opening[:block]
|
22
|
+
|
23
|
+
return html
|
24
|
+
end
|
25
|
+
|
26
|
+
# Push the link's options onto the tag stack so that we can have
|
27
|
+
# access to its attributes when we close it.
|
28
|
+
tag_stack << opt
|
29
|
+
|
30
|
+
# Get the currently open table cell and see if link color is
|
31
|
+
# overridden in there.
|
32
|
+
td_parent = ctx.tag_stack(:td).opts
|
33
|
+
table_parent = ctx.tag_stack(:table).opts
|
34
|
+
|
35
|
+
# Choose a color from the parameters or inherit from the parent td, table or context.
|
36
|
+
opt[:color] = detect(opt[:color], td_parent[:link], table_parent[:link], ctx[LINK_COLOR])
|
37
|
+
|
38
|
+
a = Element.new('a')
|
39
|
+
|
40
|
+
mix_font a, opt, ctx
|
41
|
+
|
42
|
+
id = opt[:id]
|
43
|
+
href = opt[:href]
|
44
|
+
|
45
|
+
# True if the href is missing. If so, we may try to look it up by it's ID
|
46
|
+
# or we'll insert a default TBD link.
|
47
|
+
missing = href.blank?
|
48
|
+
|
49
|
+
# True if it's a link deeper into the content.
|
50
|
+
hash = !missing && href.starts_with?(POUND_SIGN)
|
51
|
+
|
52
|
+
# True if this is a mailto link.
|
53
|
+
mailto = !missing && !hash && href.starts_with?(MAILTO)
|
54
|
+
|
55
|
+
# Only perform special processing on the link if it's TBD or not a link to
|
56
|
+
# something in the page.
|
57
|
+
unless hash || mailto
|
58
|
+
|
59
|
+
if id.blank?
|
60
|
+
|
61
|
+
# Generate a placeholder ID and warn the user about it.
|
62
|
+
id = "link#{ctx.links.size + 1}"
|
63
|
+
ctx.error 'Link missing ID', { :href => href }
|
64
|
+
|
65
|
+
else
|
66
|
+
|
67
|
+
# Check to see if we've encountered an auto-incrementing link ID (e.g. event++)
|
68
|
+
# Replace the ++ with a unique count for this ID prefix.
|
69
|
+
id = id.gsub(PLUS_PLUS, ctx.unique_id(id).to_s) if id.end_with?(PLUS_PLUS)
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
# Get the HREF that we have previously encountered for this ID. When not blank
|
74
|
+
# we'll sanity check that the URL is the same.
|
75
|
+
last_href = ctx.links[id]
|
76
|
+
|
77
|
+
if missing
|
78
|
+
|
79
|
+
# If we don't have a URL, check to see if we've encountered this
|
80
|
+
href = last_href || ctx[MISSING_LINK_HREF]
|
81
|
+
|
82
|
+
ctx.error "Link missing href", { :id => id } unless last_href
|
83
|
+
|
84
|
+
else
|
85
|
+
|
86
|
+
# Optionally tag the link's query string for post-send log analytics.
|
87
|
+
href = add_tagging(id, href, ctx)
|
88
|
+
|
89
|
+
if last_href.blank?
|
90
|
+
|
91
|
+
# Associate the href with it's ID in case we bump into this link again.
|
92
|
+
ctx.links[id] = href
|
93
|
+
|
94
|
+
elsif last_href != href
|
95
|
+
|
96
|
+
# It saves everyone a lot of time if you alert them that an ID appears multiple times
|
97
|
+
# in the email and with mismatched URLs.
|
98
|
+
ctx.error "Link href mismatch", { :id => id, :expected => last_href, :found => href }
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
# Optionally replace the href with an ESP trackable url. Gotta do this after
|
105
|
+
# the link has been stored in the context because we don't want trackable
|
106
|
+
# URLs interfering with the links file.
|
107
|
+
href = add_tracking(id, href, ctx)
|
108
|
+
|
109
|
+
a[:target] = BLANK
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
# Make sure that these types of links have quotes.
|
114
|
+
href = quote(href) unless ctx.text?
|
115
|
+
|
116
|
+
# Set the href attribute to the resolved href.
|
117
|
+
a[:href] = href
|
118
|
+
|
119
|
+
# Links never get any text decoration.
|
120
|
+
a.style[TEXT_DECORATION] = NONE
|
121
|
+
|
122
|
+
if ctx.browser?
|
123
|
+
|
124
|
+
# Programmatically we can install onclick listeners for hosted versions.
|
125
|
+
# Check to see if one is specified and the Javascript is permitted in
|
126
|
+
# this version.
|
127
|
+
onclick = opt[:onclick]
|
128
|
+
a[:onclick] = quote(onclick) unless onclick.blank?
|
129
|
+
|
130
|
+
end
|
131
|
+
|
132
|
+
html = ''
|
133
|
+
|
134
|
+
if ctx.text?
|
135
|
+
html << a[:href]
|
136
|
+
|
137
|
+
else
|
138
|
+
|
139
|
+
klass = opt[:class]
|
140
|
+
a.classes << klass unless klass.blank?
|
141
|
+
|
142
|
+
mix_responsive a, opt, ctx
|
143
|
+
|
144
|
+
# Some responsive modes (e.g. button) change the display type from in-line
|
145
|
+
# to block. This change can cause unexpected whitespace or other unexpected
|
146
|
+
# layout changes. Outlook doesn't support block display on link elements
|
147
|
+
# so the best workaround is simply to wrap the element in <div> tags.
|
148
|
+
if a.responsive_styles.any?(&:block?)
|
149
|
+
html << '<div>'
|
150
|
+
|
151
|
+
# Remember that we made this element block-display so that we can append
|
152
|
+
# the extra div when we close the tag.
|
153
|
+
opt[:block] = true
|
154
|
+
|
155
|
+
end
|
156
|
+
|
157
|
+
html << a.to_s
|
158
|
+
|
159
|
+
end
|
160
|
+
|
161
|
+
html
|
162
|
+
end
|
163
|
+
|
164
|
+
private
|
165
|
+
|
166
|
+
# Property controlling where missing links are pointed.
|
167
|
+
MISSING_LINK_HREF = :'missing-link-url'
|
168
|
+
|
169
|
+
# The configuration name of the field that holds the query parameter that
|
170
|
+
# will be tacked onto the end of all links.
|
171
|
+
TAG_LINKS = :'tag-links'
|
172
|
+
|
173
|
+
# The configuration name of the field that holds the domain name(s) for
|
174
|
+
# links that will be tagged.
|
175
|
+
TAG_LINKS_DOMAIN = :'tag-links-domain'
|
176
|
+
|
177
|
+
# The property name used to indicate that links in this email should be
|
178
|
+
# replaced with [trackable URLs].
|
179
|
+
TRACK_LINKS = :'track-links'
|
180
|
+
|
181
|
+
# Value to open links in a new window.
|
182
|
+
BLANK = '_blank'
|
183
|
+
|
184
|
+
MAILTO = 'mailto:'
|
185
|
+
|
186
|
+
# Signifies an auto-incrementing link ID.
|
187
|
+
PLUS_PLUS = '++'
|
188
|
+
|
189
|
+
def add_tagging id, href, ctx
|
190
|
+
|
191
|
+
# Check to see if we're tagging links.
|
192
|
+
tag = ctx[TAG_LINKS]
|
193
|
+
unless tag.blank?
|
194
|
+
|
195
|
+
# Blank tag domain means tag all the links - otherwise, make sure the
|
196
|
+
# href matches the desired domain name.
|
197
|
+
tag_domain = ctx[TAG_LINKS_DOMAIN]
|
198
|
+
if tag_domain.blank? || href =~ /^https?:\/\/[^\/]*#{tag_domain}/
|
199
|
+
|
200
|
+
# Prepend it with a question mark or an ampersand depending on the current
|
201
|
+
# state of the lin.
|
202
|
+
stag = href.include?('?') ? '&' : '?'
|
203
|
+
stag << replace_tag(tag, id, ctx)
|
204
|
+
|
205
|
+
# Inject before the pound sign if present - otherwise, just tack it on
|
206
|
+
# to the end of the href.
|
207
|
+
if hash = href.index(POUND_SIGN)
|
208
|
+
href[hash..0] = stag
|
209
|
+
else
|
210
|
+
href << stag
|
211
|
+
end
|
212
|
+
|
213
|
+
end
|
214
|
+
|
215
|
+
end
|
216
|
+
|
217
|
+
href
|
218
|
+
end
|
219
|
+
|
220
|
+
def add_tracking id, href, ctx
|
221
|
+
|
222
|
+
# Check to see if a trackable link string has been defined.
|
223
|
+
tracking = ctx[Inkcite::Email::TRACK_LINKS]
|
224
|
+
|
225
|
+
# Replace the fully-qualified URL with a tracking tag - presuming that the
|
226
|
+
# ESP will replace this href with it's own trackable URL at deployment.
|
227
|
+
href = URI.encode(replace_tag(tracking, id, ctx)) unless tracking.blank?
|
228
|
+
|
229
|
+
href
|
230
|
+
end
|
231
|
+
|
232
|
+
def replace_tag tag, id, ctx
|
233
|
+
|
234
|
+
# Inject the link's ID into the tag - that's the only value that can't
|
235
|
+
# be resolved from the context.
|
236
|
+
tag = tag.gsub('{id}', id)
|
237
|
+
|
238
|
+
Inkcite::Renderer.render(tag, ctx)
|
239
|
+
end
|
240
|
+
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|