Skip to content

Commit 925a45f

Browse files
[MPIR-478] Allow to save avatars images for team report during project build
1 parent c80a10d commit 925a45f

File tree

8 files changed

+435
-51
lines changed

8 files changed

+435
-51
lines changed

pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ under the License.
2828
</parent>
2929

3030
<artifactId>maven-project-info-reports-plugin</artifactId>
31-
<version>3.8.1-SNAPSHOT</version>
31+
<version>3.9.0-SNAPSHOT</version>
3232
<packaging>maven-plugin</packaging>
3333

3434
<name>Apache Maven Project Info Reports Plugin</name>
@@ -123,7 +123,7 @@ under the License.
123123
<sitePluginVersion>3.20.0</sitePluginVersion>
124124
<fluidoSkinVersion>2.0.0-M11</fluidoSkinVersion>
125125
<checkstyle.violation.ignore>ParameterNumber,MethodLength</checkstyle.violation.ignore>
126-
<project.build.outputTimestamp>2024-10-18T09:42:43Z</project.build.outputTimestamp>
126+
<project.build.outputTimestamp>2025-02-19T22:31:07Z</project.build.outputTimestamp>
127127
</properties>
128128

129129
<dependencies>

src/it/team-local-avatars/pom.xml

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
~ Licensed to the Apache Software Foundation (ASF) under one
4+
~ or more contributor license agreements. See the NOTICE file
5+
~ distributed with this work for additional information
6+
~ regarding copyright ownership. The ASF licenses this file
7+
~ to you under the Apache License, Version 2.0 (the
8+
~ "License"); you may not use this file except in compliance
9+
~ with the License. You may obtain a copy of the License at
10+
~
11+
~ http://www.apache.org/licenses/LICENSE-2.0
12+
~
13+
~ Unless required by applicable law or agreed to in writing,
14+
~ software distributed under the License is distributed on an
15+
~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16+
~ KIND, either express or implied. See the License for the
17+
~ specific language governing permissions and limitations
18+
~ under the License.
19+
-->
20+
21+
<project xmlns="http://maven.apache.org/POM/4.0.0"
22+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
23+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
24+
<modelVersion>4.0.0</modelVersion>
25+
26+
<groupId>org.apache.maven.plugins.project-info-reports</groupId>
27+
<artifactId>team-local-avatars</artifactId>
28+
<version>1.0-SNAPSHOT</version>
29+
30+
<properties>
31+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
32+
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
33+
</properties>
34+
35+
<developers>
36+
<developer>
37+
<id>one-id</id>
38+
<name>one-name</name>
39+
<email>[email protected]</email>
40+
</developer>
41+
<developer>
42+
<id>two-id</id>
43+
<name>two-name</name>
44+
</developer>
45+
<developer>
46+
<id>three-id</id>
47+
<name>three-name</name>
48+
<email>[email protected]</email>
49+
</developer>
50+
</developers>
51+
52+
<build>
53+
<plugins>
54+
<plugin>
55+
<groupId>org.apache.maven.plugins</groupId>
56+
<artifactId>maven-site-plugin</artifactId>
57+
<version>@sitePluginVersion@</version>
58+
</plugin>
59+
</plugins>
60+
</build>
61+
62+
<reporting>
63+
<excludeDefaults>true</excludeDefaults>
64+
<plugins>
65+
<plugin>
66+
<groupId>org.apache.maven.plugins</groupId>
67+
<artifactId>maven-project-info-reports-plugin</artifactId>
68+
<version>@project.version@</version>
69+
<configuration>
70+
<externalAvatarImages>false</externalAvatarImages>
71+
</configuration>
72+
<reportSets>
73+
<reportSet>
74+
<reports>
75+
<report>team</report>
76+
</reports>
77+
</reportSet>
78+
</reportSets>
79+
</plugin>
80+
</plugins>
81+
</reporting>
82+
</project>
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
def team = new File( basedir, 'target/site/team.html' ).text
21+
22+
assert team.contains('<figure><img src="avatars/182138993c4de5ac312e843239b31267.jpg" /></figure>')
23+
assert team.contains('<figure><img src="avatars/00000000000000000000000000000000.jpg" /></figure>')
24+
assert team.contains('<figure><img src="avatars/0433bc9903da776c918d0c8e86d51073.jpg" /></figure>')
25+
26+
assert new File(basedir, 'target/site/avatars/182138993c4de5ac312e843239b31267.jpg').exists()
27+
assert new File(basedir, 'target/site/avatars/00000000000000000000000000000000.jpg').exists()
28+
assert new File(basedir, 'target/site/avatars/0433bc9903da776c918d0c8e86d51073.jpg').exists()

src/main/java/org/apache/maven/report/projectinfo/TeamReport.java

Lines changed: 77 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020

2121
import javax.inject.Inject;
2222

23-
import java.security.MessageDigest;
24-
import java.security.NoSuchAlgorithmException;
23+
import java.io.IOException;
24+
import java.io.UncheckedIOException;
2525
import java.util.ArrayList;
2626
import java.util.HashMap;
2727
import java.util.List;
@@ -36,6 +36,7 @@
3636
import org.apache.maven.plugins.annotations.Mojo;
3737
import org.apache.maven.plugins.annotations.Parameter;
3838
import org.apache.maven.project.ProjectBuilder;
39+
import org.apache.maven.report.projectinfo.avatars.AvatarsProvider;
3940
import org.apache.maven.reporting.MavenReportException;
4041
import org.apache.maven.repository.RepositorySystem;
4142
import org.codehaus.plexus.i18n.I18N;
@@ -52,19 +53,51 @@ public class TeamReport extends AbstractProjectInfoReport {
5253
/**
5354
* Shows avatar images for team members that have a) properties/picUrl set b) An avatar at gravatar.com for their
5455
* email address
55-
* <p/>
56-
* Future versions of this plugin may implement different strategies for resolving avatar images, possibly
57-
* using different providers.
58-
*<p>
59-
*<strong>Note</strong>: This property will be renamed to {@code tteam.showAvatarImages} in 3.0.
56+
*
6057
* @since 2.6
6158
*/
6259
@Parameter(property = "teamlist.showAvatarImages", defaultValue = "true")
6360
private boolean showAvatarImages;
6461

62+
/**
63+
* Indicate if URL should be used for avatar images.
64+
* <p>
65+
* If set to <code>false</code> images will be downloaded and attached to report during build.
66+
* Local path will be used for images.
67+
*
68+
* @since 3.9.0
69+
*/
70+
@Parameter(property = "teamlist.externalAvatarImages", defaultValue = "true")
71+
private boolean externalAvatarImages;
72+
73+
/**
74+
* Base URL for avatar provider.
75+
*
76+
* @since 3.9.0
77+
*/
78+
@Parameter(property = "teamlist.avatarBaseUrl", defaultValue = "https://www.gravatar.com/avatar/")
79+
private String avatarBaseUrl;
80+
81+
/**
82+
* Provider name for avatar images.
83+
* <p>
84+
* Report has one implementation for gravatar.com. Users can provide other by implementing {@link AvatarsProvider}.
85+
*
86+
* @since 3.9.0
87+
*/
88+
@Parameter(property = "teamlist.avatarProviderName", defaultValue = "gravatar")
89+
private String avatarProviderName;
90+
91+
private final Map<String, AvatarsProvider> avatarsProviders;
92+
6593
@Inject
66-
public TeamReport(RepositorySystem repositorySystem, I18N i18n, ProjectBuilder projectBuilder) {
94+
public TeamReport(
95+
RepositorySystem repositorySystem,
96+
I18N i18n,
97+
ProjectBuilder projectBuilder,
98+
Map<String, AvatarsProvider> avatarsProviders) {
6799
super(repositorySystem, i18n, projectBuilder);
100+
this.avatarsProviders = avatarsProviders;
68101
}
69102

70103
// ----------------------------------------------------------------------
@@ -83,9 +116,22 @@ public boolean canGenerateReport() throws MavenReportException {
83116
}
84117

85118
@Override
86-
public void executeReport(Locale locale) {
87-
ProjectTeamRenderer r =
88-
new ProjectTeamRenderer(getSink(), project.getModel(), getI18N(locale), locale, showAvatarImages);
119+
public void executeReport(Locale locale) throws MavenReportException {
120+
AvatarsProvider avatarsProvider = avatarsProviders.get(avatarProviderName);
121+
if (avatarsProvider == null) {
122+
throw new MavenReportException("No AvatarsProvider found for name " + avatarProviderName);
123+
}
124+
avatarsProvider.setBaseUrl(avatarBaseUrl);
125+
avatarsProvider.setOutputDirectory(getReportOutputDirectory());
126+
127+
ProjectTeamRenderer r = new ProjectTeamRenderer(
128+
getSink(),
129+
project.getModel(),
130+
getI18N(locale),
131+
locale,
132+
showAvatarImages,
133+
externalAvatarImages,
134+
avatarsProvider);
89135
r.render();
90136
}
91137

@@ -134,20 +180,24 @@ private static class ProjectTeamRenderer extends AbstractProjectInfoRenderer {
134180

135181
private final boolean showAvatarImages;
136182

137-
private final String protocol;
183+
private final boolean externalAvatarImages;
138184

139-
ProjectTeamRenderer(Sink sink, Model model, I18N i18n, Locale locale, boolean showAvatarImages) {
185+
private final AvatarsProvider avatarsProvider;
186+
187+
ProjectTeamRenderer(
188+
Sink sink,
189+
Model model,
190+
I18N i18n,
191+
Locale locale,
192+
boolean showAvatarImages,
193+
boolean externalAvatarImages,
194+
AvatarsProvider avatarsProvider) {
140195
super(sink, i18n, locale);
141196

142197
this.model = model;
143198
this.showAvatarImages = showAvatarImages;
144-
145-
// prepare protocol for gravatar
146-
if (model.getUrl() != null && model.getUrl().startsWith("https://")) {
147-
this.protocol = "https";
148-
} else {
149-
this.protocol = "http";
150-
}
199+
this.externalAvatarImages = externalAvatarImages;
200+
this.avatarsProvider = avatarsProvider;
151201
}
152202

153203
@Override
@@ -226,10 +276,7 @@ private void renderTeamMember(Contributor member, Map<String, Boolean> headersMa
226276
Properties properties = member.getProperties();
227277
String picUrl = properties.getProperty("picUrl");
228278
if (picUrl == null || picUrl.isEmpty()) {
229-
picUrl = getGravatarUrl(member.getEmail());
230-
}
231-
if (picUrl == null || picUrl.isEmpty()) {
232-
picUrl = getSpacerGravatarUrl();
279+
picUrl = getExternalAvatarUrl(member.getEmail());
233280
}
234281
sink.tableCell();
235282
sink.figure();
@@ -288,32 +335,13 @@ private void renderTeamMember(Contributor member, Map<String, Boolean> headersMa
288335
sink.tableRow_();
289336
}
290337

291-
private static final String AVATAR_SIZE = "s=60";
292-
293-
private String getSpacerGravatarUrl() {
294-
return protocol + "://www.gravatar.com/avatar/00000000000000000000000000000000?d=blank&f=y&" + AVATAR_SIZE;
295-
}
296-
297-
private String getGravatarUrl(String email) {
298-
if (email == null) {
299-
return null;
300-
}
301-
email = StringUtils.trim(email);
302-
email = email.toLowerCase();
303-
MessageDigest md;
338+
private String getExternalAvatarUrl(String email) {
304339
try {
305-
md = MessageDigest.getInstance("MD5");
306-
md.update(email.getBytes());
307-
byte[] byteData = md.digest();
308-
StringBuilder sb = new StringBuilder();
309-
final int lowerEightBitsOnly = 0xff;
310-
for (byte aByteData : byteData) {
311-
sb.append(Integer.toString((aByteData & lowerEightBitsOnly) + 0x100, 16)
312-
.substring(1));
313-
}
314-
return protocol + "://www.gravatar.com/avatar/" + sb.toString() + "?d=mm&" + AVATAR_SIZE;
315-
} catch (NoSuchAlgorithmException e) {
316-
return null;
340+
return externalAvatarImages
341+
? avatarsProvider.getExternalAvatarUrl(email)
342+
: avatarsProvider.getLocalAvatarPath(email);
343+
} catch (IOException e) {
344+
throw new UncheckedIOException(e);
317345
}
318346
}
319347

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.maven.report.projectinfo.avatars;
20+
21+
import java.io.File;
22+
import java.io.IOException;
23+
24+
/**
25+
* Avatar provider API.
26+
*/
27+
public interface AvatarsProvider {
28+
29+
/**
30+
* Set a base URL for provider
31+
*
32+
* @param baseUrl for provider
33+
*/
34+
void setBaseUrl(String baseUrl);
35+
36+
/**
37+
* Set site output directory. Used to store avatar images in project.
38+
*
39+
* @param outputDirectory a site output directory
40+
*/
41+
void setOutputDirectory(File outputDirectory);
42+
43+
/**
44+
* Return an external URL for gravatar image.
45+
*
46+
* @param email email address for gravatar image
47+
* @return an external URL
48+
*/
49+
String getExternalAvatarUrl(String email);
50+
51+
/**
52+
* Return a local path to downloaded avatar image.
53+
*
54+
* @param email email address for gravatar image
55+
* @return a local avatar path
56+
* @throws IOException if problem with image downloading
57+
*/
58+
String getLocalAvatarPath(String email) throws IOException;
59+
}

0 commit comments

Comments
 (0)