This is an automated email from the ASF dual-hosted git repository.

sjaranowski pushed a commit to branch master
in repository 
https://gitbox.apache.org/repos/asf/maven-project-info-reports-plugin.git


The following commit(s) were added to refs/heads/master by this push:
     new e51cef8  [MPIR-478] Allow to save avatars images for team report 
during project build
e51cef8 is described below

commit e51cef80148c7fc79d53325275589f9f41702ca8
Author: Slawomir Jaranowski <s.jaranow...@gmail.com>
AuthorDate: Thu Feb 20 22:49:37 2025 +0100

    [MPIR-478] Allow to save avatars images for team report during project build
---
 pom.xml                                            |   4 +-
 src/it/team-local-avatars/pom.xml                  |  82 ++++++++++
 .../team-local-avatars/verify.groovy}              |  18 +--
 .../maven/report/projectinfo/TeamReport.java       | 179 +++++++++++++--------
 .../projectinfo/avatars/AvatarsProvider.java       |  59 +++++++
 .../projectinfo/avatars/GravatarProvider.java      | 156 ++++++++++++++++++
 .../report/projectinfo/avatars/default-avatar.jpg  | Bin 0 -> 129 bytes
 .../maven/report/projectinfo/TeamReportTest.java   |   3 +
 .../projectinfo/avatars/GravatarProviderTest.java  |  92 +++++++++++
 .../maven/report/projectinfo/stubs/TeamStub.java   |  15 ++
 10 files changed, 524 insertions(+), 84 deletions(-)

diff --git a/pom.xml b/pom.xml
index 3f3732e..cd81f6e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -28,7 +28,7 @@ under the License.
   </parent>
 
   <artifactId>maven-project-info-reports-plugin</artifactId>
-  <version>3.8.1-SNAPSHOT</version>
+  <version>3.9.0-SNAPSHOT</version>
   <packaging>maven-plugin</packaging>
 
   <name>Apache Maven Project Info Reports Plugin</name>
@@ -123,7 +123,7 @@ under the License.
     <sitePluginVersion>3.21.0</sitePluginVersion>
     <fluidoSkinVersion>2.0.1</fluidoSkinVersion>
     
<checkstyle.violation.ignore>ParameterNumber,MethodLength</checkstyle.violation.ignore>
-    
<project.build.outputTimestamp>2024-10-18T09:42:43Z</project.build.outputTimestamp>
+    
<project.build.outputTimestamp>2025-02-19T22:31:07Z</project.build.outputTimestamp>
   </properties>
 
   <dependencies>
diff --git a/src/it/team-local-avatars/pom.xml 
b/src/it/team-local-avatars/pom.xml
new file mode 100644
index 0000000..3b068eb
--- /dev/null
+++ b/src/it/team-local-avatars/pom.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one
+  ~ or more contributor license agreements.  See the NOTICE file
+  ~ distributed with this work for additional information
+  ~ regarding copyright ownership.  The ASF licenses this file
+  ~ to you under the Apache License, Version 2.0 (the
+  ~ "License"); you may not use this file except in compliance
+  ~ with the License.  You may obtain a copy of the License at
+  ~
+  ~   http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing,
+  ~ software distributed under the License is distributed on an
+  ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  ~ KIND, either express or implied.  See the License for the
+  ~ specific language governing permissions and limitations
+  ~ under the License.
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0";
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+  <modelVersion>4.0.0</modelVersion>
+
+  <groupId>org.apache.maven.plugins.project-info-reports</groupId>
+  <artifactId>team-local-avatars</artifactId>
+  <version>1.0-SNAPSHOT</version>
+
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+  </properties>
+
+  <developers>
+    <developer>
+      <id>one-id</id>
+      <name>one-name</name>
+      <email>sjaranow...@apache.org</email>
+    </developer>
+    <developer>
+      <id>two-id</id>
+      <name>two-name</name>
+    </developer>
+    <developer>
+      <id>three-id</id>
+      <name>three-name</name>
+      <email>ema...@ecample.com</email>
+    </developer>
+  </developers>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-site-plugin</artifactId>
+        <version>@sitePluginVersion@</version>
+      </plugin>
+    </plugins>
+  </build>
+
+  <reporting>
+    <excludeDefaults>true</excludeDefaults>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-project-info-reports-plugin</artifactId>
+        <version>@project.version@</version>
+        <configuration>
+          <externalAvatarImages>false</externalAvatarImages>
+        </configuration>
+        <reportSets>
+          <reportSet>
+            <reports>
+              <report>team</report>
+            </reports>
+          </reportSet>
+        </reportSets>
+      </plugin>
+    </plugins>
+  </reporting>
+</project>
diff --git 
a/src/test/java/org/apache/maven/report/projectinfo/stubs/TeamStub.java 
b/src/it/team-local-avatars/verify.groovy
similarity index 64%
copy from src/test/java/org/apache/maven/report/projectinfo/stubs/TeamStub.java
copy to src/it/team-local-avatars/verify.groovy
index 63d0e7b..aabeac8 100644
--- a/src/test/java/org/apache/maven/report/projectinfo/stubs/TeamStub.java
+++ b/src/it/team-local-avatars/verify.groovy
@@ -16,15 +16,11 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.maven.report.projectinfo.stubs;
 
-/**
- * @author <a href="mailto:vincent.sive...@gmail.com";>Vincent Siveton</a>
- * @version $Id$
- */
-public class TeamStub extends ProjectInfoProjectStub {
-    @Override
-    protected String getPOM() {
-        return "team-plugin-config.xml";
-    }
-}
+def team = new File( basedir, 'target/site/team.html' ).text
+
+assert team.count('<figure><img 
src="avatars/90cc13b765c79d2d55ca64388ea2bc5f.jpg" /></figure>') == 1
+assert team.count('<figure><img 
src="avatars/00000000000000000000000000000000.jpg" /></figure>') == 2
+
+assert new File(basedir, 
'target/site/avatars/90cc13b765c79d2d55ca64388ea2bc5f.jpg').exists()
+assert new File(basedir, 
'target/site/avatars/00000000000000000000000000000000.jpg').exists()
diff --git a/src/main/java/org/apache/maven/report/projectinfo/TeamReport.java 
b/src/main/java/org/apache/maven/report/projectinfo/TeamReport.java
index 5fb6bda..3b93a1c 100644
--- a/src/main/java/org/apache/maven/report/projectinfo/TeamReport.java
+++ b/src/main/java/org/apache/maven/report/projectinfo/TeamReport.java
@@ -20,9 +20,9 @@ package org.apache.maven.report.projectinfo;
 
 import javax.inject.Inject;
 
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
+import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
@@ -32,10 +32,11 @@ import java.util.Properties;
 import org.apache.maven.doxia.sink.Sink;
 import org.apache.maven.model.Contributor;
 import org.apache.maven.model.Developer;
-import org.apache.maven.model.Model;
 import org.apache.maven.plugins.annotations.Mojo;
 import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.maven.project.MavenProject;
 import org.apache.maven.project.ProjectBuilder;
+import org.apache.maven.report.projectinfo.avatars.AvatarsProvider;
 import org.apache.maven.reporting.MavenReportException;
 import org.apache.maven.repository.RepositorySystem;
 import org.codehaus.plexus.i18n.I18N;
@@ -52,19 +53,51 @@ public class TeamReport extends AbstractProjectInfoReport {
     /**
      * Shows avatar images for team members that have a) properties/picUrl set 
b) An avatar at gravatar.com for their
      * email address
-     * <p/>
-     * Future versions of this plugin may implement different strategies for 
resolving avatar images, possibly
-     * using different providers.
-     *<p>
-     *<strong>Note</strong>: This property will be renamed to {@code 
tteam.showAvatarImages} in 3.0.
+     *
      * @since 2.6
      */
     @Parameter(property = "teamlist.showAvatarImages", defaultValue = "true")
     private boolean showAvatarImages;
 
+    /**
+     * Indicate if URL should be used for avatar images.
+     * <p>
+     * If set to <code>false</code> images will be downloaded and attached to 
report during build.
+     * Local path will be used for images.
+     *
+     * @since 3.9.0
+     */
+    @Parameter(property = "teamlist.externalAvatarImages", defaultValue = 
"true")
+    private boolean externalAvatarImages;
+
+    /**
+     * Base URL for avatar provider.
+     *
+     * @since 3.9.0
+     */
+    @Parameter(property = "teamlist.avatarBaseUrl", defaultValue = 
"https://www.gravatar.com/avatar/";)
+    private String avatarBaseUrl;
+
+    /**
+     * Provider name for avatar images.
+     * <p>
+     * Report has one implementation for gravatar.com. Users can provide other 
by implementing {@link AvatarsProvider}.
+     *
+     * @since 3.9.0
+     */
+    @Parameter(property = "teamlist.avatarProviderName", defaultValue = 
"gravatar")
+    private String avatarProviderName;
+
+    private final Map<String, AvatarsProvider> avatarsProviders;
+
     @Inject
-    public TeamReport(RepositorySystem repositorySystem, I18N i18n, 
ProjectBuilder projectBuilder) {
+    public TeamReport(
+            RepositorySystem repositorySystem,
+            I18N i18n,
+            ProjectBuilder projectBuilder,
+            Map<String, AvatarsProvider> avatarsProviders) {
         super(repositorySystem, i18n, projectBuilder);
+        this.avatarsProviders = avatarsProviders;
     }
 
     // ----------------------------------------------------------------------
@@ -83,10 +116,55 @@ public class TeamReport extends AbstractProjectInfoReport {
     }
 
     @Override
-    public void executeReport(Locale locale) {
-        ProjectTeamRenderer r =
-                new ProjectTeamRenderer(getSink(), project.getModel(), 
getI18N(locale), locale, showAvatarImages);
-        r.render();
+    public void executeReport(Locale locale) throws MavenReportException {
+
+        Map<Contributor, String> avatarImages = prepareAvatars();
+
+        ProjectTeamRenderer renderer =
+                new ProjectTeamRenderer(getSink(), project, getI18N(locale), 
locale, showAvatarImages, avatarImages);
+        renderer.render();
+    }
+
+    private Map<Contributor, String> prepareAvatars() throws 
MavenReportException {
+
+        if (!showAvatarImages) {
+            return Collections.emptyMap();
+        }
+
+        AvatarsProvider avatarsProvider = 
avatarsProviders.get(avatarProviderName);
+        if (avatarsProvider == null) {
+            throw new MavenReportException("No AvatarsProvider found for name 
" + avatarProviderName);
+        }
+        avatarsProvider.setBaseUrl(avatarBaseUrl);
+        avatarsProvider.setOutputDirectory(getReportOutputDirectory());
+
+        Map<Contributor, String> result = new HashMap<>();
+        try {
+            prepareContributorAvatars(result, avatarsProvider, 
project.getDevelopers());
+            prepareContributorAvatars(result, avatarsProvider, 
project.getContributors());
+        } catch (IOException e) {
+            throw new MavenReportException("Unable to load avatar images", e);
+        }
+        return result;
+    }
+
+    private void prepareContributorAvatars(
+            Map<Contributor, String> avatarImages,
+            AvatarsProvider avatarsProvider,
+            List<? extends Contributor> contributors)
+            throws IOException {
+
+        for (Contributor contributor : contributors) {
+
+            String picSource = 
contributor.getProperties().getProperty("picUrl");
+            if (picSource == null || picSource.isEmpty()) {
+                picSource = externalAvatarImages
+                        ? avatarsProvider.getAvatarUrl(contributor.getEmail())
+                        : 
avatarsProvider.getLocalAvatarPath(contributor.getEmail());
+            }
+
+            avatarImages.put(contributor, picSource);
+        }
     }
 
     /**
@@ -130,24 +208,24 @@ public class TeamReport extends AbstractProjectInfoReport 
{
 
         private static final String ID = "id";
 
-        private final Model model;
+        private final MavenProject mavenProject;
 
         private final boolean showAvatarImages;
 
-        private final String protocol;
+        private final Map<Contributor, String> avatarImages;
 
-        ProjectTeamRenderer(Sink sink, Model model, I18N i18n, Locale locale, 
boolean showAvatarImages) {
+        ProjectTeamRenderer(
+                Sink sink,
+                MavenProject mavenProject,
+                I18N i18n,
+                Locale locale,
+                boolean showAvatarImages,
+                Map<Contributor, String> avatarImages) {
             super(sink, i18n, locale);
 
-            this.model = model;
+            this.mavenProject = mavenProject;
             this.showAvatarImages = showAvatarImages;
-
-            // prepare protocol for gravatar
-            if (model.getUrl() != null && 
model.getUrl().startsWith("https://";)) {
-                this.protocol = "https";
-            } else {
-                this.protocol = "http";
-            }
+            this.avatarImages = avatarImages;
         }
 
         @Override
@@ -164,11 +242,11 @@ public class TeamReport extends AbstractProjectInfoReport 
{
             paragraph(getI18nString("intro.description2"));
 
             // Developer section
-            List<Developer> developers = model.getDevelopers();
+            List<Developer> developers = mavenProject.getDevelopers();
 
             startSection(getI18nString("developers.title"));
 
-            if (isEmpty(developers)) {
+            if (developers.isEmpty()) {
                 paragraph(getI18nString("nodeveloper"));
             } else {
                 paragraph(getI18nString("developers.intro"));
@@ -191,11 +269,11 @@ public class TeamReport extends AbstractProjectInfoReport 
{
             endSection();
 
             // contributors section
-            List<Contributor> contributors = model.getContributors();
+            List<Contributor> contributors = mavenProject.getContributors();
 
             startSection(getI18nString("contributors.title"));
 
-            if (isEmpty(contributors)) {
+            if (contributors.isEmpty()) {
                 paragraph(getI18nString("nocontributor"));
             } else {
                 paragraph(getI18nString("contributors.intro"));
@@ -223,17 +301,9 @@ public class TeamReport extends AbstractProjectInfoReport {
             sink.tableRow();
 
             if (headersMap.get(IMAGE) == Boolean.TRUE && showAvatarImages) {
-                Properties properties = member.getProperties();
-                String picUrl = properties.getProperty("picUrl");
-                if (picUrl == null || picUrl.isEmpty()) {
-                    picUrl = getGravatarUrl(member.getEmail());
-                }
-                if (picUrl == null || picUrl.isEmpty()) {
-                    picUrl = getSpacerGravatarUrl();
-                }
                 sink.tableCell();
                 sink.figure();
-                sink.figureGraphics(picUrl);
+                sink.figureGraphics(avatarImages.get(member));
                 sink.figure_();
                 sink.tableCell_();
             }
@@ -288,35 +358,6 @@ public class TeamReport extends AbstractProjectInfoReport {
             sink.tableRow_();
         }
 
-        private static final String AVATAR_SIZE = "s=60";
-
-        private String getSpacerGravatarUrl() {
-            return protocol + 
"://www.gravatar.com/avatar/00000000000000000000000000000000?d=blank&f=y&" + 
AVATAR_SIZE;
-        }
-
-        private String getGravatarUrl(String email) {
-            if (email == null) {
-                return null;
-            }
-            email = StringUtils.trim(email);
-            email = email.toLowerCase();
-            MessageDigest md;
-            try {
-                md = MessageDigest.getInstance("MD5");
-                md.update(email.getBytes());
-                byte[] byteData = md.digest();
-                StringBuilder sb = new StringBuilder();
-                final int lowerEightBitsOnly = 0xff;
-                for (byte aByteData : byteData) {
-                    sb.append(Integer.toString((aByteData & 
lowerEightBitsOnly) + 0x100, 16)
-                            .substring(1));
-                }
-                return protocol + "://www.gravatar.com/avatar/" + 
sb.toString() + "?d=mm&" + AVATAR_SIZE;
-            } catch (NoSuchAlgorithmException e) {
-                return null;
-            }
-        }
-
         private String[] getRequiredContrHeaderArray(Map<String, Boolean> 
requiredHeaders) {
             List<String> requiredArray = new ArrayList<>();
             String image = getI18nString("contributors.image");
@@ -461,7 +502,7 @@ public class TeamReport extends AbstractProjectInfoReport {
                 if (StringUtils.isNotEmpty(unit.getOrganizationUrl())) {
                     requiredHeaders.put(ORGANIZATION_URL, Boolean.TRUE);
                 }
-                if (!isEmpty(unit.getRoles())) {
+                if (!unit.getRoles().isEmpty()) {
                     requiredHeaders.put(ROLES, Boolean.TRUE);
                 }
                 if (StringUtils.isNotEmpty(unit.getTimezone())) {
@@ -494,9 +535,5 @@ public class TeamReport extends AbstractProjectInfoReport {
 
             sink.tableCell_();
         }
-
-        private static boolean isEmpty(List<?> list) {
-            return (list == null) || list.isEmpty();
-        }
     }
 }
diff --git 
a/src/main/java/org/apache/maven/report/projectinfo/avatars/AvatarsProvider.java
 
b/src/main/java/org/apache/maven/report/projectinfo/avatars/AvatarsProvider.java
new file mode 100644
index 0000000..c9c9975
--- /dev/null
+++ 
b/src/main/java/org/apache/maven/report/projectinfo/avatars/AvatarsProvider.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.report.projectinfo.avatars;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * Avatar provider API.
+ */
+public interface AvatarsProvider {
+
+    /**
+     * Set a base URL for provider
+     *
+     * @param baseUrl for provider
+     */
+    void setBaseUrl(String baseUrl);
+
+    /**
+     * Set site output directory. Used to store avatar images in project.
+     *
+     * @param outputDirectory a site output directory
+     */
+    void setOutputDirectory(File outputDirectory);
+
+    /**
+     * Return a URL for avatar image.
+     *
+     * @param email email address for gravatar image
+     * @return a URL for avatar image
+     */
+    String getAvatarUrl(String email);
+
+    /**
+     * Return a local path to downloaded avatar image.
+     *
+     * @param email email address for gravatar image
+     * @return a local avatar path
+     * @throws IOException if problem with image downloading
+     */
+    String getLocalAvatarPath(String email) throws IOException;
+}
diff --git 
a/src/main/java/org/apache/maven/report/projectinfo/avatars/GravatarProvider.java
 
b/src/main/java/org/apache/maven/report/projectinfo/avatars/GravatarProvider.java
new file mode 100644
index 0000000..d3b5405
--- /dev/null
+++ 
b/src/main/java/org/apache/maven/report/projectinfo/avatars/GravatarProvider.java
@@ -0,0 +1,156 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.report.projectinfo.avatars;
+
+import javax.inject.Named;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Locale;
+
+import org.codehaus.plexus.util.IOUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Provider for user avatar from gravatar.com
+ * <p>
+ * <a href="https://docs.gravatar.com/api/avatars/images/";>Gravatar API</a>
+ */
+@Named("gravatar")
+class GravatarProvider implements AvatarsProvider {
+
+    private static final Logger LOGGER = 
LoggerFactory.getLogger(GravatarProvider.class);
+
+    private static final String AVATAR_SIZE = "s=60";
+
+    private static final String AVATAR_DIRECTORY = "avatars";
+
+    private static final String AVATAR_DEFAULT_FILE_NAME = 
"00000000000000000000000000000000.jpg";
+
+    private String baseUrl = "https://www.gravatar.com/avatar/";;
+
+    private Path outputDirectory;
+
+    @Override
+    public void setBaseUrl(String baseUrl) {
+        this.baseUrl = baseUrl.endsWith("/") ? baseUrl : baseUrl + "/";
+    }
+
+    @Override
+    public void setOutputDirectory(File outputDirectory) {
+        this.outputDirectory = outputDirectory.toPath();
+    }
+
+    public String getAvatarUrl(String email) {
+        return getAvatarUrl(email, "blank");
+    }
+
+    private String getAvatarUrl(String email, String defaultAvatar) {
+        if (email == null || email.isEmpty()) {
+            return getSpacerGravatarUrl();
+        }
+
+        try {
+            email = email.trim().toLowerCase(Locale.ROOT);
+            MessageDigest md = MessageDigest.getInstance("MD5");
+            md.update(email.getBytes());
+            byte[] byteData = md.digest();
+            StringBuilder sb = new StringBuilder();
+            final int lowerEightBitsOnly = 0xff;
+            for (byte aByteData : byteData) {
+                sb.append(Integer.toString((aByteData & lowerEightBitsOnly) + 
0x100, 16)
+                        .substring(1));
+            }
+            return baseUrl + sb + ".jpg?d=" + defaultAvatar + "&" + 
AVATAR_SIZE;
+        } catch (NoSuchAlgorithmException e) {
+            LOGGER.warn("Error while getting MD5 hash, use default image: {}", 
e.getMessage());
+            return getSpacerGravatarUrl();
+        }
+    }
+
+    @Override
+    public String getLocalAvatarPath(String email) throws IOException {
+        // use 404 http status for not existing avatars
+        String avatarUrl = getAvatarUrl(email, "404");
+        try {
+            URL url = new URI(avatarUrl).toURL();
+            Path name = Paths.get(url.getPath()).getFileName();
+            if (AVATAR_DEFAULT_FILE_NAME.equals(name.toString())) {
+                copyDefault();
+            } else {
+                copyUrl(url, 
outputDirectory.resolve(AVATAR_DIRECTORY).resolve(name));
+            }
+            return AVATAR_DIRECTORY + "/" + name;
+        } catch (URISyntaxException | IOException e) {
+            if (e instanceof FileNotFoundException) {
+                LOGGER.debug(
+                        "Error while getting external avatar url for: {}, use 
default image: {}:{}",
+                        email,
+                        e.getClass().getName(),
+                        e.getMessage());
+            } else {
+                LOGGER.warn(
+                        "Error while getting external avatar url for: {}, use 
default image: {}:{}",
+                        email,
+                        e.getClass().getName(),
+                        e.getMessage());
+            }
+            copyDefault();
+            return AVATAR_DIRECTORY + "/" + AVATAR_DEFAULT_FILE_NAME;
+        }
+    }
+
+    private String getSpacerGravatarUrl() {
+        return baseUrl + AVATAR_DEFAULT_FILE_NAME + "?d=blank&f=y&" + 
AVATAR_SIZE;
+    }
+
+    private void copyUrl(URL url, Path outputPath) throws IOException {
+        if (!Files.exists(outputPath)) {
+            Files.createDirectories(outputPath.getParent());
+            try (InputStream in = url.openStream();
+                    OutputStream out = Files.newOutputStream(outputPath)) {
+                LOGGER.debug("Copying URL {} to {}", url, outputPath);
+                IOUtil.copy(in, out);
+            }
+        }
+    }
+
+    private void copyDefault() throws IOException {
+        Path outputPath = 
outputDirectory.resolve(AVATAR_DIRECTORY).resolve(AVATAR_DEFAULT_FILE_NAME);
+        if (!Files.exists(outputPath)) {
+            Files.createDirectories(outputPath.getParent());
+            try (InputStream in = 
getClass().getResourceAsStream("default-avatar.jpg");
+                    OutputStream out = Files.newOutputStream(outputPath)) {
+                IOUtil.copy(in, out);
+            }
+        }
+    }
+}
diff --git 
a/src/main/resources/org/apache/maven/report/projectinfo/avatars/default-avatar.jpg
 
b/src/main/resources/org/apache/maven/report/projectinfo/avatars/default-avatar.jpg
new file mode 100644
index 0000000..a32c67d
Binary files /dev/null and 
b/src/main/resources/org/apache/maven/report/projectinfo/avatars/default-avatar.jpg
 differ
diff --git 
a/src/test/java/org/apache/maven/report/projectinfo/TeamReportTest.java 
b/src/test/java/org/apache/maven/report/projectinfo/TeamReportTest.java
index e9d9340..409ea0d 100644
--- a/src/test/java/org/apache/maven/report/projectinfo/TeamReportTest.java
+++ b/src/test/java/org/apache/maven/report/projectinfo/TeamReportTest.java
@@ -50,6 +50,9 @@ public class TeamReportTest extends 
AbstractProjectInfoTestCase {
         File pluginXmlFile = new File(getBasedir(), 
"src/test/resources/plugin-configs/" + "team-plugin-config.xml");
         AbstractProjectInfoReport mojo = createReportMojo(getGoal(), 
pluginXmlFile);
         setVariableValueToObject(mojo, "showAvatarImages", Boolean.TRUE);
+        setVariableValueToObject(mojo, "externalAvatarImages", Boolean.TRUE);
+        setVariableValueToObject(mojo, "avatarProviderName", "gravatar");
+        setVariableValueToObject(mojo, "avatarBaseUrl", 
"https://www.gravatar.com/avatar/";);
         generateReport(mojo, pluginXmlFile);
         assertTrue("Test html generated", 
getGeneratedReport("team.html").exists());
 
diff --git 
a/src/test/java/org/apache/maven/report/projectinfo/avatars/GravatarProviderTest.java
 
b/src/test/java/org/apache/maven/report/projectinfo/avatars/GravatarProviderTest.java
new file mode 100644
index 0000000..f3c91b4
--- /dev/null
+++ 
b/src/test/java/org/apache/maven/report/projectinfo/avatars/GravatarProviderTest.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.report.projectinfo.avatars;
+
+import java.io.File;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class GravatarProviderTest {
+
+    @Rule
+    public TemporaryFolder tmpFolder = new TemporaryFolder();
+
+    @Test
+    public void urlShouldBeCorrect() {
+        GravatarProvider gravatarProvider = new GravatarProvider();
+        gravatarProvider.setBaseUrl("https://www.gravatar.com/avatar";);
+        String externalAvatarUrl = 
gravatarProvider.getAvatarUrl("em...@example.com");
+
+        assertEquals(
+                
"https://www.gravatar.com/avatar/5658ffccee7f0ebfda2b226238b1eb6e.jpg?d=blank&s=60";,
 externalAvatarUrl);
+    }
+
+    @Test
+    public void urlForEmptyEmailShouldBeCorrect() {
+        GravatarProvider gravatarProvider = new GravatarProvider();
+        gravatarProvider.setBaseUrl("https://www.gravatar.com/avatar";);
+
+        String externalAvatarUrl = gravatarProvider.getAvatarUrl(null);
+
+        assertEquals(
+                
"https://www.gravatar.com/avatar/00000000000000000000000000000000.jpg?d=blank&f=y&s=60";,
+                externalAvatarUrl);
+
+        externalAvatarUrl = gravatarProvider.getAvatarUrl("");
+
+        assertEquals(
+                
"https://www.gravatar.com/avatar/00000000000000000000000000000000.jpg?d=blank&f=y&s=60";,
+                externalAvatarUrl);
+    }
+
+    @Test
+    public void localAvatarPathShouldBeCorrect() throws Exception {
+        GravatarProvider gravatarProvider = new GravatarProvider();
+        gravatarProvider.setBaseUrl("https://www.gravatar.com/avatar";);
+        gravatarProvider.setOutputDirectory(tmpFolder.getRoot());
+        String localAvatarUrl = 
gravatarProvider.getLocalAvatarPath("sjaranow...@apache.org");
+        assertEquals("avatars/90cc13b765c79d2d55ca64388ea2bc5f.jpg", 
localAvatarUrl);
+        assertTrue(new File(tmpFolder.getRoot(), 
"avatars/90cc13b765c79d2d55ca64388ea2bc5f.jpg").exists());
+    }
+
+    @Test
+    public void localAvatarPathShouldHaveDefaultForNotExisting() throws 
Exception {
+        GravatarProvider gravatarProvider = new GravatarProvider();
+        gravatarProvider.setBaseUrl("https://www.gravatar.com/avatar";);
+        gravatarProvider.setOutputDirectory(tmpFolder.getRoot());
+        String localAvatarUrl = 
gravatarProvider.getLocalAvatarPath("t...@example.com");
+        assertEquals("avatars/00000000000000000000000000000000.jpg", 
localAvatarUrl);
+        assertTrue(new File(tmpFolder.getRoot(), 
"avatars/00000000000000000000000000000000.jpg").exists());
+    }
+
+    @Test
+    public void localAvatarPathShouldBeCorrectForDefault() throws Exception {
+        GravatarProvider gravatarProvider = new GravatarProvider();
+        gravatarProvider.setBaseUrl("https://www.gravatar.com/avatar";);
+        gravatarProvider.setOutputDirectory(tmpFolder.getRoot());
+        String localAvatarUrl = gravatarProvider.getLocalAvatarPath(null);
+        assertEquals("avatars/00000000000000000000000000000000.jpg", 
localAvatarUrl);
+        assertTrue(new File(tmpFolder.getRoot(), 
"avatars/00000000000000000000000000000000.jpg").exists());
+    }
+}
diff --git 
a/src/test/java/org/apache/maven/report/projectinfo/stubs/TeamStub.java 
b/src/test/java/org/apache/maven/report/projectinfo/stubs/TeamStub.java
index 63d0e7b..bc41f1a 100644
--- a/src/test/java/org/apache/maven/report/projectinfo/stubs/TeamStub.java
+++ b/src/test/java/org/apache/maven/report/projectinfo/stubs/TeamStub.java
@@ -18,6 +18,11 @@
  */
 package org.apache.maven.report.projectinfo.stubs;
 
+import java.util.List;
+
+import org.apache.maven.model.Contributor;
+import org.apache.maven.model.Developer;
+
 /**
  * @author <a href="mailto:vincent.sive...@gmail.com";>Vincent Siveton</a>
  * @version $Id$
@@ -27,4 +32,14 @@ public class TeamStub extends ProjectInfoProjectStub {
     protected String getPOM() {
         return "team-plugin-config.xml";
     }
+
+    @Override
+    public List<Contributor> getContributors() {
+        return getModel().getContributors();
+    }
+
+    @Override
+    public List<Developer> getDevelopers() {
+        return getModel().getDevelopers();
+    }
 }

Reply via email to