James Moger
2011-11-21 797322eb90a1f5d21166fd691479b050ad7b754b
Tighter Gravatar integration. New window/tab on most activity links.

Each Gravatar thumbnail is now a link to the Gitblit Gravatar profile
page. This page displays some of the profile information and contains a
link to the full Gravatar profile.

Activity page links now launch a new window/tab instead of targeting
"self" since it can be expensive to requery the activity information.
1 files deleted
6 files added
7 files modified
443 ■■■■ changed files
src/com/gitblit/models/GravatarProfile.java 83 ●●●●● patch | view | raw | blame | history
src/com/gitblit/utils/ActivityUtils.java 66 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/GitBlitWebApp.java 2 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/GravatarImage.java 62 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/CommitPage.html 4 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/CommitPage.java 2 ●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/GravatarProfilePage.html 20 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/GravatarProfilePage.java 64 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/panels/ActivityPanel.html 4 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/panels/ActivityPanel.java 11 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/panels/GravatarImage.html 9 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/panels/GravatarImage.java 68 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/panels/LinkPanel.java 17 ●●●● patch | view | raw | blame | history
tests/com/gitblit/tests/ActivityTest.java 31 ●●●●● patch | view | raw | blame | history
src/com/gitblit/models/GravatarProfile.java
New file
@@ -0,0 +1,83 @@
/*
 * Copyright 2011 gitblit.com.
 *
 * Licensed 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 com.gitblit.models;
import java.io.Serializable;
import java.util.List;
/**
 * Represents a Gravatar profile.
 *
 * @author James Moger
 *
 */
public class GravatarProfile implements Serializable {
    private static final long serialVersionUID = 1L;
    public String id;
    public String hash;
    public String requestHash;
    public String displayName;
    public String preferredUsername;
    public String currentLocation;
    public String aboutMe;
    public String profileUrl;
    public String thumbnailUrl;
    public List<ProfileObject> photos;
//    public Map<String, String> profileBackground;
//    public Map<String, String> name;
    public List<ProfileObject> phoneNumbers;
    public List<ProfileObject> emails;
    public List<ProfileObject> ims;
    public List<Account> accounts;
    public List<ProfileObject> urls;
    public static class ProfileObject implements Serializable {
        private static final long serialVersionUID = 1L;
        public String title;
        public String type;
        public String value;
        public boolean primary;
        @Override
        public String toString() {
            return value;
        }
    }
    public static class Account implements Serializable {
        private static final long serialVersionUID = 1L;
        public String domain;
        public String display;
        public String url;
        public String username;
        public String userid;
        public boolean verified;
        public String shortname;
        @Override
        public String toString() {
            return display;
        }
    }
}
src/com/gitblit/utils/ActivityUtils.java
@@ -15,7 +15,11 @@
 */
package com.gitblit.utils;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.Type;
import java.text.DateFormat;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
@@ -33,8 +37,10 @@
import com.gitblit.GitBlit;
import com.gitblit.models.Activity;
import com.gitblit.models.Activity.RepositoryCommit;
import com.gitblit.models.GravatarProfile;
import com.gitblit.models.RefModel;
import com.gitblit.models.RepositoryModel;
import com.google.gson.reflect.TypeToken;
/**
 * Utility class for building activity information from repositories.
@@ -127,4 +133,64 @@
        }
        return recentActivity;
    }
    /**
     * Returns the Gravatar profile, if available, for the specified email
     * address.
     *
     * @param emailaddress
     * @return a Gravatar Profile
     * @throws IOException
     */
    public static GravatarProfile getGravatarProfileFromAddress(String emailaddress)
            throws IOException {
        return getGravatarProfile(StringUtils.getMD5(emailaddress.toLowerCase()));
    }
    /**
     * Creates a Gravatar thumbnail url from the specified email address.
     *
     * @param email
     *            address to query Gravatar
     * @param width
     *            size of thumbnail. if width <= 0, the defalt of 60 is used.
     * @return
     */
    public static String getGravatarThumbnailUrl(String email, int width) {
        if (width <= 0) {
            width = 60;
        }
        String emailHash = StringUtils.getMD5(email);
        String url = MessageFormat.format(
                "http://www.gravatar.com/avatar/{0}?s={1,number,0}&d=identicon", emailHash, width);
        return url;
    }
    /**
     * Returns the Gravatar profile, if available, for the specified hashcode.
     * address.
     *
     * @param hash
     *            the hash of the email address
     * @return a Gravatar Profile
     * @throws IOException
     */
    public static GravatarProfile getGravatarProfile(String hash) throws IOException {
        String url = MessageFormat.format("http://www.gravatar.com/{0}.json", hash);
        // Gravatar has a complex json structure
        Type profileType = new TypeToken<Map<String, List<GravatarProfile>>>() {
        }.getType();
        Map<String, List<GravatarProfile>> profiles = null;
        try {
            profiles = JsonUtils.retrieveJson(url, profileType);
        } catch (FileNotFoundException e) {
        }
        if (profiles == null || profiles.size() == 0) {
            return null;
        }
        // due to the complex json structure we need to pull out the profile
        // from a list 2 levels deep
        GravatarProfile profile = profiles.values().iterator().next().get(0);
        return profile;
    }
}
src/com/gitblit/wicket/GitBlitWebApp.java
@@ -34,6 +34,7 @@
import com.gitblit.wicket.pages.CommitPage;
import com.gitblit.wicket.pages.DocsPage;
import com.gitblit.wicket.pages.FederationRegistrationPage;
import com.gitblit.wicket.pages.GravatarProfilePage;
import com.gitblit.wicket.pages.HistoryPage;
import com.gitblit.wicket.pages.LogPage;
import com.gitblit.wicket.pages.MarkdownPage;
@@ -106,6 +107,7 @@
        mount("/registration", FederationRegistrationPage.class, "u", "n");
        
        mount("/activity", ActivityPage.class, "r", "h");
        mount("/gravatar", GravatarProfilePage.class, "h");
    }
    private void mount(String location, Class<? extends WebPage> clazz, String... parameters) {
src/com/gitblit/wicket/GravatarImage.java
File was deleted
src/com/gitblit/wicket/pages/CommitPage.html
@@ -16,7 +16,7 @@
    <div wicket:id="commitHeader">[commit header]</div>
    
    <!--  Author Gravatar -->
    <img style="float:right;vertical-align: top;" wicket:id="authorAvatar" />
    <span style="float:right;vertical-align: top;" wicket:id="authorAvatar" />
    
    <!-- commit info -->
    <table class="plain">
@@ -57,7 +57,7 @@
                    <tr><td><span class="sha1" wicket:id="authorDate"></span></td></tr>
                </table>
                <!--  Note Author Gravatar -->
                <img style="vertical-align: top;" wicket:id="noteAuthorAvatar" />
                <span style="vertical-align: top;" wicket:id="noteAuthorAvatar" />
            </td>
            <td class="message"><span class="sha1" wicket:id="noteContent"></span></td>
        </tr>
src/com/gitblit/wicket/pages/CommitPage.java
@@ -38,10 +38,10 @@
import com.gitblit.models.GitNote;
import com.gitblit.models.PathModel.PathChangeModel;
import com.gitblit.utils.JGitUtils;
import com.gitblit.wicket.GravatarImage;
import com.gitblit.wicket.WicketUtils;
import com.gitblit.wicket.panels.CommitHeaderPanel;
import com.gitblit.wicket.panels.CommitLegendPanel;
import com.gitblit.wicket.panels.GravatarImage;
import com.gitblit.wicket.panels.LinkPanel;
import com.gitblit.wicket.panels.RefsPanel;
src/com/gitblit/wicket/pages/GravatarProfilePage.html
New file
@@ -0,0 +1,20 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
      xml:lang="en"
      lang="en">
<body>
<wicket:extend>
    <div class="page-header">
        <h2>Gravatar<small> / <span wicket:id="username">[username]</span></small></h2>
    </div>
    <img class="gravatar" wicket:id="profileImage"></img>
    <h2 wicket:id="displayName"></h2>
    <div style="color:#888;"wicket:id="location"></div>
    <div style="padding-top:5px;" wicket:id="aboutMe"></div>
    <p></p>
    <a wicket:id="profileLink">Complete profile on Gravatar.com</a>
    <p></p>
</wicket:extend>
</body>
</html>
src/com/gitblit/wicket/pages/GravatarProfilePage.java
New file
@@ -0,0 +1,64 @@
/*
 * Copyright 2011 gitblit.com.
 *
 * Licensed 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 com.gitblit.wicket.pages;
import java.io.IOException;
import java.text.MessageFormat;
import org.apache.wicket.AttributeModifier;
import org.apache.wicket.PageParameters;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.image.Image;
import org.apache.wicket.markup.html.link.ExternalLink;
import org.apache.wicket.model.Model;
import com.gitblit.models.GravatarProfile;
import com.gitblit.utils.ActivityUtils;
import com.gitblit.wicket.WicketUtils;
/**
 * Gravatar Profile Page shows the Gravatar information, if available.
 *
 * @author James Moger
 *
 */
public class GravatarProfilePage extends RootPage {
    public GravatarProfilePage(PageParameters params) {
        super();
        setupPage("", "");
        String object = WicketUtils.getObject(params);
        GravatarProfile profile = null;
        try {
            if (object.indexOf('@') > -1) {
                profile = ActivityUtils.getGravatarProfileFromAddress(object);
            } else {
                profile = ActivityUtils.getGravatarProfile(object);
            }
        } catch (IOException e) {
            error(MessageFormat.format("Failed to find Gravatar profile for {0}", object), e, true);
        }
        add(new Label("displayName", profile.displayName));
        add(new Label("username", profile.preferredUsername));
        add(new Label("location", profile.currentLocation));
        add(new Label("aboutMe", profile.aboutMe));
        Image image = new Image("profileImage");
        image.add(new AttributeModifier("src", true, new Model<String>(profile.thumbnailUrl + "?s=256&d=identicon")));
        add(image);
        add(new ExternalLink("profileLink", profile.profileUrl));
    }
}
src/com/gitblit/wicket/panels/ActivityPanel.html
@@ -19,7 +19,7 @@
        <td style="width:10em;text-align:left;vertical-align: middle;">
            <span wicket:id="repository" class="repositorySwatch">[repository link]</span>
        </td>
        <td style="width:30px;vertical-align: middle;"><img wicket:id="avatar" style="vertical-align: middle;"></img></td>
        <td style="width:30px;vertical-align: middle;"><span wicket:id="avatar" style="vertical-align: middle;"></span></td>
        <td class="author" style="vertical-align: middle;">
            <img wicket:id="commitIcon" style="vertical-align: middle;"></img>
            <span wicket:id="message">[shortlog commit link]</span><br/>
@@ -30,7 +30,7 @@
        </td>
        <td class="rightAlign" style="width:7em;vertical-align: middle;">
            <span class="link">
                <a wicket:id="view"><wicket:message key="gb.view"></wicket:message></a> | <a wicket:id="diff"><wicket:message key="gb.diff"></wicket:message></a> | <a wicket:id="tree"><wicket:message key="gb.tree"></wicket:message></a>
                <a wicket:id="view" target="_blank"><wicket:message key="gb.view"></wicket:message></a> | <a wicket:id="diff" target="_blank"><wicket:message key="gb.diff"></wicket:message></a> | <a wicket:id="tree" target="_blank"><wicket:message key="gb.tree"></wicket:message></a>
            </span>
        </td>        
    </wicket:fragment>
src/com/gitblit/wicket/panels/ActivityPanel.java
@@ -29,7 +29,6 @@
import com.gitblit.models.Activity.RepositoryCommit;
import com.gitblit.utils.StringUtils;
import com.gitblit.wicket.GitBlitWebSession;
import com.gitblit.wicket.GravatarImage;
import com.gitblit.wicket.WicketUtils;
import com.gitblit.wicket.pages.CommitDiffPage;
import com.gitblit.wicket.pages.CommitPage;
@@ -90,27 +89,27 @@
                        String author = commit.getAuthorIdent().getName();
                        LinkPanel authorLink = new LinkPanel("author", "list", author,
                                SearchPage.class, WicketUtils.newSearchParameter(commit.repository,
                                        commit.getName(), author, Constants.SearchType.AUTHOR));
                                        commit.getName(), author, Constants.SearchType.AUTHOR), true);
                        setPersonSearchTooltip(authorLink, author, Constants.SearchType.AUTHOR);
                        fragment.add(authorLink);
                        // repository
                        LinkPanel repositoryLink = new LinkPanel("repository", null,
                                commit.repository, SummaryPage.class,
                                WicketUtils.newRepositoryParameter(commit.repository));
                                WicketUtils.newRepositoryParameter(commit.repository), true);
                        WicketUtils.setCssBackground(repositoryLink, commit.repository);
                        fragment.add(repositoryLink);
                        // repository branch
                        LinkPanel branchLink = new LinkPanel("branch", "list", commit.branch,
                                LogPage.class, WicketUtils.newObjectParameter(commit.repository,
                                        commit.branch));
                                        commit.branch), true);
                        WicketUtils.setCssStyle(branchLink, "color: #008000;");
                        fragment.add(branchLink);
                        LinkPanel commitid = new LinkPanel("commitid", "list subject",
                                commit.getShortName(), CommitPage.class,
                                WicketUtils.newObjectParameter(commit.repository, commit.getName()));
                                WicketUtils.newObjectParameter(commit.repository, commit.getName()), true);
                        fragment.add(commitid);
                        // message/commit link
@@ -118,7 +117,7 @@
                        String trimmedMessage = StringUtils.trimShortLog(shortMessage);
                        LinkPanel shortlog = new LinkPanel("message", "list subject",
                                trimmedMessage, CommitPage.class, WicketUtils.newObjectParameter(
                                        commit.repository, commit.getName()));
                                        commit.repository, commit.getName()), true);
                        if (!shortMessage.equals(trimmedMessage)) {
                            WicketUtils.setHtmlTooltip(shortlog, shortMessage);
                        }
src/com/gitblit/wicket/panels/GravatarImage.html
New file
@@ -0,0 +1,9 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
      xml:lang="en"
      lang="en">
<wicket:panel>
<a href="#" wicket:id="link"><img wicket:id="image"></img></a>
</wicket:panel>
</html>
src/com/gitblit/wicket/panels/GravatarImage.java
New file
@@ -0,0 +1,68 @@
/*
 * Copyright 2011 gitblit.com.
 *
 * Licensed 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 com.gitblit.wicket.panels;
import java.text.MessageFormat;
import org.apache.wicket.AttributeModifier;
import org.apache.wicket.behavior.SimpleAttributeModifier;
import org.apache.wicket.markup.html.image.Image;
import org.apache.wicket.markup.html.link.BookmarkablePageLink;
import org.apache.wicket.markup.html.link.Link;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.model.Model;
import org.eclipse.jgit.lib.PersonIdent;
import com.gitblit.GitBlit;
import com.gitblit.Keys;
import com.gitblit.utils.ActivityUtils;
import com.gitblit.utils.StringUtils;
import com.gitblit.wicket.WicketUtils;
import com.gitblit.wicket.pages.GravatarProfilePage;
/**
 * Represents a Gravatar image and links to the Gravatar profile page.
 *
 * @author James Moger
 *
 */
public class GravatarImage extends Panel {
    private static final long serialVersionUID = 1L;
    public GravatarImage(String id, PersonIdent person) {
        this(id, person, 0);
    }
    public GravatarImage(String id, PersonIdent person, int width) {
        super(id);
        String email = person.getEmailAddress().toLowerCase();
        String hash = StringUtils.getMD5(email);
        Link<Void> link = new BookmarkablePageLink<Void>("link", GravatarProfilePage.class,
                WicketUtils.newObjectParameter(hash));
        link.add(new SimpleAttributeModifier("target", "_blank"));
        String url = ActivityUtils.getGravatarThumbnailUrl(email, width);
        Image image = new Image("image");
        image.add(new AttributeModifier("src", true, new Model<String>(url)));
        WicketUtils.setCssClass(image, "gravatar");
        link.add(image);
        WicketUtils.setHtmlTooltip(link,
                MessageFormat.format("View Gravatar profile for {0}", person.getName()));
        add(link);
        setVisible(GitBlit.getBoolean(Keys.web.allowGravatar, true));
    }
}
src/com/gitblit/wicket/panels/LinkPanel.java
@@ -33,16 +33,26 @@
    public LinkPanel(String wicketId, String linkCssClass, String label,
            Class<? extends WebPage> clazz) {
        this(wicketId, linkCssClass, new Model<String>(label), clazz, null);
        this(wicketId, linkCssClass, new Model<String>(label), clazz, null, false);
    }
    public LinkPanel(String wicketId, String linkCssClass, String label,
            Class<? extends WebPage> clazz, PageParameters parameters) {
        this(wicketId, linkCssClass, new Model<String>(label), clazz, parameters);
        this(wicketId, linkCssClass, new Model<String>(label), clazz, parameters, false);
    }
    public LinkPanel(String wicketId, String linkCssClass, String label,
            Class<? extends WebPage> clazz, PageParameters parameters, boolean newWindow) {
        this(wicketId, linkCssClass, new Model<String>(label), clazz, parameters, newWindow);
    }
    public LinkPanel(String wicketId, String linkCssClass, IModel<String> model,
            Class<? extends WebPage> clazz, PageParameters parameters) {
        this(wicketId, linkCssClass, model, clazz, parameters, false);
    }
    public LinkPanel(String wicketId, String linkCssClass, IModel<String> model,
            Class<? extends WebPage> clazz, PageParameters parameters, boolean newWindow) {
        super(wicketId);
        this.labelModel = model;
        Link<Void> link = null;
@@ -51,6 +61,9 @@
        } else {
            link = new BookmarkablePageLink<Void>("link", clazz, parameters);
        }
        if (newWindow) {
            link.add(new SimpleAttributeModifier("target", "_blank"));
        }
        if (linkCssClass != null) {
            link.add(new SimpleAttributeModifier("class", linkCssClass));
        }
tests/com/gitblit/tests/ActivityTest.java
New file
@@ -0,0 +1,31 @@
/*
 * Copyright 2011 gitblit.com.
 *
 * Licensed 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 com.gitblit.tests;
import java.io.IOException;
import junit.framework.TestCase;
import com.gitblit.models.GravatarProfile;
import com.gitblit.utils.ActivityUtils;
public class ActivityTest extends TestCase {
    public void testGravatarProfile() throws IOException {
        GravatarProfile profile = ActivityUtils.getGravatarProfile("beau@dentedreality.com.au");
        assertEquals("beau", profile.preferredUsername);
    }
}