al_email_protect 1.0.0 → 1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 45eeb1de42070ef099e8c97dac39d19b382a24176bb366cae134999a44a8f563
4
- data.tar.gz: '08d20381439a1b9f8338fa52b62a784fa021b7a6a00823867379d01cec833631'
3
+ metadata.gz: eda48a119414b48f22f99423d978f6b4fbc0ffd48d8624825aaaa07b75349492
4
+ data.tar.gz: c604de4cc07165de773b748e2748a43e3350cba1d5c2051106702ef9578a7db5
5
5
  SHA512:
6
- metadata.gz: f035867e44a23b79ea54a50e63fbb6370a284d469fdeb35eabd95daa8b78324c4ce072bd20d2a1f3d1fbd8626f8700636e79256bd3024de7fa1613c9c0d12c9e
7
- data.tar.gz: cc611264e9d7fb0d5dcb0350b2cd93f50b38ae9bad99510715afb760b917b29bdabb880198e2a9291486d6f47b80353c1a6b4a3e93726f4146944cd0a25c566d
6
+ metadata.gz: 2d93ea976edc2dd4b057d2670c2ea46fe49714ee9aa820c337cd5db3ede3e0227ea5ed6edfa16720098ff938ec7be02a3609682ef8759e6be73a2485df4e8318
7
+ data.tar.gz: 0a660b1b7d1c21f2d1e0f58c5f4c999e7b06aa0ed38f0cf0e6c35b88a5daa2c371ec1e5ac921d3d01368ff87353d9c5e66ee886e8d045dcf169e34113fd30106
data/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.0.1 — 2026-08-03
4
+
5
+ - Added `al_email_protect_html`, a filter that rewrites `mailto:` anchors inside already-rendered HTML. al-folio's social
6
+ links come from the third-party `jekyll-socials` gem, which owns the whole block and offers no hook to render the email
7
+ entry differently — capturing its output and rewriting it afterwards is the only interception point, and it works
8
+ regardless of which gem produced the markup. Percent-encoded targets (as `encode_email` produces) are decoded first,
9
+ visible address text is replaced so the plaintext does not survive, non-`mailto:` links and unusable addresses are left
10
+ untouched, and the filter is a pass-through when the feature is off.
11
+
3
12
  ## 1.0.0 — 2026-08-02
4
13
 
5
14
  - Initial release of `al_email_protect`, implementing the email obfuscation proposed in
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AlEmailProtect
4
- VERSION = "1.0.0"
4
+ VERSION = "1.0.1"
5
5
  end
@@ -148,12 +148,65 @@ module AlEmailProtect
148
148
  end
149
149
 
150
150
  # {{ cv.email | al_email_obfuscate }}
151
+ # Rewrites every `mailto:` anchor in a fragment of already-rendered HTML.
152
+ #
153
+ # This exists because the markup that needs protecting is frequently not ours.
154
+ # al-folio's social links come from the third-party `jekyll-socials` gem, which
155
+ # owns the whole block and emits `mailto:%s` for the email entry — there is no
156
+ # hook to render that one link differently. Capturing its output and rewriting
157
+ # it afterwards is the only interception point, and it works regardless of
158
+ # which gem produced the markup.
159
+ #
160
+ # Deliberately narrow: it matches an <a> whose href begins with `mailto:` and
161
+ # replaces the whole element. It does not attempt to parse HTML generally.
162
+ ANCHOR = %r{<a\b([^>]*?)href=(["'])mailto:([^"']+)\2([^>]*)>(.*?)</a>}im.freeze
163
+
164
+ # `mailto:` targets are commonly percent-encoded (jekyll-email-protect's
165
+ # `encode_email` does exactly that), so decode before splitting or the local
166
+ # and domain halves come out as escape sequences.
167
+ def decode_target(value)
168
+ CGI.unescape(value.to_s.split("?").first.to_s)
169
+ rescue StandardError
170
+ value.to_s
171
+ end
172
+
173
+ def rewrite_html(html)
174
+ html.to_s.gsub(ANCHOR) do
175
+ before = Regexp.last_match(1)
176
+ after = Regexp.last_match(4)
177
+ address = decode_target(Regexp.last_match(3))
178
+ label = Regexp.last_match(5)
179
+ parts = split_address(address)
180
+
181
+ # Leave anything that is not a usable address exactly as it was; a
182
+ # half-rewritten contact link is worse than an unprotected one.
183
+ next Regexp.last_match(0) unless parts
184
+
185
+ local, domain = parts
186
+ attributes = "#{before}#{after}".gsub(/\s+/, " ").strip
187
+ # The visible text often *is* the address. Replace it with the split
188
+ # rendering, or the plaintext survives in the markup and the rewrite
189
+ # achieves nothing.
190
+ inner = label.to_s.include?("@") ? "#{escape(local)}<span class=\"al-email-sep\"> [at] </span>#{domain_markup(domain)}" : label
191
+
192
+ %(<a href="#" class="al-email-protect #{attributes}" data-eu="#{escape(local)}" data-ed="#{escape(domain)}">#{inner}</a>)
193
+ end
194
+ end
195
+
151
196
  module Filters
152
197
  def al_email_obfuscate(value)
153
198
  return value.to_s unless AlEmailProtect.enabled?(@context.registers[:site])
154
199
 
155
200
  AlEmailProtect.obfuscate_text(value)
156
201
  end
202
+
203
+ # `{{ captured_html | al_email_protect_html }}` — a pass-through when the
204
+ # feature is off, so callers can pipe unconditionally.
205
+ def al_email_protect_html(value)
206
+ return value.to_s unless AlEmailProtect.enabled?(@context.registers[:site])
207
+
208
+ AlEmailProtect.rewrite_html(value)
209
+ end
157
210
  end
158
211
  end
159
212
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: al_email_protect
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - al-folio maintainers
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-08-02 00:00:00.000000000 Z
11
+ date: 2026-08-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: jekyll