Skip to content

Commit 39461a1

Browse files
committed
add avatar download rb plugin to download images
1 parent 9f8bede commit 39461a1

38 files changed

+269
-6
lines changed

_src/_includes/themes/apache/partials/_head.html

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,4 @@
4949

5050
<!-- favicons -->
5151
<link rel="shortcut icon" href="/assets/img/favicon.png">
52-
53-
{% if page.path contains "community" %}
54-
<!-- allow github profile images, only in community page -->
55-
<meta http-equiv="Content-Security-Policy" content="img-src 'self' data: blob: https://avatars.githubusercontent.com/;">
56-
{% endif %}
5752
</head>

_src/_plugins/avatar_downloader.rb

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
# Avatar Downloader Plugin for Jekyll
2+
# ==================================
3+
#
4+
# This plugin automates the process of downloading GitHub avatar images for
5+
# contributors listed in the site's data file. It downloads the images during
6+
# the Jekyll build process and stores them in the assets/img/avatars directory.
7+
#
8+
# Process Flow:
9+
# ------------
10+
# ```
11+
# +-----------------+ +------------------+ +-----------------------+
12+
# | Read contributor| | For each | | Check if avatar exists|
13+
# | data from |--->| contributor with |--->|or check ETag/Modified |
14+
# | site.data | | GitHub ID | | headers for changes |
15+
# +-----------------+ +------------------+ +-----------------------+
16+
# |
17+
# v
18+
# +-----------------+ +------------------+ +-----------------------+
19+
# | Update site | | Save avatar | | Download avatar only |
20+
# | configuration |<---| to assets/img/ |<---| if changed or new |
21+
# | with avatar path| | avatars | | (using HTTP headers) |
22+
# +-----------------+ +------------------+ +-----------------------+
23+
# ```
24+
#
25+
# Benefits:
26+
# --------
27+
# 1. No manual downloading of avatar images required
28+
# 2. Images stay fresh but are only downloaded when actually changed
29+
# 3. Images are included in the built site automatically
30+
# 4. Faster page loads since images are served from the same domain
31+
#
32+
# Usage:
33+
# ------
34+
# In templates: <img src="/assets/img/avatars/{{ github_username }}.jpg">
35+
# The plugin automatically runs during Jekyll build process.
36+
#
37+
# Configuration:
38+
# --------------
39+
# - Requires contributor data with 'githubId' field
40+
# - Uses HTTP ETag/Last-Modified headers to only download changed images
41+
42+
require 'net/http'
43+
require 'fileutils'
44+
require 'digest'
45+
require 'yaml'
46+
47+
module Jekyll
48+
class AvatarDownloader < Generator
49+
safe true
50+
priority :high
51+
52+
def generate(site)
53+
# Store avatars in the source assets directory so they're included in the build
54+
avatar_dir = File.join(site.source, 'assets', 'img', 'avatars')
55+
FileUtils.mkdir_p(avatar_dir) unless Dir.exist?(avatar_dir)
56+
57+
# Create metadata directory for ETag/Last-Modified storage
58+
metadata_dir = File.join(site.source, 'assets', 'img', 'avatars', '.metadata')
59+
FileUtils.mkdir_p(metadata_dir) unless Dir.exist?(metadata_dir)
60+
61+
# Load previous metadata if it exists
62+
metadata_file = File.join(metadata_dir, 'metadata.yml')
63+
avatar_metadata = {}
64+
if File.exist?(metadata_file)
65+
begin
66+
avatar_metadata = YAML.load_file(metadata_file) || {}
67+
rescue => e
68+
puts "Error loading avatar metadata: #{e.message}"
69+
avatar_metadata = {}
70+
end
71+
end
72+
73+
# Get contributors from site data
74+
contributors = site.data['contributors'] || []
75+
76+
# Download each avatar
77+
contributors.each do |contributor|
78+
next unless contributor['githubId']
79+
80+
github_id = contributor['githubId']
81+
avatar_url = "https://avatars.githubusercontent.com/#{github_id}"
82+
local_path = File.join(avatar_dir, "#{github_id}.jpg")
83+
84+
# Get stored ETag and Last-Modified values if they exist
85+
user_metadata = avatar_metadata[github_id] || {}
86+
stored_etag = user_metadata['etag']
87+
stored_last_modified = user_metadata['last_modified']
88+
89+
download_needed = false
90+
headers = {}
91+
92+
# Add conditional headers if we have previous values
93+
if stored_etag
94+
headers['If-None-Match'] = stored_etag
95+
end
96+
97+
if stored_last_modified
98+
headers['If-Modified-Since'] = stored_last_modified
99+
end
100+
101+
# Check if we need to download: file doesn't exist or we need to check if it's changed
102+
if !File.exist?(local_path) || stored_etag || stored_last_modified
103+
begin
104+
uri = URI(avatar_url)
105+
106+
# Make a HEAD request first to check headers
107+
Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
108+
request = Net::HTTP::Head.new(uri, headers)
109+
response = http.request(request)
110+
111+
case response.code
112+
when '200'
113+
# Resource changed or first download
114+
download_needed = true
115+
# Store new ETag and Last-Modified for future requests
116+
avatar_metadata[github_id] = {
117+
'etag' => response['ETag'],
118+
'last_modified' => response['Last-Modified']
119+
}
120+
when '304'
121+
# Resource not modified
122+
puts "Avatar for #{github_id} is up-to-date (HTTP 304)"
123+
download_needed = false
124+
else
125+
puts "Unexpected response for #{github_id}: #{response.code}"
126+
download_needed = false
127+
end
128+
end
129+
130+
# Only download if needed
131+
if download_needed
132+
puts "Downloading avatar for #{github_id}..."
133+
Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
134+
request = Net::HTTP::Get.new(uri)
135+
response = http.request(request)
136+
137+
if response.code == '200'
138+
File.open(local_path, 'wb') do |file|
139+
file.write(response.body)
140+
end
141+
puts "Avatar saved for #{github_id}"
142+
143+
# Update metadata
144+
avatar_metadata[github_id] = {
145+
'etag' => response['ETag'],
146+
'last_modified' => response['Last-Modified']
147+
}
148+
else
149+
puts "Failed to download avatar for #{github_id}: #{response.code}"
150+
end
151+
end
152+
end
153+
rescue => e
154+
puts "Error processing avatar for #{github_id}: #{e.message}"
155+
end
156+
end
157+
end
158+
159+
# Save metadata for next build
160+
File.open(metadata_file, 'w') do |file|
161+
file.write(avatar_metadata.to_yaml)
162+
end
163+
164+
# Add site.avatar_path for use in templates
165+
site.config['avatar_path'] = '/assets/img/avatars'
166+
end
167+
end
168+
end
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
---
2+
ae2015:
3+
etag: '"88e86f61b57d6d3109510ffbc34976bc694b34cb5fc661ec14f94ef023ec592b"'
4+
last_modified: Sat, 02 May 2015 15:01:36 GMT
5+
phaniarnab:
6+
etag: '"06fceca82aba23e83d6c661b3d09b1d81d918f0d83e66c8caad93c948873081b"'
7+
last_modified: Fri, 12 Jul 2024 21:06:09 GMT
8+
asurve:
9+
etag: '"503aeffe134d57975ee975d093338ee2a9268127ccbb9aca5d0b4c06cfa82bc6"'
10+
last_modified: Wed, 28 Oct 2015 17:41:20 GMT
11+
BACtaki:
12+
etag: '"cd95e29deefa0ff63c67b6a1247f90c0f5a9ef3899e04ef376154332ef40d86a"'
13+
last_modified: Sat, 02 May 2015 15:01:36 GMT
14+
bertholdreinwald:
15+
etag: '"8642d7447dd2b2d365bc4acc72f86e4e7db0b902d7da04d40c86299f482e916b"'
16+
last_modified: Sun, 23 Oct 2016 05:37:40 GMT
17+
ywcb00:
18+
etag: '"0405526ff14518ab62d44a56637050ea1592906681464a48911b60bb98f15c22"'
19+
last_modified: Sat, 02 May 2015 15:01:36 GMT
20+
dbtsai:
21+
etag: '"09ff9dc128b12ce2afd5c732390bd0bdb9407fd2fdd9a65da1a2d95603811aa9"'
22+
last_modified: Tue, 10 Jun 2014 22:42:13 GMT
23+
deroneriksson:
24+
etag: '"70b78cfb1cd7a54c2ca8d23081ab84e6dc45a2c666b54a0f01eb4677cd5e8c28"'
25+
last_modified: Wed, 01 Jul 2015 18:42:34 GMT
26+
fmakari:
27+
etag: '"4451d0e84bed47fb139c508e1b5e4a08e051fe45439ae952b67bbdcb87cf0ff2"'
28+
last_modified: Fri, 28 Apr 2017 12:15:44 GMT
29+
fschueler:
30+
etag:
31+
last_modified: Tue, 24 Sep 2013 14:46:00 GMT
32+
frreiss:
33+
etag: '"1674414a6a0c7949af322ba32f28de49d5002a9ff3d6c9f9bc1b2839e2b16a96"'
34+
last_modified: Mon, 24 Jun 2024 20:14:27 GMT
35+
gweidner:
36+
etag: '"cf9f5a2d267c350f1ae7bf681dd20e7354e87ba6fcf7a0ce9f8609432d3bc81e"'
37+
last_modified: Wed, 30 Sep 2015 20:39:05 GMT
38+
EdgarLGB:
39+
etag: '"276581349793b598f5535fa6fe150546aeef0e97b805414cf13ce08953a49140"'
40+
last_modified: Mon, 13 Nov 2017 14:15:06 GMT
41+
hsaputra:
42+
etag: '"9a3fcfa4a9944c4c377376c84f325c3a5795776a6f07ecf05f908e6ab2833919"'
43+
last_modified: Mon, 02 Feb 2015 20:49:52 GMT
44+
holdenk:
45+
etag:
46+
last_modified: Tue, 04 Dec 2012 02:21:02 GMT
47+
j143:
48+
etag: '"56d0b022238cbd3a259721f3aa44bbfa40a45ee9e9e81ba279b1cc04867cff0b"'
49+
last_modified: Fri, 29 Oct 2021 18:24:34 GMT
50+
jkbradley:
51+
etag: '"3235eed8302c7ffd77fd3f32ec3963d193c9cd1fb227045a05894bd9c45a4e47"'
52+
last_modified: Sat, 02 May 2015 15:01:36 GMT
53+
kev-inn:
54+
etag: '"90c32cb4953463f0bb061dce46b4e319c4caf9d8baddb2829d2903da6ce4a4fe"'
55+
last_modified: Mon, 21 Nov 2022 14:52:57 GMT
56+
krishnakalyan3:
57+
etag: '"8d66a63baf5ace0ef349fe9f46d0a9341942c7eee1959b1d3591074ecbbf9a2e"'
58+
last_modified: Sat, 18 Jun 2022 23:27:02 GMT
59+
lresende:
60+
etag: '"ddb2be794e75e36a7003c98a8ba03e18e5ef909102f7ce9f4a3c4dcb23728f8e"'
61+
last_modified: Wed, 09 Dec 2020 04:19:15 GMT
62+
corepointer:
63+
etag: '"dec4d54e940328ea97ca7d39811a8da89ca662b2f9102b95016224563c1c18d3"'
64+
last_modified: Mon, 26 Jun 2023 14:33:55 GMT
65+
mboehm7:
66+
etag: '"b9ad82d205557c7a10af2ed4f2bbb2bc4d8e67b4d863c1cc1323cfb93670c343"'
67+
last_modified: Sun, 09 Aug 2015 20:58:15 GMT
68+
dusenberrymw:
69+
etag: '"8214346a3edb810790c12d4f4b26ca91300c37a7d40724acecf7d3500fa0960f"'
70+
last_modified: Tue, 02 Aug 2016 17:09:50 GMT
71+
nakul02:
72+
etag: '"3a4ee7985ef9fe366e0f3ca4af9d1e2f5bcaf9ebb2b247e1c05f29168fbee4a6"'
73+
last_modified: Fri, 08 Jan 2016 02:21:34 GMT
74+
niketanpansare:
75+
etag:
76+
last_modified: Sun, 15 Jun 2014 16:05:04 GMT
77+
OlgaOvcharenko:
78+
etag: '"870478e44146c49681f269297b5ec813271a55c836d06e7efb24b722a04b5fd6"'
79+
last_modified: Sun, 03 Jan 2021 18:31:42 GMT
80+
pwendell:
81+
etag: '"6ac4d173092d13c36578b3f5e4bed1d461a29113244c19d5e08729597d3a4c2e"'
82+
last_modified: Mon, 14 Sep 2015 21:46:02 GMT
83+
prithvirajsen:
84+
etag: '"e588c1ebcfa09f07e218f024425885c49078b44928e735ef577a04586357d0ae"'
85+
last_modified: Sat, 02 May 2015 15:01:36 GMT
86+
rxin:
87+
etag: '"3afacc3d9641230c1a0b4a0041e3022ef74c996bab569011d30cbb2e78bad917"'
88+
last_modified: Mon, 03 Mar 2025 01:28:38 GMT
89+
Baunsgaard:
90+
etag: '"7f11c27c7e27b0077e5eddfdc9358c1faea74525620d9902299d5d46ae2d5b8c"'
91+
last_modified: Sun, 05 Nov 2023 13:18:42 GMT
92+
shirisht:
93+
etag: '"952fbc91eeff8f58bfdb0cd068c82ed323c7bf8c8fdd1ae21848144d1b0fe225"'
94+
last_modified: Sat, 02 May 2015 15:01:36 GMT
95+
Shafaq-Siddiqi:
96+
etag: '"3e8f6474b0ddf35f090546871e2a462e39cacf4953ffdae996b916f2684f820b"'
97+
last_modified: Sat, 02 May 2015 15:01:36 GMT
98+
mengxr:
99+
etag: '"3024504bf3d26f3e5b0e9b9d7cd094dc23c19d7802aba428c8603e0d8deb526a"'
100+
last_modified: Mon, 16 Feb 2015 21:10:39 GMT
1.52 KB
Loading
22.6 KB
Loading
26.5 KB
Loading
13.5 KB
Loading
1.53 KB
Loading

_src/assets/img/avatars/ae2015.jpg

1.49 KB
Loading

_src/assets/img/avatars/asurve.jpg

19.5 KB
Loading

0 commit comments

Comments
 (0)