summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--distrib/gitblit.properties17
-rw-r--r--docs/04_releases.mkd5
-rw-r--r--src/com/gitblit/models/DailyActivity.java47
-rw-r--r--src/com/gitblit/models/RepositoryCommit.java86
-rw-r--r--src/com/gitblit/wicket/GitBlitWebApp.java3
-rw-r--r--src/com/gitblit/wicket/GitBlitWebApp.properties8
-rw-r--r--src/com/gitblit/wicket/WicketUtils.java59
-rw-r--r--src/com/gitblit/wicket/pages/ActivityPage.html19
-rw-r--r--src/com/gitblit/wicket/pages/ActivityPage.java260
-rw-r--r--src/com/gitblit/wicket/pages/RootPage.java1
-rw-r--r--src/com/gitblit/wicket/panels/ActivityPanel.html38
-rw-r--r--src/com/gitblit/wicket/panels/ActivityPanel.java142
12 files changed, 684 insertions, 1 deletions
diff --git a/distrib/gitblit.properties b/distrib/gitblit.properties
index 083a658e..5c50a2ce 100644
--- a/distrib/gitblit.properties
+++ b/distrib/gitblit.properties
@@ -161,12 +161,23 @@ web.repositoriesMessage = gitblit
# RESTART REQUIRED
web.useClientTimezone = false
+# Time format
+# <http://download.oracle.com/javase/6/docs/api/java/text/SimpleDateFormat.html>
+#
+# SINCE 0.8.0
+web.timeFormat = HH:mm
+
# Short date format
# <http://download.oracle.com/javase/6/docs/api/java/text/SimpleDateFormat.html>
#
# SINCE 0.5.0
web.datestampShortFormat = yyyy-MM-dd
+# Long date format
+#
+# SINCE 0.8.0
+web.datestampLongFormat = EEEE, MMMM d, yyyy
+
# Long timestamp format
# <http://download.oracle.com/javase/6/docs/api/java/text/SimpleDateFormat.html>
#
@@ -239,6 +250,12 @@ web.showSearchTypeSelection = false
# SINCE 0.5.0
web.generateActivityGraph = true
+# The number of days to show on the activity page.
+# Value must exceed 0 else default of 14 is used
+#
+# SINCE 0.8.0
+web.activityDuration = 14
+
# The number of commits to display on the summary page
# Value must exceed 0 else default of 20 is used
#
diff --git a/docs/04_releases.mkd b/docs/04_releases.mkd
index 5719b191..22ecce78 100644
--- a/docs/04_releases.mkd
+++ b/docs/04_releases.mkd
@@ -5,6 +5,11 @@
- added: optional Gravatar integration
**New:** *web.allowGravatar = true*
+- added: multi-repository activity page. this is a timeline of commit activity over the last N days for one or more repositories.
+ **New:** *web.activityDuration = 14*
+ **New:** *web.timeFormat = HH:mm*
+ **New:** *web.datestampLongFormat = EEEE, MMMM d, yyyy*
+
### Older Releases
diff --git a/src/com/gitblit/models/DailyActivity.java b/src/com/gitblit/models/DailyActivity.java
new file mode 100644
index 00000000..f2e816cb
--- /dev/null
+++ b/src/com/gitblit/models/DailyActivity.java
@@ -0,0 +1,47 @@
+/*
+ * 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.List;
+
+/**
+ * Model class to represent the commit activity across many repositories. This
+ * class is used by the Activity page.
+ *
+ * @author James Moger
+ */
+public class DailyActivity implements Serializable, Comparable<DailyActivity> {
+
+ private static final long serialVersionUID = 1L;
+
+ public final Date date;
+
+ public final List<RepositoryCommit> commits;
+
+ public DailyActivity(Date date) {
+ this.date = date;
+ commits = new ArrayList<RepositoryCommit>();
+ }
+
+ @Override
+ public int compareTo(DailyActivity o) {
+ // reverse chronological order
+ return o.date.compareTo(date);
+ }
+}
diff --git a/src/com/gitblit/models/RepositoryCommit.java b/src/com/gitblit/models/RepositoryCommit.java
new file mode 100644
index 00000000..8f5b2bd0
--- /dev/null
+++ b/src/com/gitblit/models/RepositoryCommit.java
@@ -0,0 +1,86 @@
+/*
+ * 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;
+
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.revwalk.RevCommit;
+
+/**
+ * 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 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;
+ }
+} \ No newline at end of file
diff --git a/src/com/gitblit/wicket/GitBlitWebApp.java b/src/com/gitblit/wicket/GitBlitWebApp.java
index 301adb32..79083efb 100644
--- a/src/com/gitblit/wicket/GitBlitWebApp.java
+++ b/src/com/gitblit/wicket/GitBlitWebApp.java
@@ -25,6 +25,7 @@ import org.apache.wicket.protocol.http.WebApplication;
import com.gitblit.GitBlit;
import com.gitblit.Keys;
+import com.gitblit.wicket.pages.ActivityPage;
import com.gitblit.wicket.pages.BlamePage;
import com.gitblit.wicket.pages.BlobDiffPage;
import com.gitblit.wicket.pages.BlobPage;
@@ -103,6 +104,8 @@ public class GitBlitWebApp extends WebApplication {
// federation urls
mount("/proposal", ReviewProposalPage.class, "t");
mount("/registration", FederationRegistrationPage.class, "u", "n");
+
+ mount("/activity", ActivityPage.class, "r", "h");
}
private void mount(String location, Class<? extends WebPage> clazz, String... parameters) {
diff --git a/src/com/gitblit/wicket/GitBlitWebApp.properties b/src/com/gitblit/wicket/GitBlitWebApp.properties
index 51938433..f157a7e5 100644
--- a/src/com/gitblit/wicket/GitBlitWebApp.properties
+++ b/src/com/gitblit/wicket/GitBlitWebApp.properties
@@ -180,4 +180,10 @@ gb.date = date
gb.activity = activity
gb.subscribe = subscribe
gb.branch = branch
-gb.maxHits = max hits \ No newline at end of file
+gb.maxHits = max hits
+gb.recentActivity = recent activity
+gb.recentActivitySubheader = last {0} days / {1} commits by {2} authors
+gb.dailyActivity = daily activity
+gb.activeRepositories = active repositories
+gb.activeAuthors = active authors
+gb.commits = commits \ No newline at end of file
diff --git a/src/com/gitblit/wicket/WicketUtils.java b/src/com/gitblit/wicket/WicketUtils.java
index 37b44472..6157a44f 100644
--- a/src/com/gitblit/wicket/WicketUtils.java
+++ b/src/com/gitblit/wicket/WicketUtils.java
@@ -358,6 +358,14 @@ public class WicketUtils {
return params.getInt("pg", 1);
}
+ public static String getSet(PageParameters params) {
+ return params.getString("set", "");
+ }
+
+ public static int getDaysBack(PageParameters params) {
+ return params.getInt("db", 14);
+ }
+
public static String getUsername(PageParameters params) {
return params.getString("user", "");
}
@@ -403,6 +411,57 @@ public class WicketUtils {
}
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);
+ if (timeZone != null) {
+ df.setTimeZone(timeZone);
+ }
+ String timeString;
+ if (date.getTime() == 0) {
+ timeString = "--";
+ } else {
+ timeString = df.format(date);
+ }
+ String title = TimeUtils.timeAgo(date);
+ Label label = new Label(wicketId, timeString);
+ WicketUtils.setCssClass(label, TimeUtils.timeAgoCss(date));
+ if (!StringUtils.isEmpty(title)) {
+ WicketUtils.setHtmlTooltip(label, title);
+ }
+ 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);
+ if (timeZone != null) {
+ df.setTimeZone(timeZone);
+ }
+ String dateString;
+ if (date.getTime() == 0) {
+ dateString = "--";
+ } else {
+ dateString = df.format(date);
+ }
+ String title = null;
+ if (date.getTime() <= System.currentTimeMillis()) {
+ // past
+ title = TimeUtils.timeAgo(date);
+ }
+ if ((System.currentTimeMillis() - date.getTime()) < 10 * 24 * 60 * 60 * 1000L) {
+ String tmp = dateString;
+ dateString = title;
+ title = tmp;
+ }
+ Label label = new Label(wicketId, dateString);
+ WicketUtils.setCssClass(label, TimeUtils.timeAgoCss(date));
+ if (!StringUtils.isEmpty(title)) {
+ WicketUtils.setHtmlTooltip(label, title);
+ }
+ return label;
+ }
public static Label createTimestampLabel(String wicketId, Date date, TimeZone timeZone) {
String format = GitBlit.getString(Keys.web.datetimestampLongFormat,
diff --git a/src/com/gitblit/wicket/pages/ActivityPage.html b/src/com/gitblit/wicket/pages/ActivityPage.html
new file mode 100644
index 00000000..c86028f2
--- /dev/null
+++ b/src/com/gitblit/wicket/pages/ActivityPage.html
@@ -0,0 +1,19 @@
+<!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><wicket:message key="gb.recentActivity"></wicket:message><small> / <span wicket:id="subheader">[days back]</span></small></h2>
+ </div>
+ <div style="text-align: center;">
+ <span id="chartDaily"></span>
+ <span id="chartRepositories"></span>
+ <span id="chartAuthors"></span>
+ </div>
+ <div wicket:id="activityPanel" style="padding-top:5px;" >[activity panel]</div>
+</wicket:extend>
+</body>
+</html> \ No newline at end of file
diff --git a/src/com/gitblit/wicket/pages/ActivityPage.java b/src/com/gitblit/wicket/pages/ActivityPage.java
new file mode 100644
index 00000000..53c7f41c
--- /dev/null
+++ b/src/com/gitblit/wicket/pages/ActivityPage.java
@@ -0,0 +1,260 @@
+/*
+ * 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.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.List;
+import java.util.Map;
+
+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.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.StringUtils;
+import com.gitblit.utils.TimeUtils;
+import com.gitblit.wicket.GitBlitWebSession;
+import com.gitblit.wicket.WicketUtils;
+import com.gitblit.wicket.charting.GoogleChart;
+import com.gitblit.wicket.charting.GoogleCharts;
+import com.gitblit.wicket.charting.GoogleLineChart;
+import com.gitblit.wicket.charting.GooglePieChart;
+import com.gitblit.wicket.panels.ActivityPanel;
+
+/**
+ * Activity Page shows a list of recent commits across all visible Gitblit
+ * repositories.
+ *
+ * @author James Moger
+ *
+ */
+public class ActivityPage extends RootPage {
+
+ public ActivityPage(PageParameters params) {
+ super();
+ setupPage("", "");
+ final UserModel user = GitBlitWebSession.get().getUser();
+
+ // parameters
+ int daysBack = WicketUtils.getDaysBack(params);
+ if (daysBack < 1) {
+ daysBack = 14;
+ }
+ String set = WicketUtils.getSet(params);
+ String repositoryName = WicketUtils.getRepositoryName(params);
+ String objectId = WicketUtils.getObject(params);
+
+ List<RepositoryModel> models = null;
+ if (!StringUtils.isEmpty(repositoryName)) {
+ // named repository
+ models = new ArrayList<RepositoryModel>();
+ RepositoryModel model = GitBlit.self().getRepositoryModel(repositoryName);
+ if (user.canAccessRepository(model)) {
+ models.add(model);
+ }
+ }
+
+ // get all user accessible repositories
+ if (models == null) {
+ models = GitBlit.self().getRepositoryModels(user);
+ }
+
+ // filter the repositories by the specified set
+ if (!StringUtils.isEmpty(set)) {
+ List<String> sets = StringUtils.getStringsFromValue(set, ",");
+ List<RepositoryModel> setModels = new ArrayList<RepositoryModel>();
+ for (RepositoryModel model : models) {
+ for (String curr : sets) {
+ if (model.federationSets.contains(curr)) {
+ setModels.add(model);
+ }
+ }
+ }
+ models = setModels;
+ }
+
+ // 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);
+ }
+ }
+ }
+
+ // 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));
+ }
+
+ // 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();
+ if (!authorMetrics.containsKey(author)) {
+ authorMetrics.put(author, new Metric(author));
+ }
+ authorMetrics.get(author).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();
+ }
+
+ // build google charts
+ int w = 310;
+ int h = 150;
+ GoogleCharts charts = new GoogleCharts();
+
+ // sort in reverse-chronological order and then reverse that
+ Collections.sort(recentActivity);
+ Collections.reverse(recentActivity);
+
+ // 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());
+ }
+ chart.setWidth(w);
+ chart.setHeight(h);
+ charts.addChart(chart);
+
+ // active repositories pie chart
+ chart = new GooglePieChart("chartRepositories", getString("gb.activeRepositories"),
+ getString("gb.repository"), getString("gb.commits"));
+ for (Metric metric : repositoryMetrics.values()) {
+ chart.addValue(metric.name, metric.count);
+ }
+ chart.setWidth(w);
+ chart.setHeight(h);
+ charts.addChart(chart);
+
+ // active authors pie chart
+ chart = new GooglePieChart("chartAuthors", getString("gb.activeAuthors"),
+ getString("gb.author"), getString("gb.commits"));
+ for (Metric metric : authorMetrics.values()) {
+ chart.addValue(metric.name, metric.count);
+ }
+ chart.setWidth(w);
+ 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));
+ }
+}
diff --git a/src/com/gitblit/wicket/pages/RootPage.java b/src/com/gitblit/wicket/pages/RootPage.java
index f00c0411..06ab2985 100644
--- a/src/com/gitblit/wicket/pages/RootPage.java
+++ b/src/com/gitblit/wicket/pages/RootPage.java
@@ -82,6 +82,7 @@ public abstract class RootPage extends BasePage {
// navigation links
List<PageRegistration> pages = new ArrayList<PageRegistration>();
pages.add(new PageRegistration("gb.repositories", RepositoriesPage.class));
+ pages.add(new PageRegistration("gb.activity", ActivityPage.class));
if (showAdmin) {
pages.add(new PageRegistration("gb.users", UsersPage.class));
}
diff --git a/src/com/gitblit/wicket/panels/ActivityPanel.html b/src/com/gitblit/wicket/panels/ActivityPanel.html
new file mode 100644
index 00000000..703dc009
--- /dev/null
+++ b/src/com/gitblit/wicket/panels/ActivityPanel.html
@@ -0,0 +1,38 @@
+<!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:panel>
+
+ <div wicket:id="activity">
+ <div class="header"><span wicket:id="title">[title]</span></div>
+ <table wicket:id="commits">
+ <tr wicket:id="commit"></tr>
+ </table>
+ </div>
+
+ <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: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>
+ <span wicket:id="message">[shortlog commit link]</span><br/>
+ <span wicket:id="author" style="padding-left:20px;">[author link]</span> committed <span wicket:id="commitid">[commit id]</span> to <span wicket:id="branch"></span>
+ </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">
+ <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>
+ </span>
+ </td>
+ </wicket:fragment>
+
+</wicket:panel>
+</body>
+</html> \ No newline at end of file
diff --git a/src/com/gitblit/wicket/panels/ActivityPanel.java b/src/com/gitblit/wicket/panels/ActivityPanel.java
new file mode 100644
index 00000000..128ef2bd
--- /dev/null
+++ b/src/com/gitblit/wicket/panels/ActivityPanel.java
@@ -0,0 +1,142 @@
+/*
+ * 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.util.Collections;
+import java.util.List;
+
+import org.apache.wicket.markup.html.link.BookmarkablePageLink;
+import org.apache.wicket.markup.html.panel.Fragment;
+import org.apache.wicket.markup.repeater.Item;
+import org.apache.wicket.markup.repeater.data.DataView;
+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.wicket.GitBlitWebSession;
+import com.gitblit.wicket.GravatarImage;
+import com.gitblit.wicket.WicketUtils;
+import com.gitblit.wicket.pages.CommitDiffPage;
+import com.gitblit.wicket.pages.CommitPage;
+import com.gitblit.wicket.pages.LogPage;
+import com.gitblit.wicket.pages.SearchPage;
+import com.gitblit.wicket.pages.SummaryPage;
+import com.gitblit.wicket.pages.TreePage;
+
+/**
+ * Renders activity in day-blocks in reverse-chronological order.
+ *
+ * @author James Moger
+ *
+ */
+public class ActivityPanel extends BasePanel {
+
+ private static final long serialVersionUID = 1L;
+
+ public ActivityPanel(String wicketId, List<DailyActivity> recentActivity) {
+ super(wicketId);
+
+ Collections.sort(recentActivity);
+
+ DataView<DailyActivity> activityView = new DataView<DailyActivity>("activity",
+ new ListDataProvider<DailyActivity>(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
+ .get().getTimezone()));
+
+ // display the commits in chronological order
+ DataView<RepositoryCommit> commits = new DataView<RepositoryCommit>("commits",
+ new ListDataProvider<RepositoryCommit>(entry.commits)) {
+ private static final long serialVersionUID = 1L;
+
+ public void populateItem(final Item<RepositoryCommit> item) {
+ final RepositoryCommit commit = item.getModelObject();
+ Fragment fragment = new Fragment("commit", "commitFragment", this);
+
+ // time of day
+ fragment.add(WicketUtils.createTimeLabel("time", commit.getAuthorIdent()
+ .getWhen(), GitBlitWebSession.get().getTimezone()));
+
+ // avatar
+ fragment.add(new GravatarImage("avatar", commit.getAuthorIdent(), 36));
+
+ // merge icon
+ if (commit.getParentCount() > 1) {
+ fragment.add(WicketUtils.newImage("commitIcon",
+ "commit_merge_16x16.png"));
+ } else {
+ fragment.add(WicketUtils.newBlankImage("commitIcon"));
+ }
+
+ // author search link
+ String author = commit.getAuthorIdent().getName();
+ LinkPanel authorLink = new LinkPanel("author", "list", author,
+ SearchPage.class, WicketUtils.newSearchParameter(commit.repository,
+ commit.getName(), author, Constants.SearchType.AUTHOR));
+ setPersonSearchTooltip(authorLink, author, Constants.SearchType.AUTHOR);
+ fragment.add(authorLink);
+
+ // repository summary page link
+ LinkPanel repositoryLink = new LinkPanel("repository", "list",
+ commit.repository, SummaryPage.class,
+ WicketUtils.newRepositoryParameter(commit.repository));
+ fragment.add(repositoryLink);
+
+ // repository branch
+ LinkPanel branchLink = new LinkPanel("branch", "list", commit.branch,
+ LogPage.class, WicketUtils.newObjectParameter(commit.repository,
+ commit.branch));
+ 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()));
+ fragment.add(commitid);
+
+ // message/commit link
+ String shortMessage = commit.getShortMessage();
+ LinkPanel shortlog = new LinkPanel("message", "list subject", shortMessage,
+ CommitPage.class, WicketUtils.newObjectParameter(commit.repository,
+ commit.getName()));
+ fragment.add(shortlog);
+
+ // refs
+ fragment.add(new RefsPanel("commitRefs", commit.repository, commit
+ .getRefs()));
+
+ // view, diff, tree links
+ fragment.add(new BookmarkablePageLink<Void>("view", CommitPage.class,
+ WicketUtils.newObjectParameter(commit.repository, commit.getName())));
+ fragment.add(new BookmarkablePageLink<Void>("diff", CommitDiffPage.class,
+ WicketUtils.newObjectParameter(commit.repository, commit.getName()))
+ .setEnabled(commit.getParentCount() > 0));
+ fragment.add(new BookmarkablePageLink<Void>("tree", TreePage.class,
+ WicketUtils.newObjectParameter(commit.repository, commit.getName())));
+
+ item.add(fragment);
+ }
+ };
+ item.add(commits);
+ }
+ };
+ add(activityView);
+ }
+}