James Moger
2011-11-19 db91a34ccb5135f733c7431fdba49669131e97da
Reorganizing the recent activity feature
2 files deleted
2 files added
8 files modified
701 ■■■■■ changed files
src/com/gitblit/models/Activity.java 169 ●●●●● patch | view | raw | blame | history
src/com/gitblit/models/DailyActivity.java 47 ●●●●● patch | view | raw | blame | history
src/com/gitblit/models/RepositoryCommit.java 86 ●●●●● patch | view | raw | blame | history
src/com/gitblit/utils/ActivityUtils.java 130 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/GitBlitWebApp.properties 3 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/WicketUtils.java 23 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/charting/GoogleCharts.java 4 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/charting/GooglePieChart.java 14 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/ActivityPage.html 2 ●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/ActivityPage.java 187 ●●●●● 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 32 ●●●●● patch | view | raw | blame | history
src/com/gitblit/models/Activity.java
New file
@@ -0,0 +1,169 @@
/*
 * 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.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.revwalk.RevCommit;
import com.gitblit.utils.TimeUtils;
/**
 * Model class to represent the commit activity across many repositories. This
 * class is used by the Activity page.
 *
 * @author James Moger
 */
public class Activity implements Serializable, Comparable<Activity> {
    private static final long serialVersionUID = 1L;
    public final Date startDate;
    public final Date endDate;
    public final List<RepositoryCommit> commits;
    private final Map<String, Metric> authorMetrics;
    private final Map<String, Metric> repositoryMetrics;
    /**
     * Constructor for one day of activity.
     *
     * @param date
     */
    public Activity(Date date) {
        this(date, TimeUtils.ONEDAY - 1);
    }
    /**
     * Constructor for specified duration of activity from start date.
     *
     * @param date
     *            the start date of the activity
     * @param duration
     *            the duration of the period in milliseconds
     */
    public Activity(Date date, long duration) {
        startDate = date;
        endDate = new Date(date.getTime() + duration);
        commits = new ArrayList<RepositoryCommit>();
        authorMetrics = new HashMap<String, Metric>();
        repositoryMetrics = new HashMap<String, Metric>();
    }
    public RepositoryCommit addCommit(String repository, String branch, RevCommit commit) {
        RepositoryCommit commitModel = new RepositoryCommit(repository, branch, commit);
        commits.add(commitModel);
        if (!repositoryMetrics.containsKey(repository)) {
            repositoryMetrics.put(repository, new Metric(repository));
        }
        repositoryMetrics.get(repository).count++;
        String author = commit.getAuthorIdent().getEmailAddress().toLowerCase();
        if (!authorMetrics.containsKey(author)) {
            authorMetrics.put(author, new Metric(author));
        }
        authorMetrics.get(author).count++;
        return commitModel;
    }
    public Map<String, Metric> getAuthorMetrics() {
        return authorMetrics;
    }
    public Map<String, Metric> getRepositoryMetrics() {
        return repositoryMetrics;
    }
    @Override
    public int compareTo(Activity o) {
        // reverse chronological order
        return o.startDate.compareTo(startDate);
    }
    /**
     * Model class to represent a RevCommit, it's source repository, and the
     * branch. This class is used by the activity page.
     *
     * @author James Moger
     */
    public static class RepositoryCommit implements Serializable, Comparable<RepositoryCommit> {
        private static final long serialVersionUID = 1L;
        public final String repository;
        public final String branch;
        private final RevCommit commit;
        private List<RefModel> refs;
        public RepositoryCommit(String repository, String branch, RevCommit commit) {
            this.repository = repository;
            this.branch = branch;
            this.commit = commit;
        }
        public void setRefs(List<RefModel> refs) {
            this.refs = refs;
        }
        public List<RefModel> getRefs() {
            return refs;
        }
        public String getName() {
            return commit.getName();
        }
        public String getShortName() {
            return commit.getName().substring(0, 8);
        }
        public String getShortMessage() {
            return commit.getShortMessage();
        }
        public int getParentCount() {
            return commit.getParentCount();
        }
        public PersonIdent getAuthorIdent() {
            return commit.getAuthorIdent();
        }
        @Override
        public int compareTo(RepositoryCommit o) {
            // reverse-chronological order
            if (commit.getCommitTime() > o.commit.getCommitTime()) {
                return -1;
            } else if (commit.getCommitTime() < o.commit.getCommitTime()) {
                return 1;
            }
            return 0;
        }
    }
}
src/com/gitblit/models/DailyActivity.java
File was deleted
src/com/gitblit/models/RepositoryCommit.java
File was deleted
src/com/gitblit/utils/ActivityUtils.java
New file
@@ -0,0 +1,130 @@
/*
 * 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.utils;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import com.gitblit.GitBlit;
import com.gitblit.models.Activity;
import com.gitblit.models.Activity.RepositoryCommit;
import com.gitblit.models.RefModel;
import com.gitblit.models.RepositoryModel;
/**
 * Utility class for building activity information from repositories.
 *
 * @author James Moger
 *
 */
public class ActivityUtils {
    /**
     * Gets the recent activity from the repositories for the last daysBack days
     * on the specified branch.
     *
     * @param models
     *            the list of repositories to query
     * @param daysBack
     *            the number of days back from Now to collect
     * @param objectId
     *            the branch to retrieve. If this value is null the default
     *            branch of the repository is used.
     * @return
     */
    public static List<Activity> getRecentActivity(List<RepositoryModel> models, int daysBack,
            String objectId) {
        // Activity panel shows last daysBack of activity across all
        // repositories.
        Date thresholdDate = new Date(System.currentTimeMillis() - daysBack * TimeUtils.ONEDAY);
        // Build a map of DailyActivity from the available repositories for the
        // specified threshold date.
        DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
        Calendar cal = Calendar.getInstance();
        Map<String, Activity> activity = new HashMap<String, Activity>();
        for (RepositoryModel model : models) {
            if (model.hasCommits && model.lastChange.after(thresholdDate)) {
                Repository repository = GitBlit.self().getRepository(model.name);
                List<RevCommit> commits = JGitUtils.getRevLog(repository, objectId, thresholdDate);
                Map<ObjectId, List<RefModel>> allRefs = JGitUtils.getAllRefs(repository);
                repository.close();
                // determine commit branch
                String branch = objectId;
                if (StringUtils.isEmpty(branch)) {
                    List<RefModel> headRefs = allRefs.get(commits.get(0).getId());
                    List<String> localBranches = new ArrayList<String>();
                    for (RefModel ref : headRefs) {
                        if (ref.getName().startsWith(Constants.R_HEADS)) {
                            localBranches.add(ref.getName().substring(Constants.R_HEADS.length()));
                        }
                    }
                    // determine branch
                    if (localBranches.size() == 1) {
                        // only one branch, choose it
                        branch = localBranches.get(0);
                    } else if (localBranches.size() > 1) {
                        if (localBranches.contains("master")) {
                            // choose master
                            branch = "master";
                        } else {
                            // choose first branch
                            branch = localBranches.get(0);
                        }
                    }
                }
                for (RevCommit commit : commits) {
                    Date date = JGitUtils.getCommitDate(commit);
                    String dateStr = df.format(date);
                    if (!activity.containsKey(dateStr)) {
                        // Normalize the date to midnight
                        cal.setTime(date);
                        cal.set(Calendar.HOUR_OF_DAY, 0);
                        cal.set(Calendar.MINUTE, 0);
                        cal.set(Calendar.SECOND, 0);
                        cal.set(Calendar.MILLISECOND, 0);
                        activity.put(dateStr, new Activity(cal.getTime()));
                    }
                    RepositoryCommit commitModel = activity.get(dateStr).addCommit(model.name,
                            branch, commit);
                    commitModel.setRefs(allRefs.get(commit.getId()));
                }
            }
        }
        List<Activity> recentActivity = new ArrayList<Activity>(activity.values());
        for (Activity daily : recentActivity) {
            Collections.sort(daily.commits);
        }
        return recentActivity;
    }
}
src/com/gitblit/wicket/GitBlitWebApp.properties
@@ -182,7 +182,8 @@
gb.branch = branch
gb.maxHits = max hits
gb.recentActivity = recent activity
gb.recentActivitySubheader = last {0} days / {1} commits by {2} authors
gb.recentActivityStats = last {0} days / {1} commits by {2} authors
gb.recentActivityNone = last {0} days / none
gb.dailyActivity = daily activity
gb.activeRepositories = active repositories
gb.activeAuthors = active authors
src/com/gitblit/wicket/WicketUtils.java
@@ -16,6 +16,7 @@
package com.gitblit.wicket;
import java.text.DateFormat;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
@@ -57,6 +58,12 @@
    public static void setCssStyle(Component container, String value) {
        container.add(new SimpleAttributeModifier("style", value));
    }
    public static void setCssBackground(Component container, String value) {
        String background = MessageFormat.format("background-color:{0};",
                StringUtils.getColor(value));
        container.add(new SimpleAttributeModifier("style", background));
    }
    public static void setHtmlTooltip(Component container, String value) {
@@ -129,7 +136,7 @@
            filename = "bullet_white.png";
            break;
        case PENDING:
        case NOCHANGE:
        case NOCHANGE:
        default:
            filename = "bullet_black.png";
        }
@@ -239,11 +246,11 @@
            }
        });
    }
    public static PageParameters newTokenParameter(String token) {
        return new PageParameters("t=" + token);
    }
    public static PageParameters newRegistrationParameter(String url, String name) {
        return new PageParameters("u=" + url + ",n=" + name);
    }
@@ -297,8 +304,8 @@
        if (StringUtils.isEmpty(objectId)) {
            return new PageParameters("r=" + repositoryName + ",f=" + path + ",pg=" + pageNumber);
        }
        return new PageParameters("r=" + repositoryName + ",h=" + objectId + ",f=" + path
                + ",pg=" + pageNumber);
        return new PageParameters("r=" + repositoryName + ",h=" + objectId + ",f=" + path + ",pg="
                + pageNumber);
    }
    public static PageParameters newBlobDiffParameter(String repositoryName, String baseCommitId,
@@ -373,7 +380,7 @@
    public static String getToken(PageParameters params) {
        return params.getString("t", "");
    }
    public static String getUrlParameter(PageParameters params) {
        return params.getString("u", "");
    }
@@ -411,7 +418,7 @@
        }
        return label;
    }
    public static Label createTimeLabel(String wicketId, Date date, TimeZone timeZone) {
        String format = GitBlit.getString(Keys.web.timeFormat, "HH:mm");
        DateFormat df = new SimpleDateFormat(format);
@@ -432,7 +439,7 @@
        }
        return label;
    }
    public static Label createDatestampLabel(String wicketId, Date date, TimeZone timeZone) {
        String format = GitBlit.getString(Keys.web.datestampLongFormat, "EEEE, MMMM d, yyyy");
        DateFormat df = new SimpleDateFormat(format);
src/com/gitblit/wicket/charting/GoogleCharts.java
@@ -1,5 +1,5 @@
/*
 Copyright 2011 comSysto GmbH
 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.
@@ -34,7 +34,7 @@
    private static final long serialVersionUID = 1L;
    private List<GoogleChart> charts = new ArrayList<GoogleChart>();
    public final List<GoogleChart> charts = new ArrayList<GoogleChart>();
    public void addChart(GoogleChart chart) {
        charts.add(chart);
src/com/gitblit/wicket/charting/GooglePieChart.java
@@ -18,6 +18,8 @@
import java.text.MessageFormat;
import java.util.Collections;
import com.gitblit.utils.StringUtils;
/**
 * Builds an interactive pie chart using the Visualization API.
 * 
@@ -43,13 +45,21 @@
        Collections.sort(values);
        StringBuilder colors = new StringBuilder("colors:[");
        for (int i = 0; i < values.size(); i++) {
            ChartValue value = values.get(i);
            colors.append('\'');
            colors.append(StringUtils.getColor(value.name));
            colors.append('\'');
            if (i < values.size() - 1) {
                colors.append(',');
            }
            line(sb, MessageFormat.format("{0}.setValue({1,number,0}, 0, ''{2}'');", dName, i,
                    value.name));
            line(sb, MessageFormat.format("{0}.setValue({1,number,0}, 1, {2,number,0.0});", dName,
                    i, value.value));
        }
        colors.append(']');
        // instantiate chart
        String cName = "chart_" + dataName;
@@ -58,8 +68,8 @@
                cName, tagId));
        line(sb,
                MessageFormat
                        .format("{0}.draw({1}, '{'width: {2,number,0}, height: {3,number,0}, chartArea:'{'left:20,top:20'}', title: ''{4}'' '}');",
                                cName, dName, width, height, title));
                        .format("{0}.draw({1}, '{'width: {2,number,0}, height: {3,number,0}, chartArea:'{'left:20,top:20'}', title: ''{4}'', {5} '}');",
                                cName, dName, width, height, title, colors.toString()));
        line(sb, "");
    }
}
src/com/gitblit/wicket/pages/ActivityPage.html
@@ -8,7 +8,7 @@
    <div class="page-header">
        <h2><wicket:message key="gb.recentActivity"></wicket:message><small> / <span wicket:id="subheader">[days back]</span></small></h2>
    </div>
    <div style="text-align: center;">
    <div style="height: 155px;text-align: center;">
        <span id="chartDaily"></span>        
        <span id="chartRepositories"></span>
        <span id="chartAuthors"></span>
src/com/gitblit/wicket/pages/ActivityPage.java
@@ -15,35 +15,27 @@
 */
package com.gitblit.wicket.pages;
import java.text.DateFormat;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.wicket.PageParameters;
import org.apache.wicket.behavior.HeaderContributor;
import org.apache.wicket.markup.html.basic.Label;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import com.gitblit.GitBlit;
import com.gitblit.models.DailyActivity;
import com.gitblit.models.Activity;
import com.gitblit.models.Metric;
import com.gitblit.models.RefModel;
import com.gitblit.models.RepositoryCommit;
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.UserModel;
import com.gitblit.utils.JGitUtils;
import com.gitblit.utils.ActivityUtils;
import com.gitblit.utils.StringUtils;
import com.gitblit.utils.TimeUtils;
import com.gitblit.wicket.GitBlitWebSession;
import com.gitblit.wicket.WicketUtils;
import com.gitblit.wicket.charting.GoogleChart;
@@ -64,16 +56,50 @@
    public ActivityPage(PageParameters params) {
        super();
        setupPage("", "");
        final UserModel user = GitBlitWebSession.get().getUser();
        // parameters
        int daysBack = WicketUtils.getDaysBack(params);
        if (daysBack < 1) {
            daysBack = 14;
        }
        }
        String objectId = WicketUtils.getObject(params);
        // determine repositories to view and retrieve the activity
        List<RepositoryModel> models = getRepositories(params);
        List<Activity> recentActivity = ActivityUtils.getRecentActivity(models, daysBack, objectId);
        if (recentActivity.size() == 0) {
            // no activity, skip graphs and activity panel
            add(new Label("subheader", MessageFormat.format(getString("gb.recentActivityNone"),
                    daysBack)));
            add(new Label("activityPanel"));
        } else {
            // calculate total commits and total authors
            int totalCommits = 0;
            Set<String> uniqueAuthors = new HashSet<String>();
            for (Activity activity : recentActivity) {
                totalCommits += activity.commits.size();
                uniqueAuthors.addAll(activity.getAuthorMetrics().keySet());
            }
            int totalAuthors = uniqueAuthors.size();
            // add the subheader with stat numbers
            add(new Label("subheader", MessageFormat.format(getString("gb.recentActivityStats"),
                    daysBack, totalCommits, totalAuthors)));
            // create the activity charts
            GoogleCharts charts = createCharts(recentActivity);
            add(new HeaderContributor(charts));
            // add activity panel
            add(new ActivityPanel("activityPanel", recentActivity));
        }
    }
    private List<RepositoryModel> getRepositories(PageParameters params) {
        final UserModel user = GitBlitWebSession.get().getUser();
        String set = WicketUtils.getSet(params);
        String repositoryName = WicketUtils.getRepositoryName(params);
        String objectId = WicketUtils.getObject(params);
        List<RepositoryModel> models = null;
        if (!StringUtils.isEmpty(repositoryName)) {
@@ -103,110 +129,41 @@
            }
            models = setModels;
        }
        return models;
    }
        // Activity panel shows last daysBack of activity across all
        // repositories.
        Date thresholdDate = new Date(System.currentTimeMillis() - daysBack * TimeUtils.ONEDAY);
        // Build a map of DailyActivity from the available repositories for the
        // specified threshold date.
        DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
        Calendar cal = Calendar.getInstance();
        Map<String, DailyActivity> activity = new HashMap<String, DailyActivity>();
        for (RepositoryModel model : models) {
            if (model.hasCommits && model.lastChange.after(thresholdDate)) {
                Repository repository = GitBlit.self().getRepository(model.name);
                List<RevCommit> commits = JGitUtils.getRevLog(repository, objectId, thresholdDate);
                Map<ObjectId, List<RefModel>> allRefs = JGitUtils.getAllRefs(repository);
                repository.close();
                // determine commit branch
                String branch = objectId;
                if (StringUtils.isEmpty(branch)) {
                    List<RefModel> headRefs = allRefs.get(commits.get(0).getId());
                    List<String> localBranches = new ArrayList<String>();
                    for (RefModel ref : headRefs) {
                        if (ref.getName().startsWith(Constants.R_HEADS)) {
                            localBranches.add(ref.getName().substring(Constants.R_HEADS.length()));
                        }
                    }
                    // determine branch
                    if (localBranches.size() == 1) {
                        // only one branch, choose it
                        branch = localBranches.get(0);
                    } else if (localBranches.size() > 1) {
                        if (localBranches.contains("master")) {
                            // choose master
                            branch = "master";
                        } else {
                            // choose first branch
                            branch = localBranches.get(0);
                        }
                    }
                }
                for (RevCommit commit : commits) {
                    Date date = JGitUtils.getCommitDate(commit);
                    String dateStr = df.format(date);
                    if (!activity.containsKey(dateStr)) {
                        // Normalize the date to midnight
                        cal.setTime(date);
                        cal.set(Calendar.HOUR_OF_DAY, 0);
                        cal.set(Calendar.MINUTE, 0);
                        cal.set(Calendar.SECOND, 0);
                        cal.set(Calendar.MILLISECOND, 0);
                        activity.put(dateStr, new DailyActivity(cal.getTime()));
                    }
                    RepositoryCommit commitModel = new RepositoryCommit(model.name, branch, commit);
                    commitModel.setRefs(allRefs.get(commit.getId()));
                    activity.get(dateStr).commits.add(commitModel);
                }
            }
        }
    /**
     * Creates the daily activity line chart, the active repositories pie chart,
     * and the active authors pie chart
     *
     * @param recentActivity
     * @return
     */
    private GoogleCharts createCharts(List<Activity> recentActivity) {
        // activity metrics
        Map<String, Metric> dayMetrics = new HashMap<String, Metric>();
        Map<String, Metric> repositoryMetrics = new HashMap<String, Metric>();
        Map<String, Metric> authorMetrics = new HashMap<String, Metric>();
        // prepare day metrics
        cal.setTimeInMillis(System.currentTimeMillis());
        for (int i = 0; i < daysBack; i++) {
            cal.add(Calendar.DATE, -1);
            String key = df.format(cal.getTime());
            dayMetrics.put(key, new Metric(key));
        }
        // aggregate repository and author metrics
        for (Activity activity : recentActivity) {
        // calculate activity metrics
        for (Map.Entry<String, DailyActivity> entry : activity.entrySet()) {
            // day metrics
            Metric day = dayMetrics.get(entry.getKey());
            day.count = entry.getValue().commits.size();
            for (RepositoryCommit commit : entry.getValue().commits) {
                // repository metrics
                String repository = commit.repository;
                if (!repositoryMetrics.containsKey(repository)) {
                    repositoryMetrics.put(repository, new Metric(repository));
                }
                repositoryMetrics.get(repository).count++;
                // author metrics
                String author = commit.getAuthorIdent().getEmailAddress().toLowerCase();
            // aggregate author metrics
            for (Map.Entry<String, Metric> entry : activity.getAuthorMetrics().entrySet()) {
                String author = entry.getKey();
                if (!authorMetrics.containsKey(author)) {
                    authorMetrics.put(author, new Metric(author));
                }
                authorMetrics.get(author).count++;
                authorMetrics.get(author).count += entry.getValue().count;
            }
        }
        // sort the activity groups and their commit contents
        int totalCommits = 0;
        List<DailyActivity> recentActivity = new ArrayList<DailyActivity>(activity.values());
        for (DailyActivity daily : recentActivity) {
            Collections.sort(daily.commits);
            totalCommits += daily.commits.size();
            // aggregate repository metrics
            for (Map.Entry<String, Metric> entry : activity.getRepositoryMetrics().entrySet()) {
                String repository = entry.getKey();
                if (!repositoryMetrics.containsKey(repository)) {
                    repositoryMetrics.put(repository, new Metric(repository));
                }
                repositoryMetrics.get(repository).count += entry.getValue().count;
            }
        }
        // build google charts
@@ -221,9 +178,9 @@
        // daily line chart
        GoogleChart chart = new GoogleLineChart("chartDaily", getString("gb.dailyActivity"), "day",
                getString("gb.commits"));
        df = new SimpleDateFormat("MMM dd");
        for (DailyActivity metric : recentActivity) {
            chart.addValue(df.format(metric.date), metric.commits.size());
        SimpleDateFormat df = new SimpleDateFormat("MMM dd");
        for (Activity metric : recentActivity) {
            chart.addValue(df.format(metric.startDate), metric.commits.size());
        }
        chart.setWidth(w);
        chart.setHeight(h);
@@ -249,12 +206,6 @@
        chart.setHeight(h);
        charts.addChart(chart);
        add(new HeaderContributor(charts));
        add(new Label("subheader", MessageFormat.format(getString("gb.recentActivitySubheader"),
                daysBack, totalCommits, authorMetrics.size())));
        // add activity panel
        add(new ActivityPanel("activityPanel", recentActivity));
        return charts;
    }
}
src/com/gitblit/wicket/panels/ActivityPanel.html
@@ -16,6 +16,9 @@
    
    <wicket:fragment wicket:id="commitFragment">
        <td class="date" style="width:40px; vertical-align: middle;" ><span wicket:id="time">[time of day]</span></td>
        <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 class="author" style="vertical-align: middle;">
            <img wicket:id="commitIcon" style="vertical-align: middle;"></img>
@@ -24,7 +27,6 @@
        </td>
        <td style="text-align:right;vertical-align: middle;">
            <div wicket:id="commitRefs">[commit refs]</div>
            <span wicket:id="repository">[repository link]</span>
        </td>
        <td class="rightAlign" style="width:7em;vertical-align: middle;">
            <span class="link">
src/com/gitblit/wicket/panels/ActivityPanel.java
@@ -25,8 +25,9 @@
import org.apache.wicket.markup.repeater.data.ListDataProvider;
import com.gitblit.Constants;
import com.gitblit.models.DailyActivity;
import com.gitblit.models.RepositoryCommit;
import com.gitblit.models.Activity;
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;
@@ -47,18 +48,18 @@
    private static final long serialVersionUID = 1L;
    public ActivityPanel(String wicketId, List<DailyActivity> recentActivity) {
    public ActivityPanel(String wicketId, List<Activity> recentActivity) {
        super(wicketId);
        Collections.sort(recentActivity);
        DataView<DailyActivity> activityView = new DataView<DailyActivity>("activity",
                new ListDataProvider<DailyActivity>(recentActivity)) {
        DataView<Activity> activityView = new DataView<Activity>("activity",
                new ListDataProvider<Activity>(recentActivity)) {
            private static final long serialVersionUID = 1L;
            public void populateItem(final Item<DailyActivity> item) {
                final DailyActivity entry = item.getModelObject();
                item.add(WicketUtils.createDatestampLabel("title", entry.date, GitBlitWebSession
            public void populateItem(final Item<Activity> item) {
                final Activity entry = item.getModelObject();
                item.add(WicketUtils.createDatestampLabel("title", entry.startDate, GitBlitWebSession
                        .get().getTimezone()));
                // display the commits in chronological order
@@ -93,10 +94,11 @@
                        setPersonSearchTooltip(authorLink, author, Constants.SearchType.AUTHOR);
                        fragment.add(authorLink);
                        // repository summary page link
                        LinkPanel repositoryLink = new LinkPanel("repository", "list",
                        // repository
                        LinkPanel repositoryLink = new LinkPanel("repository", null,
                                commit.repository, SummaryPage.class,
                                WicketUtils.newRepositoryParameter(commit.repository));
                        WicketUtils.setCssBackground(repositoryLink, commit.repository);
                        fragment.add(repositoryLink);
                        // repository branch
@@ -113,9 +115,13 @@
                        // message/commit link
                        String shortMessage = commit.getShortMessage();
                        LinkPanel shortlog = new LinkPanel("message", "list subject", shortMessage,
                                CommitPage.class, WicketUtils.newObjectParameter(commit.repository,
                                        commit.getName()));
                        String trimmedMessage = StringUtils.trimShortLog(shortMessage);
                        LinkPanel shortlog = new LinkPanel("message", "list subject",
                                trimmedMessage, CommitPage.class, WicketUtils.newObjectParameter(
                                        commit.repository, commit.getName()));
                        if (!shortMessage.equals(trimmedMessage)) {
                            WicketUtils.setHtmlTooltip(shortlog, shortMessage);
                        }
                        fragment.add(shortlog);
                        // refs