]> source.dussan.org Git - gitblit.git/commitdiff
Working draft of the aggregate activity page
authorJames Moger <james.moger@gitblit.com>
Thu, 17 Nov 2011 03:43:37 +0000 (22:43 -0500)
committerJames Moger <james.moger@gitblit.com>
Thu, 17 Nov 2011 03:43:37 +0000 (22:43 -0500)
12 files changed:
distrib/gitblit.properties
docs/04_releases.mkd
src/com/gitblit/models/DailyActivity.java [new file with mode: 0644]
src/com/gitblit/models/RepositoryCommit.java [new file with mode: 0644]
src/com/gitblit/wicket/GitBlitWebApp.java
src/com/gitblit/wicket/GitBlitWebApp.properties
src/com/gitblit/wicket/WicketUtils.java
src/com/gitblit/wicket/pages/ActivityPage.html [new file with mode: 0644]
src/com/gitblit/wicket/pages/ActivityPage.java [new file with mode: 0644]
src/com/gitblit/wicket/pages/RootPage.java
src/com/gitblit/wicket/panels/ActivityPanel.html [new file with mode: 0644]
src/com/gitblit/wicket/panels/ActivityPanel.java [new file with mode: 0644]

index 083a658e2a0166b49f7ab43077736753dd414af8..5c50a2ce87bfde20c71878c144a617611bca1090 100644 (file)
@@ -161,12 +161,23 @@ web.repositoriesMessage = gitblit
 # RESTART REQUIRED\r
 web.useClientTimezone = false\r
 \r
+# Time format\r
+# <http://download.oracle.com/javase/6/docs/api/java/text/SimpleDateFormat.html>\r
+#\r
+# SINCE 0.8.0\r
+web.timeFormat = HH:mm\r
+\r
 # Short date format\r
 # <http://download.oracle.com/javase/6/docs/api/java/text/SimpleDateFormat.html>\r
 #\r
 # SINCE 0.5.0\r
 web.datestampShortFormat = yyyy-MM-dd\r
 \r
+# Long date format\r
+#\r
+# SINCE 0.8.0\r
+web.datestampLongFormat = EEEE, MMMM d, yyyy\r
+\r
 # Long timestamp format\r
 # <http://download.oracle.com/javase/6/docs/api/java/text/SimpleDateFormat.html>\r
 #\r
@@ -239,6 +250,12 @@ web.showSearchTypeSelection = false
 # SINCE 0.5.0 \r
 web.generateActivityGraph = true\r
 \r
+# The number of days to show on the activity page.\r
+# Value must exceed 0 else default of 14 is used\r
+#\r
+# SINCE 0.8.0\r
+web.activityDuration = 14\r
+\r
 # The number of commits to display on the summary page\r
 # Value must exceed 0 else default of 20 is used\r
 #\r
index 5719b1915c5ba6d72ee888bb1452746bc72a2c06..22ecce7824c5ad12c81dbff0e7165eaff390dddd 100644 (file)
@@ -5,6 +5,11 @@
 \r
 - added: optional Gravatar integration  \r
     **New:** *web.allowGravatar = true*   \r
+- added: multi-repository activity page.  this is a timeline of commit activity over the last N days for one or more repositories.\r
+   **New:** *web.activityDuration = 14*  \r
+   **New:** *web.timeFormat = HH:mm*  \r
+   **New:** *web.datestampLongFormat = EEEE, MMMM d, yyyy*  \r
+\r
 \r
 ### Older Releases\r
 \r
diff --git a/src/com/gitblit/models/DailyActivity.java b/src/com/gitblit/models/DailyActivity.java
new file mode 100644 (file)
index 0000000..f2e816c
--- /dev/null
@@ -0,0 +1,47 @@
+/*\r
+ * Copyright 2011 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *     http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit.models;\r
+\r
+import java.io.Serializable;\r
+import java.util.ArrayList;\r
+import java.util.Date;\r
+import java.util.List;\r
+\r
+/**\r
+ * Model class to represent the commit activity across many repositories. This\r
+ * class is used by the Activity page.\r
+ * \r
+ * @author James Moger\r
+ */\r
+public class DailyActivity implements Serializable, Comparable<DailyActivity> {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       public final Date date;\r
+\r
+       public final List<RepositoryCommit> commits;\r
+\r
+       public DailyActivity(Date date) {\r
+               this.date = date;\r
+               commits = new ArrayList<RepositoryCommit>();\r
+       }\r
+\r
+       @Override\r
+       public int compareTo(DailyActivity o) {\r
+               // reverse chronological order\r
+               return o.date.compareTo(date);\r
+       }\r
+}\r
diff --git a/src/com/gitblit/models/RepositoryCommit.java b/src/com/gitblit/models/RepositoryCommit.java
new file mode 100644 (file)
index 0000000..8f5b2bd
--- /dev/null
@@ -0,0 +1,86 @@
+/*\r
+ * Copyright 2011 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *     http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit.models;\r
+\r
+import java.io.Serializable;\r
+import java.util.List;\r
+\r
+import org.eclipse.jgit.lib.PersonIdent;\r
+import org.eclipse.jgit.revwalk.RevCommit;\r
+\r
+/**\r
+ * Model class to represent a RevCommit, it's source repository, and the branch.\r
+ * This class is used by the activity page.\r
+ * \r
+ * @author James Moger\r
+ */\r
+public class RepositoryCommit implements Serializable, Comparable<RepositoryCommit> {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       public final String repository;\r
+\r
+       public final String branch;\r
+\r
+       private final RevCommit commit;\r
+\r
+       private List<RefModel> refs;\r
+\r
+       public RepositoryCommit(String repository, String branch, RevCommit commit) {\r
+               this.repository = repository;\r
+               this.branch = branch;\r
+               this.commit = commit;\r
+       }\r
+\r
+       public void setRefs(List<RefModel> refs) {\r
+               this.refs = refs;\r
+       }\r
+\r
+       public List<RefModel> getRefs() {\r
+               return refs;\r
+       }\r
+\r
+       public String getName() {\r
+               return commit.getName();\r
+       }\r
+\r
+       public String getShortName() {\r
+               return commit.getName().substring(0, 8);\r
+       }\r
+\r
+       public String getShortMessage() {\r
+               return commit.getShortMessage();\r
+       }\r
+\r
+       public int getParentCount() {\r
+               return commit.getParentCount();\r
+       }\r
+\r
+       public PersonIdent getAuthorIdent() {\r
+               return commit.getAuthorIdent();\r
+       }\r
+\r
+       @Override\r
+       public int compareTo(RepositoryCommit o) {\r
+               // reverse-chronological order\r
+               if (commit.getCommitTime() > o.commit.getCommitTime()) {\r
+                       return -1;\r
+               } else if (commit.getCommitTime() < o.commit.getCommitTime()) {\r
+                       return 1;\r
+               }\r
+               return 0;\r
+       }\r
+}
\ No newline at end of file
index 301adb3272fc18eb93b0bc6d62dd7788fd95d560..79083efbf6743b6435a07f19401a5c0d1c43be57 100644 (file)
@@ -25,6 +25,7 @@ import org.apache.wicket.protocol.http.WebApplication;
 \r
 import com.gitblit.GitBlit;\r
 import com.gitblit.Keys;\r
+import com.gitblit.wicket.pages.ActivityPage;\r
 import com.gitblit.wicket.pages.BlamePage;\r
 import com.gitblit.wicket.pages.BlobDiffPage;\r
 import com.gitblit.wicket.pages.BlobPage;\r
@@ -103,6 +104,8 @@ public class GitBlitWebApp extends WebApplication {
                // federation urls\r
                mount("/proposal", ReviewProposalPage.class, "t");\r
                mount("/registration", FederationRegistrationPage.class, "u", "n");\r
+               \r
+               mount("/activity", ActivityPage.class, "r", "h");\r
        }\r
 \r
        private void mount(String location, Class<? extends WebPage> clazz, String... parameters) {\r
index 51938433f0bac59e89b27bb88e40cd38bd61cd11..f157a7e578f531fc730e321938a5ebde2b5a9c14 100644 (file)
@@ -180,4 +180,10 @@ gb.date = date
 gb.activity = activity\r
 gb.subscribe = subscribe\r
 gb.branch = branch\r
-gb.maxHits = max hits
\ No newline at end of file
+gb.maxHits = max hits\r
+gb.recentActivity = recent activity\r
+gb.recentActivitySubheader = last {0} days / {1} commits by {2} authors\r
+gb.dailyActivity = daily activity\r
+gb.activeRepositories = active repositories\r
+gb.activeAuthors = active authors\r
+gb.commits = commits
\ No newline at end of file
index 37b44472fbd798df1e703f6d69240484944f148c..6157a44f3375192f3bfe2598f27fde38f13bfc10 100644 (file)
@@ -358,6 +358,14 @@ public class WicketUtils {
                return params.getInt("pg", 1);\r
        }\r
 \r
+       public static String getSet(PageParameters params) {\r
+               return params.getString("set", "");\r
+       }\r
+\r
+       public static int getDaysBack(PageParameters params) {\r
+               return params.getInt("db", 14);\r
+       }\r
+\r
        public static String getUsername(PageParameters params) {\r
                return params.getString("user", "");\r
        }\r
@@ -403,6 +411,57 @@ public class WicketUtils {
                }\r
                return label;\r
        }\r
+       \r
+       public static Label createTimeLabel(String wicketId, Date date, TimeZone timeZone) {\r
+               String format = GitBlit.getString(Keys.web.timeFormat, "HH:mm");\r
+               DateFormat df = new SimpleDateFormat(format);\r
+               if (timeZone != null) {\r
+                       df.setTimeZone(timeZone);\r
+               }\r
+               String timeString;\r
+               if (date.getTime() == 0) {\r
+                       timeString = "--";\r
+               } else {\r
+                       timeString = df.format(date);\r
+               }\r
+               String title = TimeUtils.timeAgo(date);\r
+               Label label = new Label(wicketId, timeString);\r
+               WicketUtils.setCssClass(label, TimeUtils.timeAgoCss(date));\r
+               if (!StringUtils.isEmpty(title)) {\r
+                       WicketUtils.setHtmlTooltip(label, title);\r
+               }\r
+               return label;\r
+       }\r
+       \r
+       public static Label createDatestampLabel(String wicketId, Date date, TimeZone timeZone) {\r
+               String format = GitBlit.getString(Keys.web.datestampLongFormat, "EEEE, MMMM d, yyyy");\r
+               DateFormat df = new SimpleDateFormat(format);\r
+               if (timeZone != null) {\r
+                       df.setTimeZone(timeZone);\r
+               }\r
+               String dateString;\r
+               if (date.getTime() == 0) {\r
+                       dateString = "--";\r
+               } else {\r
+                       dateString = df.format(date);\r
+               }\r
+               String title = null;\r
+               if (date.getTime() <= System.currentTimeMillis()) {\r
+                       // past\r
+                       title = TimeUtils.timeAgo(date);\r
+               }\r
+               if ((System.currentTimeMillis() - date.getTime()) < 10 * 24 * 60 * 60 * 1000L) {\r
+                       String tmp = dateString;\r
+                       dateString = title;\r
+                       title = tmp;\r
+               }\r
+               Label label = new Label(wicketId, dateString);\r
+               WicketUtils.setCssClass(label, TimeUtils.timeAgoCss(date));\r
+               if (!StringUtils.isEmpty(title)) {\r
+                       WicketUtils.setHtmlTooltip(label, title);\r
+               }\r
+               return label;\r
+       }\r
 \r
        public static Label createTimestampLabel(String wicketId, Date date, TimeZone timeZone) {\r
                String format = GitBlit.getString(Keys.web.datetimestampLongFormat,\r
diff --git a/src/com/gitblit/wicket/pages/ActivityPage.html b/src/com/gitblit/wicket/pages/ActivityPage.html
new file mode 100644 (file)
index 0000000..c86028f
--- /dev/null
@@ -0,0 +1,19 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\r
+<html xmlns="http://www.w3.org/1999/xhtml"  \r
+      xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"  \r
+      xml:lang="en"  \r
+      lang="en"> \r
+<body>\r
+<wicket:extend>\r
+       <div class="page-header">\r
+               <h2><wicket:message key="gb.recentActivity"></wicket:message><small> / <span wicket:id="subheader">[days back]</span></small></h2>\r
+       </div>\r
+       <div style="text-align: center;">\r
+               <span id="chartDaily"></span>           \r
+               <span id="chartRepositories"></span>\r
+               <span id="chartAuthors"></span>\r
+       </div>\r
+       <div wicket:id="activityPanel" style="padding-top:5px;" >[activity panel]</div>\r
+</wicket:extend>\r
+</body>\r
+</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 (file)
index 0000000..53c7f41
--- /dev/null
@@ -0,0 +1,260 @@
+/*\r
+ * Copyright 2011 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *     http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit.wicket.pages;\r
+\r
+import java.text.DateFormat;\r
+import java.text.MessageFormat;\r
+import java.text.SimpleDateFormat;\r
+import java.util.ArrayList;\r
+import java.util.Calendar;\r
+import java.util.Collections;\r
+import java.util.Date;\r
+import java.util.HashMap;\r
+import java.util.List;\r
+import java.util.Map;\r
+\r
+import org.apache.wicket.PageParameters;\r
+import org.apache.wicket.behavior.HeaderContributor;\r
+import org.apache.wicket.markup.html.basic.Label;\r
+import org.eclipse.jgit.lib.Constants;\r
+import org.eclipse.jgit.lib.ObjectId;\r
+import org.eclipse.jgit.lib.Repository;\r
+import org.eclipse.jgit.revwalk.RevCommit;\r
+\r
+import com.gitblit.GitBlit;\r
+import com.gitblit.models.DailyActivity;\r
+import com.gitblit.models.Metric;\r
+import com.gitblit.models.RefModel;\r
+import com.gitblit.models.RepositoryCommit;\r
+import com.gitblit.models.RepositoryModel;\r
+import com.gitblit.models.UserModel;\r
+import com.gitblit.utils.JGitUtils;\r
+import com.gitblit.utils.StringUtils;\r
+import com.gitblit.utils.TimeUtils;\r
+import com.gitblit.wicket.GitBlitWebSession;\r
+import com.gitblit.wicket.WicketUtils;\r
+import com.gitblit.wicket.charting.GoogleChart;\r
+import com.gitblit.wicket.charting.GoogleCharts;\r
+import com.gitblit.wicket.charting.GoogleLineChart;\r
+import com.gitblit.wicket.charting.GooglePieChart;\r
+import com.gitblit.wicket.panels.ActivityPanel;\r
+\r
+/**\r
+ * Activity Page shows a list of recent commits across all visible Gitblit\r
+ * repositories.\r
+ * \r
+ * @author James Moger\r
+ * \r
+ */\r
+public class ActivityPage extends RootPage {\r
+\r
+       public ActivityPage(PageParameters params) {\r
+               super();\r
+               setupPage("", "");\r
+               final UserModel user = GitBlitWebSession.get().getUser();\r
+\r
+               // parameters\r
+               int daysBack = WicketUtils.getDaysBack(params);\r
+               if (daysBack < 1) {\r
+                       daysBack = 14;\r
+               }               \r
+               String set = WicketUtils.getSet(params);\r
+               String repositoryName = WicketUtils.getRepositoryName(params);\r
+               String objectId = WicketUtils.getObject(params);\r
+\r
+               List<RepositoryModel> models = null;\r
+               if (!StringUtils.isEmpty(repositoryName)) {\r
+                       // named repository\r
+                       models = new ArrayList<RepositoryModel>();\r
+                       RepositoryModel model = GitBlit.self().getRepositoryModel(repositoryName);\r
+                       if (user.canAccessRepository(model)) {\r
+                               models.add(model);\r
+                       }\r
+               }\r
+\r
+               // get all user accessible repositories\r
+               if (models == null) {\r
+                       models = GitBlit.self().getRepositoryModels(user);\r
+               }\r
+\r
+               // filter the repositories by the specified set\r
+               if (!StringUtils.isEmpty(set)) {\r
+                       List<String> sets = StringUtils.getStringsFromValue(set, ",");\r
+                       List<RepositoryModel> setModels = new ArrayList<RepositoryModel>();\r
+                       for (RepositoryModel model : models) {\r
+                               for (String curr : sets) {\r
+                                       if (model.federationSets.contains(curr)) {\r
+                                               setModels.add(model);\r
+                                       }\r
+                               }\r
+                       }\r
+                       models = setModels;\r
+               }\r
+\r
+               // Activity panel shows last daysBack of activity across all\r
+               // repositories.\r
+               Date thresholdDate = new Date(System.currentTimeMillis() - daysBack * TimeUtils.ONEDAY);\r
+\r
+               // Build a map of DailyActivity from the available repositories for the\r
+               // specified threshold date.\r
+               DateFormat df = new SimpleDateFormat("yyyy-MM-dd");\r
+               Calendar cal = Calendar.getInstance();\r
+\r
+               Map<String, DailyActivity> activity = new HashMap<String, DailyActivity>();\r
+               for (RepositoryModel model : models) {\r
+                       if (model.hasCommits && model.lastChange.after(thresholdDate)) {\r
+                               Repository repository = GitBlit.self().getRepository(model.name);\r
+                               List<RevCommit> commits = JGitUtils.getRevLog(repository, objectId, thresholdDate);\r
+                               Map<ObjectId, List<RefModel>> allRefs = JGitUtils.getAllRefs(repository);\r
+                               repository.close();\r
+\r
+                               // determine commit branch\r
+                               String branch = objectId;\r
+                               if (StringUtils.isEmpty(branch)) {\r
+                                       List<RefModel> headRefs = allRefs.get(commits.get(0).getId());\r
+                                       List<String> localBranches = new ArrayList<String>();\r
+                                       for (RefModel ref : headRefs) {\r
+                                               if (ref.getName().startsWith(Constants.R_HEADS)) {\r
+                                                       localBranches.add(ref.getName().substring(Constants.R_HEADS.length()));\r
+                                               }\r
+                                       }\r
+                                       // determine branch\r
+                                       if (localBranches.size() == 1) {\r
+                                               // only one branch, choose it\r
+                                               branch = localBranches.get(0);\r
+                                       } else if (localBranches.size() > 1) {\r
+                                               if (localBranches.contains("master")) {\r
+                                                       // choose master\r
+                                                       branch = "master";\r
+                                               } else {\r
+                                                       // choose first branch\r
+                                                       branch = localBranches.get(0);\r
+                                               }\r
+                                       }\r
+                               }\r
+\r
+                               for (RevCommit commit : commits) {\r
+                                       Date date = JGitUtils.getCommitDate(commit);\r
+                                       String dateStr = df.format(date);\r
+                                       if (!activity.containsKey(dateStr)) {\r
+                                               // Normalize the date to midnight\r
+                                               cal.setTime(date);\r
+                                               cal.set(Calendar.HOUR_OF_DAY, 0);\r
+                                               cal.set(Calendar.MINUTE, 0);\r
+                                               cal.set(Calendar.SECOND, 0);\r
+                                               cal.set(Calendar.MILLISECOND, 0);\r
+                                               activity.put(dateStr, new DailyActivity(cal.getTime()));\r
+                                       }\r
+                                       RepositoryCommit commitModel = new RepositoryCommit(model.name, branch, commit);\r
+                                       commitModel.setRefs(allRefs.get(commit.getId()));\r
+                                       activity.get(dateStr).commits.add(commitModel);\r
+                               }\r
+                       }\r
+               }\r
+\r
+               // activity metrics\r
+               Map<String, Metric> dayMetrics = new HashMap<String, Metric>();\r
+               Map<String, Metric> repositoryMetrics = new HashMap<String, Metric>();\r
+               Map<String, Metric> authorMetrics = new HashMap<String, Metric>();\r
+\r
+               // prepare day metrics\r
+               cal.setTimeInMillis(System.currentTimeMillis());\r
+               for (int i = 0; i < daysBack; i++) {\r
+                       cal.add(Calendar.DATE, -1);\r
+                       String key = df.format(cal.getTime());\r
+                       dayMetrics.put(key, new Metric(key));\r
+               }\r
+\r
+               // calculate activity metrics\r
+               for (Map.Entry<String, DailyActivity> entry : activity.entrySet()) {\r
+                       // day metrics\r
+                       Metric day = dayMetrics.get(entry.getKey());\r
+                       day.count = entry.getValue().commits.size();\r
+\r
+                       for (RepositoryCommit commit : entry.getValue().commits) {\r
+                               // repository metrics\r
+                               String repository = commit.repository;\r
+                               if (!repositoryMetrics.containsKey(repository)) {\r
+                                       repositoryMetrics.put(repository, new Metric(repository));\r
+                               }\r
+                               repositoryMetrics.get(repository).count++;\r
+\r
+                               // author metrics\r
+                               String author = commit.getAuthorIdent().getEmailAddress().toLowerCase();\r
+                               if (!authorMetrics.containsKey(author)) {\r
+                                       authorMetrics.put(author, new Metric(author));\r
+                               }\r
+                               authorMetrics.get(author).count++;\r
+                       }\r
+               }\r
+\r
+               // sort the activity groups and their commit contents\r
+               int totalCommits = 0;\r
+               List<DailyActivity> recentActivity = new ArrayList<DailyActivity>(activity.values());\r
+               for (DailyActivity daily : recentActivity) {\r
+                       Collections.sort(daily.commits);\r
+                       totalCommits += daily.commits.size();\r
+               }\r
+\r
+               // build google charts\r
+               int w = 310;\r
+               int h = 150;\r
+               GoogleCharts charts = new GoogleCharts();\r
+\r
+               // sort in reverse-chronological order and then reverse that\r
+               Collections.sort(recentActivity);\r
+               Collections.reverse(recentActivity);\r
+\r
+               // daily line chart\r
+               GoogleChart chart = new GoogleLineChart("chartDaily", getString("gb.dailyActivity"), "day",\r
+                               getString("gb.commits"));\r
+               df = new SimpleDateFormat("MMM dd");\r
+               for (DailyActivity metric : recentActivity) {\r
+                       chart.addValue(df.format(metric.date), metric.commits.size());\r
+               }\r
+               chart.setWidth(w);\r
+               chart.setHeight(h);\r
+               charts.addChart(chart);\r
+\r
+               // active repositories pie chart\r
+               chart = new GooglePieChart("chartRepositories", getString("gb.activeRepositories"),\r
+                               getString("gb.repository"), getString("gb.commits"));\r
+               for (Metric metric : repositoryMetrics.values()) {\r
+                       chart.addValue(metric.name, metric.count);\r
+               }\r
+               chart.setWidth(w);\r
+               chart.setHeight(h);\r
+               charts.addChart(chart);\r
+\r
+               // active authors pie chart\r
+               chart = new GooglePieChart("chartAuthors", getString("gb.activeAuthors"),\r
+                               getString("gb.author"), getString("gb.commits"));\r
+               for (Metric metric : authorMetrics.values()) {\r
+                       chart.addValue(metric.name, metric.count);\r
+               }\r
+               chart.setWidth(w);\r
+               chart.setHeight(h);\r
+               charts.addChart(chart);\r
+\r
+               add(new HeaderContributor(charts));\r
+\r
+               add(new Label("subheader", MessageFormat.format(getString("gb.recentActivitySubheader"),\r
+                               daysBack, totalCommits, authorMetrics.size())));\r
+\r
+               // add activity panel\r
+               add(new ActivityPanel("activityPanel", recentActivity));\r
+       }\r
+}\r
index f00c0411163310ee44249ef1047251b16a4571a1..06ab2985f6b0b26ecbfb28b3421bf36469e81c41 100644 (file)
@@ -82,6 +82,7 @@ public abstract class RootPage extends BasePage {
                // navigation links\r
                List<PageRegistration> pages = new ArrayList<PageRegistration>();\r
                pages.add(new PageRegistration("gb.repositories", RepositoriesPage.class));\r
+               pages.add(new PageRegistration("gb.activity", ActivityPage.class));\r
                if (showAdmin) {\r
                        pages.add(new PageRegistration("gb.users", UsersPage.class));\r
                }\r
diff --git a/src/com/gitblit/wicket/panels/ActivityPanel.html b/src/com/gitblit/wicket/panels/ActivityPanel.html
new file mode 100644 (file)
index 0000000..703dc00
--- /dev/null
@@ -0,0 +1,38 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\r
+<html xmlns="http://www.w3.org/1999/xhtml"  \r
+      xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"  \r
+      xml:lang="en"  \r
+      lang="en"> \r
+\r
+<body>\r
+<wicket:panel>\r
+\r
+       <div wicket:id="activity">\r
+               <div class="header"><span wicket:id="title">[title]</span></div>\r
+               <table wicket:id="commits">\r
+                       <tr wicket:id="commit"></tr>            \r
+               </table>        \r
+       </div>\r
+       \r
+       <wicket:fragment wicket:id="commitFragment">\r
+               <td class="date" style="width:40px; vertical-align: middle;" ><span wicket:id="time">[time of day]</span></td>\r
+               <td style="width:30px;vertical-align: middle;"><img wicket:id="avatar" style="vertical-align: middle;"></img></td>\r
+               <td class="author" style="vertical-align: middle;">\r
+                       <img wicket:id="commitIcon" style="vertical-align: middle;"></img>\r
+                       <span wicket:id="message">[shortlog commit link]</span><br/>\r
+                       <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>\r
+               </td>\r
+               <td style="text-align:right;vertical-align: middle;">\r
+                       <div wicket:id="commitRefs">[commit refs]</div>\r
+                       <span wicket:id="repository">[repository link]</span>\r
+               </td>\r
+               <td class="rightAlign" style="width:7em;vertical-align: middle;">\r
+               <span class="link">\r
+                               <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>\r
+                       </span>\r
+               </td>           \r
+       </wicket:fragment>\r
+       \r
+</wicket:panel>\r
+</body>\r
+</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 (file)
index 0000000..128ef2b
--- /dev/null
@@ -0,0 +1,142 @@
+/*\r
+ * Copyright 2011 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *     http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit.wicket.panels;\r
+\r
+import java.util.Collections;\r
+import java.util.List;\r
+\r
+import org.apache.wicket.markup.html.link.BookmarkablePageLink;\r
+import org.apache.wicket.markup.html.panel.Fragment;\r
+import org.apache.wicket.markup.repeater.Item;\r
+import org.apache.wicket.markup.repeater.data.DataView;\r
+import org.apache.wicket.markup.repeater.data.ListDataProvider;\r
+\r
+import com.gitblit.Constants;\r
+import com.gitblit.models.DailyActivity;\r
+import com.gitblit.models.RepositoryCommit;\r
+import com.gitblit.wicket.GitBlitWebSession;\r
+import com.gitblit.wicket.GravatarImage;\r
+import com.gitblit.wicket.WicketUtils;\r
+import com.gitblit.wicket.pages.CommitDiffPage;\r
+import com.gitblit.wicket.pages.CommitPage;\r
+import com.gitblit.wicket.pages.LogPage;\r
+import com.gitblit.wicket.pages.SearchPage;\r
+import com.gitblit.wicket.pages.SummaryPage;\r
+import com.gitblit.wicket.pages.TreePage;\r
+\r
+/**\r
+ * Renders activity in day-blocks in reverse-chronological order.\r
+ * \r
+ * @author James Moger\r
+ * \r
+ */\r
+public class ActivityPanel extends BasePanel {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       public ActivityPanel(String wicketId, List<DailyActivity> recentActivity) {\r
+               super(wicketId);\r
+\r
+               Collections.sort(recentActivity);\r
+\r
+               DataView<DailyActivity> activityView = new DataView<DailyActivity>("activity",\r
+                               new ListDataProvider<DailyActivity>(recentActivity)) {\r
+                       private static final long serialVersionUID = 1L;\r
+\r
+                       public void populateItem(final Item<DailyActivity> item) {\r
+                               final DailyActivity entry = item.getModelObject();\r
+                               item.add(WicketUtils.createDatestampLabel("title", entry.date, GitBlitWebSession\r
+                                               .get().getTimezone()));\r
+\r
+                               // display the commits in chronological order\r
+                               DataView<RepositoryCommit> commits = new DataView<RepositoryCommit>("commits",\r
+                                               new ListDataProvider<RepositoryCommit>(entry.commits)) {\r
+                                       private static final long serialVersionUID = 1L;\r
+\r
+                                       public void populateItem(final Item<RepositoryCommit> item) {\r
+                                               final RepositoryCommit commit = item.getModelObject();\r
+                                               Fragment fragment = new Fragment("commit", "commitFragment", this);\r
+\r
+                                               // time of day\r
+                                               fragment.add(WicketUtils.createTimeLabel("time", commit.getAuthorIdent()\r
+                                                               .getWhen(), GitBlitWebSession.get().getTimezone()));\r
+\r
+                                               // avatar\r
+                                               fragment.add(new GravatarImage("avatar", commit.getAuthorIdent(), 36));\r
+\r
+                                               // merge icon\r
+                                               if (commit.getParentCount() > 1) {\r
+                                                       fragment.add(WicketUtils.newImage("commitIcon",\r
+                                                                       "commit_merge_16x16.png"));\r
+                                               } else {\r
+                                                       fragment.add(WicketUtils.newBlankImage("commitIcon"));\r
+                                               }\r
+\r
+                                               // author search link\r
+                                               String author = commit.getAuthorIdent().getName();\r
+                                               LinkPanel authorLink = new LinkPanel("author", "list", author,\r
+                                                               SearchPage.class, WicketUtils.newSearchParameter(commit.repository,\r
+                                                                               commit.getName(), author, Constants.SearchType.AUTHOR));\r
+                                               setPersonSearchTooltip(authorLink, author, Constants.SearchType.AUTHOR);\r
+                                               fragment.add(authorLink);\r
+\r
+                                               // repository summary page link\r
+                                               LinkPanel repositoryLink = new LinkPanel("repository", "list",\r
+                                                               commit.repository, SummaryPage.class,\r
+                                                               WicketUtils.newRepositoryParameter(commit.repository));\r
+                                               fragment.add(repositoryLink);\r
+\r
+                                               // repository branch\r
+                                               LinkPanel branchLink = new LinkPanel("branch", "list", commit.branch,\r
+                                                               LogPage.class, WicketUtils.newObjectParameter(commit.repository,\r
+                                                                               commit.branch));\r
+                                               WicketUtils.setCssStyle(branchLink, "color: #008000;");\r
+                                               fragment.add(branchLink);\r
+\r
+                                               LinkPanel commitid = new LinkPanel("commitid", "list subject",\r
+                                                               commit.getShortName(), CommitPage.class,\r
+                                                               WicketUtils.newObjectParameter(commit.repository, commit.getName()));\r
+                                               fragment.add(commitid);\r
+\r
+                                               // message/commit link\r
+                                               String shortMessage = commit.getShortMessage();\r
+                                               LinkPanel shortlog = new LinkPanel("message", "list subject", shortMessage,\r
+                                                               CommitPage.class, WicketUtils.newObjectParameter(commit.repository,\r
+                                                                               commit.getName()));\r
+                                               fragment.add(shortlog);\r
+\r
+                                               // refs\r
+                                               fragment.add(new RefsPanel("commitRefs", commit.repository, commit\r
+                                                               .getRefs()));\r
+\r
+                                               // view, diff, tree links\r
+                                               fragment.add(new BookmarkablePageLink<Void>("view", CommitPage.class,\r
+                                                               WicketUtils.newObjectParameter(commit.repository, commit.getName())));\r
+                                               fragment.add(new BookmarkablePageLink<Void>("diff", CommitDiffPage.class,\r
+                                                               WicketUtils.newObjectParameter(commit.repository, commit.getName()))\r
+                                                               .setEnabled(commit.getParentCount() > 0));\r
+                                               fragment.add(new BookmarkablePageLink<Void>("tree", TreePage.class,\r
+                                                               WicketUtils.newObjectParameter(commit.repository, commit.getName())));\r
+\r
+                                               item.add(fragment);\r
+                                       }\r
+                               };\r
+                               item.add(commits);\r
+                       }\r
+               };\r
+               add(activityView);\r
+       }\r
+}\r