]> source.dussan.org Git - gitblit.git/commitdiff
Reflogs, Digests, and Dashboards
authorJames Moger <james.moger@gitblit.com>
Wed, 19 Jun 2013 02:22:31 +0000 (22:22 -0400)
committerJames Moger <james.moger@gitblit.com>
Wed, 19 Jun 2013 02:22:31 +0000 (22:22 -0400)
Renamed pushlog to reflog to better match it's current and future purpose.
Split PushesPanel into ReflogPanel and DigestsPanel.
Overhauled project pages and gave them a coherent purpose from the dashboard.

36 files changed:
src/main/distrib/data/gitblit.properties
src/main/java/com/gitblit/git/ReceiveHook.java
src/main/java/com/gitblit/models/DailyLogEntry.java
src/main/java/com/gitblit/models/PushLogEntry.java [deleted file]
src/main/java/com/gitblit/models/RefLogEntry.java [new file with mode: 0644]
src/main/java/com/gitblit/utils/PushLogUtils.java [deleted file]
src/main/java/com/gitblit/utils/RefLogUtils.java [new file with mode: 0644]
src/main/java/com/gitblit/wicket/GitBlitWebApp.java
src/main/java/com/gitblit/wicket/GitBlitWebApp.properties
src/main/java/com/gitblit/wicket/pages/DashboardPage.html [deleted file]
src/main/java/com/gitblit/wicket/pages/DashboardPage.java
src/main/java/com/gitblit/wicket/pages/MyDashboardPage.html [new file with mode: 0644]
src/main/java/com/gitblit/wicket/pages/MyDashboardPage.java [new file with mode: 0644]
src/main/java/com/gitblit/wicket/pages/OverviewPage.html
src/main/java/com/gitblit/wicket/pages/OverviewPage.java
src/main/java/com/gitblit/wicket/pages/ProjectPage.html
src/main/java/com/gitblit/wicket/pages/ProjectPage.java
src/main/java/com/gitblit/wicket/pages/ProjectsPage.html
src/main/java/com/gitblit/wicket/pages/ProjectsPage.java
src/main/java/com/gitblit/wicket/pages/PushesPage.html [deleted file]
src/main/java/com/gitblit/wicket/pages/PushesPage.java [deleted file]
src/main/java/com/gitblit/wicket/pages/ReflogPage.html [new file with mode: 0644]
src/main/java/com/gitblit/wicket/pages/ReflogPage.java [new file with mode: 0644]
src/main/java/com/gitblit/wicket/pages/RepositoryPage.java
src/main/java/com/gitblit/wicket/pages/RootPage.java
src/main/java/com/gitblit/wicket/panels/DigestsPanel.html [new file with mode: 0644]
src/main/java/com/gitblit/wicket/panels/DigestsPanel.java [new file with mode: 0644]
src/main/java/com/gitblit/wicket/panels/ProjectRepositoryPanel.html
src/main/java/com/gitblit/wicket/panels/ProjectRepositoryPanel.java
src/main/java/com/gitblit/wicket/panels/PushesPanel.html [deleted file]
src/main/java/com/gitblit/wicket/panels/PushesPanel.java [deleted file]
src/main/java/com/gitblit/wicket/panels/ReflogPanel.html [new file with mode: 0644]
src/main/java/com/gitblit/wicket/panels/ReflogPanel.java [new file with mode: 0644]
src/main/resources/gitblit.css
src/test/java/com/gitblit/tests/GitServletTest.java
src/test/java/com/gitblit/tests/PushLogTest.java

index 9f7eb778b5ad1d6b105205659e8c76dbbe36e141..7936bc2f67e66383cf20f91af897552a4e505503 100644 (file)
@@ -853,17 +853,17 @@ web.summaryRefsCount = 5
 # SINCE 0.5.0\r
 web.itemsPerPage = 50\r
 \r
-# The number of pushes to display on the overview page\r
+# The number of reflog changes to display on the overview page\r
 # Value must exceed 0 else default of 5 is used\r
 #\r
 # SINCE 1.3.0\r
-web.overviewPushCount = 5\r
+web.overviewReflogCount = 5\r
 \r
-# The number of pushes to show on a push page before show the first, prev, next\r
-# pagination links.  A default of 10 is used for any invalid value.\r
+# The number of reflog changes to show on a reflog page before show the first,\r
+#  prev, next pagination links.  A default of 10 is used for any invalid value.\r
 #\r
 # SINCE 1.3.0\r
-web.pushesPerPage = 10\r
+web.reflogChangesPerPage = 10\r
 \r
 # Registered file extensions to ignore during Lucene indexing\r
 #\r
index 3e00b3812aa0607396c328bf6d3f1662be3884dd..e3435ffb1aa7cb618aa1b1c9ccfc0891f78a68e1 100644 (file)
@@ -45,7 +45,7 @@ import com.gitblit.models.UserModel;
 import com.gitblit.utils.ArrayUtils;\r
 import com.gitblit.utils.ClientLogger;\r
 import com.gitblit.utils.JGitUtils;\r
-import com.gitblit.utils.PushLogUtils;\r
+import com.gitblit.utils.RefLogUtils;\r
 import com.gitblit.utils.StringUtils;\r
 \r
 /**\r
@@ -256,7 +256,7 @@ public class ReceiveHook implements PreReceiveHook, PostReceiveHook {
 \r
                // update push log\r
                try {\r
-                       PushLogUtils.updatePushLog(user, rp.getRepository(), commands);\r
+                       RefLogUtils.updateRefLog(user, rp.getRepository(), commands);\r
                        logger.debug(MessageFormat.format("{0} push log updated", repository.name));\r
                } catch (Exception e) {\r
                        logger.error(MessageFormat.format("Failed to update {0} pushlog", repository.name), e);\r
index a459d76f394f2640ffd9c8a47a4f9424242fe04d..41f13818de0086061a5b207576ed9202942ae24c 100644 (file)
@@ -28,7 +28,7 @@ import org.eclipse.jgit.transport.ReceiveCommand;
  * 
  * @author James Moger
  */
-public class DailyLogEntry extends PushLogEntry implements Serializable {
+public class DailyLogEntry extends RefLogEntry implements Serializable {
 
        private static final long serialVersionUID = 1L;
 
diff --git a/src/main/java/com/gitblit/models/PushLogEntry.java b/src/main/java/com/gitblit/models/PushLogEntry.java
deleted file mode 100644 (file)
index 77bed38..0000000
+++ /dev/null
@@ -1,340 +0,0 @@
-/*\r
- * Copyright 2013 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.text.MessageFormat;\r
-import java.util.ArrayList;\r
-import java.util.Collections;\r
-import java.util.Date;\r
-import java.util.HashMap;\r
-import java.util.HashSet;\r
-import java.util.LinkedHashSet;\r
-import java.util.List;\r
-import java.util.Map;\r
-import java.util.Set;\r
-\r
-import org.eclipse.jgit.lib.Constants;\r
-import org.eclipse.jgit.lib.PersonIdent;\r
-import org.eclipse.jgit.revwalk.RevCommit;\r
-import org.eclipse.jgit.transport.ReceiveCommand;\r
-\r
-import com.gitblit.utils.StringUtils;\r
-\r
-/**\r
- * Model class to represent a push into a repository.\r
- * \r
- * @author James Moger\r
- */\r
-public class PushLogEntry implements Serializable, Comparable<PushLogEntry> {\r
-\r
-       private static final long serialVersionUID = 1L;\r
-\r
-       public final String repository;\r
-       \r
-       public final Date date;\r
-       \r
-       public final UserModel user;\r
-\r
-       private final Set<RepositoryCommit> commits;\r
-       \r
-       protected final Map<String, ReceiveCommand.Type> refUpdates;\r
-       \r
-       protected final Map<String, String> refIdChanges;\r
-       \r
-       private int authorCount;\r
-\r
-       /**\r
-        * Constructor for specified duration of push from start date.\r
-        * \r
-        * @param repository\r
-        *            the repository that received the push\r
-        * @param date\r
-        *            the date of the push\r
-        * @param user\r
-        *            the user who pushed\r
-        */\r
-       public PushLogEntry(String repository, Date date, UserModel user) {\r
-               this.repository = repository;\r
-               this.date = date;\r
-               this.user = user;\r
-               this.commits = new LinkedHashSet<RepositoryCommit>();\r
-               this.refUpdates = new HashMap<String, ReceiveCommand.Type>();\r
-               this.refIdChanges = new HashMap<String, String>();\r
-               this.authorCount = -1;\r
-       }\r
-       \r
-       /**\r
-        * Tracks the change type for the specified ref.\r
-        * \r
-        * @param ref\r
-        * @param type\r
-        */\r
-       public void updateRef(String ref, ReceiveCommand.Type type) {\r
-               if (!refUpdates.containsKey(ref)) {\r
-                       refUpdates.put(ref, type);\r
-               }\r
-       }\r
-       \r
-       /**\r
-        * Tracks the change type for the specified ref.\r
-        * \r
-        * @param ref\r
-        * @param type\r
-        * @param oldId\r
-        * @param newId\r
-        */\r
-       public void updateRef(String ref, ReceiveCommand.Type type, String oldId, String newId) {\r
-               if (!refUpdates.containsKey(ref)) {\r
-                       refUpdates.put(ref, type);\r
-                       refIdChanges.put(ref, oldId + "-" + newId);\r
-               }\r
-       }\r
-       \r
-       /**\r
-        * Returns the old id of a ref.\r
-        * \r
-        * @param ref\r
-        * @return the old id\r
-        */\r
-       public String getOldId(String ref) {\r
-               String change = refIdChanges.get(ref);\r
-               if (StringUtils.isEmpty(change)) {\r
-                       return null;\r
-               }\r
-               return change.split("-")[0];\r
-       }\r
-\r
-       /**\r
-        * Returns the new id of a ref\r
-        * \r
-        * @param ref\r
-        * @return the new id\r
-        */\r
-       public String getNewId(String ref) {\r
-               String change = refIdChanges.get(ref);\r
-               if (StringUtils.isEmpty(change)) {\r
-                       return null;\r
-               }\r
-               return change.split("-")[1];\r
-       }\r
-       \r
-       /**\r
-        * Returns the change type of the ref change.\r
-        * \r
-        * @param ref\r
-        * @return the change type for the ref\r
-        */\r
-       public ReceiveCommand.Type getChangeType(String ref) {\r
-               ReceiveCommand.Type type = refUpdates.get(ref);\r
-               return type;\r
-       }\r
-\r
-       /**\r
-        * Adds a commit to the push entry object as long as the commit is not a\r
-        * duplicate.\r
-        * \r
-        * @param branch\r
-        * @param commit\r
-        * @return a RepositoryCommit, if one was added. Null if this is duplicate\r
-        *         commit\r
-        */\r
-       public RepositoryCommit addCommit(String branch, RevCommit commit) {\r
-               RepositoryCommit commitModel = new RepositoryCommit(repository, branch, commit);\r
-               if (commits.add(commitModel)) {\r
-                       authorCount = -1;\r
-                       return commitModel;\r
-               }\r
-               return null;\r
-       }\r
-       \r
-       /**\r
-        * Adds a a list of repository commits.  This is used to construct discrete\r
-        * ref push log entries\r
-        * \r
-        * @param commits\r
-        */\r
-       public void addCommits(List<RepositoryCommit> list) {\r
-               commits.addAll(list);\r
-               authorCount = -1;\r
-       }\r
-       \r
-       /**\r
-        * Returns true if this push contains a non-fastforward ref update.\r
-        * \r
-        * @return true if this is a non-fastforward push\r
-        */\r
-       public boolean isNonFastForward() {\r
-               for (Map.Entry<String, ReceiveCommand.Type> entry : refUpdates.entrySet()) {\r
-                       if (ReceiveCommand.Type.UPDATE_NONFASTFORWARD.equals(entry.getValue())) {\r
-                               return true;\r
-                       }\r
-               }\r
-               return false;\r
-       }\r
-       \r
-       /**\r
-        * Returns true if this ref has been rewound.\r
-        * \r
-        * @param ref\r
-        * @return true if this is a non-fastforward ref update\r
-        */\r
-       public boolean isNonFastForward(String ref) {\r
-               ReceiveCommand.Type type = refUpdates.get(ref);\r
-               if (type == null) {\r
-                       return false;\r
-               }\r
-               return ReceiveCommand.Type.UPDATE_NONFASTFORWARD.equals(type);\r
-       }\r
-\r
-       /**\r
-        * Returns true if this ref has been deleted.\r
-        * \r
-        * @param ref\r
-        * @return true if this is a delete ref update\r
-        */\r
-       public boolean isDelete(String ref) {\r
-               ReceiveCommand.Type type = refUpdates.get(ref);\r
-               if (type == null) {\r
-                       return false;\r
-               }\r
-               return ReceiveCommand.Type.DELETE.equals(type);\r
-       }\r
-       \r
-       /**\r
-        * Returns the list of refs changed by the push.\r
-        * \r
-        * @return a list of refs\r
-        */\r
-       public List<String> getChangedRefs() {\r
-               return new ArrayList<String>(refUpdates.keySet());\r
-       }\r
-       \r
-       /**\r
-        * Returns the list of branches changed by the push.\r
-        * \r
-        * @return a list of branches\r
-        */\r
-       public List<String> getChangedBranches() {\r
-               return getChangedRefs(Constants.R_HEADS);\r
-       }\r
-       \r
-       /**\r
-        * Returns the list of tags changed by the push.\r
-        * \r
-        * @return a list of tags\r
-        */\r
-       public List<String> getChangedTags() {\r
-               return getChangedRefs(Constants.R_TAGS);\r
-       }\r
-\r
-       /**\r
-        * Gets the changed refs in the push.\r
-        * \r
-        * @param baseRef\r
-        * @return the changed refs\r
-        */\r
-       protected List<String> getChangedRefs(String baseRef) {\r
-               Set<String> refs = new HashSet<String>();\r
-               for (String ref : refUpdates.keySet()) {\r
-                       if (baseRef == null || ref.startsWith(baseRef)) {\r
-                               refs.add(ref);\r
-                       }\r
-               }\r
-               List<String> list = new ArrayList<String>(refs);\r
-               Collections.sort(list);\r
-               return list;\r
-       }\r
-       \r
-       public int getAuthorCount() {\r
-               if (authorCount == -1) {\r
-                       Set<String> authors = new HashSet<String>();\r
-                       for (RepositoryCommit commit : commits) {\r
-                               String name = commit.getAuthorIdent().getName();\r
-                               authors.add(name);\r
-                       }\r
-                       authorCount = authors.size();\r
-               }\r
-               return authorCount;\r
-       }\r
-       \r
-       /**\r
-        * The total number of commits in the push.\r
-        * \r
-        * @return the number of commits in the push\r
-        */\r
-       public int getCommitCount() {\r
-               return commits.size();\r
-       }\r
-       \r
-       /**\r
-        * Returns all commits in the push.\r
-        * \r
-        * @return a list of commits\r
-        */\r
-       public List<RepositoryCommit> getCommits() {\r
-               List<RepositoryCommit> list = new ArrayList<RepositoryCommit>(commits);\r
-               Collections.sort(list);\r
-               return list;\r
-       }\r
-       \r
-       /**\r
-        * Returns all commits that belong to a particular ref\r
-        * \r
-        * @param ref\r
-        * @return a list of commits\r
-        */\r
-       public List<RepositoryCommit> getCommits(String ref) {\r
-               List<RepositoryCommit> list = new ArrayList<RepositoryCommit>();\r
-               for (RepositoryCommit commit : commits) {\r
-                       if (commit.branch.equals(ref)) {\r
-                               list.add(commit);\r
-                       }\r
-               }\r
-               Collections.sort(list);\r
-               return list;\r
-       }\r
-       \r
-       public PersonIdent getCommitterIdent() {\r
-               return new PersonIdent(user.getDisplayName(), user.emailAddress == null ? user.username : user.emailAddress);\r
-       }\r
-\r
-       public PersonIdent getAuthorIdent() {\r
-               return getCommitterIdent();\r
-       }\r
-\r
-       @Override\r
-       public int compareTo(PushLogEntry o) {\r
-               // reverse chronological order\r
-               return o.date.compareTo(date);\r
-       }\r
-       \r
-       @Override\r
-       public String toString() {\r
-               StringBuilder sb = new StringBuilder();\r
-               sb.append(MessageFormat.format("{0,date,yyyy-MM-dd HH:mm}: {1} pushed {2,number,0} commit{3} to {4} ",\r
-                               date, user.getDisplayName(), commits.size(), commits.size() == 1 ? "":"s", repository));\r
-               for (Map.Entry<String, ReceiveCommand.Type> entry : refUpdates.entrySet()) {\r
-                       String ref = entry.getKey();\r
-                       ReceiveCommand.Type type = entry.getValue();\r
-                       sb.append("\n  ").append(ref).append(' ').append(type.name()).append('\n');\r
-                       for (RepositoryCommit commit : getCommits(ref)) {\r
-                               sb.append("    ").append(commit.toString()).append('\n');\r
-                       }\r
-               }\r
-               return sb.toString();\r
-       }\r
-}\r
diff --git a/src/main/java/com/gitblit/models/RefLogEntry.java b/src/main/java/com/gitblit/models/RefLogEntry.java
new file mode 100644 (file)
index 0000000..54d1777
--- /dev/null
@@ -0,0 +1,340 @@
+/*\r
+ * Copyright 2013 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.text.MessageFormat;\r
+import java.util.ArrayList;\r
+import java.util.Collections;\r
+import java.util.Date;\r
+import java.util.HashMap;\r
+import java.util.HashSet;\r
+import java.util.LinkedHashSet;\r
+import java.util.List;\r
+import java.util.Map;\r
+import java.util.Set;\r
+\r
+import org.eclipse.jgit.lib.Constants;\r
+import org.eclipse.jgit.lib.PersonIdent;\r
+import org.eclipse.jgit.revwalk.RevCommit;\r
+import org.eclipse.jgit.transport.ReceiveCommand;\r
+\r
+import com.gitblit.utils.StringUtils;\r
+\r
+/**\r
+ * Model class to represent a push into a repository.\r
+ * \r
+ * @author James Moger\r
+ */\r
+public class RefLogEntry implements Serializable, Comparable<RefLogEntry> {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       public final String repository;\r
+       \r
+       public final Date date;\r
+       \r
+       public final UserModel user;\r
+\r
+       private final Set<RepositoryCommit> commits;\r
+       \r
+       protected final Map<String, ReceiveCommand.Type> refUpdates;\r
+       \r
+       protected final Map<String, String> refIdChanges;\r
+       \r
+       private int authorCount;\r
+\r
+       /**\r
+        * Constructor for specified duration of push from start date.\r
+        * \r
+        * @param repository\r
+        *            the repository that received the push\r
+        * @param date\r
+        *            the date of the push\r
+        * @param user\r
+        *            the user who pushed\r
+        */\r
+       public RefLogEntry(String repository, Date date, UserModel user) {\r
+               this.repository = repository;\r
+               this.date = date;\r
+               this.user = user;\r
+               this.commits = new LinkedHashSet<RepositoryCommit>();\r
+               this.refUpdates = new HashMap<String, ReceiveCommand.Type>();\r
+               this.refIdChanges = new HashMap<String, String>();\r
+               this.authorCount = -1;\r
+       }\r
+       \r
+       /**\r
+        * Tracks the change type for the specified ref.\r
+        * \r
+        * @param ref\r
+        * @param type\r
+        */\r
+       public void updateRef(String ref, ReceiveCommand.Type type) {\r
+               if (!refUpdates.containsKey(ref)) {\r
+                       refUpdates.put(ref, type);\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * Tracks the change type for the specified ref.\r
+        * \r
+        * @param ref\r
+        * @param type\r
+        * @param oldId\r
+        * @param newId\r
+        */\r
+       public void updateRef(String ref, ReceiveCommand.Type type, String oldId, String newId) {\r
+               if (!refUpdates.containsKey(ref)) {\r
+                       refUpdates.put(ref, type);\r
+                       refIdChanges.put(ref, oldId + "-" + newId);\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * Returns the old id of a ref.\r
+        * \r
+        * @param ref\r
+        * @return the old id\r
+        */\r
+       public String getOldId(String ref) {\r
+               String change = refIdChanges.get(ref);\r
+               if (StringUtils.isEmpty(change)) {\r
+                       return null;\r
+               }\r
+               return change.split("-")[0];\r
+       }\r
+\r
+       /**\r
+        * Returns the new id of a ref\r
+        * \r
+        * @param ref\r
+        * @return the new id\r
+        */\r
+       public String getNewId(String ref) {\r
+               String change = refIdChanges.get(ref);\r
+               if (StringUtils.isEmpty(change)) {\r
+                       return null;\r
+               }\r
+               return change.split("-")[1];\r
+       }\r
+       \r
+       /**\r
+        * Returns the change type of the ref change.\r
+        * \r
+        * @param ref\r
+        * @return the change type for the ref\r
+        */\r
+       public ReceiveCommand.Type getChangeType(String ref) {\r
+               ReceiveCommand.Type type = refUpdates.get(ref);\r
+               return type;\r
+       }\r
+\r
+       /**\r
+        * Adds a commit to the push entry object as long as the commit is not a\r
+        * duplicate.\r
+        * \r
+        * @param branch\r
+        * @param commit\r
+        * @return a RepositoryCommit, if one was added. Null if this is duplicate\r
+        *         commit\r
+        */\r
+       public RepositoryCommit addCommit(String branch, RevCommit commit) {\r
+               RepositoryCommit commitModel = new RepositoryCommit(repository, branch, commit);\r
+               if (commits.add(commitModel)) {\r
+                       authorCount = -1;\r
+                       return commitModel;\r
+               }\r
+               return null;\r
+       }\r
+       \r
+       /**\r
+        * Adds a a list of repository commits.  This is used to construct discrete\r
+        * ref push log entries\r
+        * \r
+        * @param commits\r
+        */\r
+       public void addCommits(List<RepositoryCommit> list) {\r
+               commits.addAll(list);\r
+               authorCount = -1;\r
+       }\r
+       \r
+       /**\r
+        * Returns true if this push contains a non-fastforward ref update.\r
+        * \r
+        * @return true if this is a non-fastforward push\r
+        */\r
+       public boolean isNonFastForward() {\r
+               for (Map.Entry<String, ReceiveCommand.Type> entry : refUpdates.entrySet()) {\r
+                       if (ReceiveCommand.Type.UPDATE_NONFASTFORWARD.equals(entry.getValue())) {\r
+                               return true;\r
+                       }\r
+               }\r
+               return false;\r
+       }\r
+       \r
+       /**\r
+        * Returns true if this ref has been rewound.\r
+        * \r
+        * @param ref\r
+        * @return true if this is a non-fastforward ref update\r
+        */\r
+       public boolean isNonFastForward(String ref) {\r
+               ReceiveCommand.Type type = refUpdates.get(ref);\r
+               if (type == null) {\r
+                       return false;\r
+               }\r
+               return ReceiveCommand.Type.UPDATE_NONFASTFORWARD.equals(type);\r
+       }\r
+\r
+       /**\r
+        * Returns true if this ref has been deleted.\r
+        * \r
+        * @param ref\r
+        * @return true if this is a delete ref update\r
+        */\r
+       public boolean isDelete(String ref) {\r
+               ReceiveCommand.Type type = refUpdates.get(ref);\r
+               if (type == null) {\r
+                       return false;\r
+               }\r
+               return ReceiveCommand.Type.DELETE.equals(type);\r
+       }\r
+       \r
+       /**\r
+        * Returns the list of refs changed by the push.\r
+        * \r
+        * @return a list of refs\r
+        */\r
+       public List<String> getChangedRefs() {\r
+               return new ArrayList<String>(refUpdates.keySet());\r
+       }\r
+       \r
+       /**\r
+        * Returns the list of branches changed by the push.\r
+        * \r
+        * @return a list of branches\r
+        */\r
+       public List<String> getChangedBranches() {\r
+               return getChangedRefs(Constants.R_HEADS);\r
+       }\r
+       \r
+       /**\r
+        * Returns the list of tags changed by the push.\r
+        * \r
+        * @return a list of tags\r
+        */\r
+       public List<String> getChangedTags() {\r
+               return getChangedRefs(Constants.R_TAGS);\r
+       }\r
+\r
+       /**\r
+        * Gets the changed refs in the push.\r
+        * \r
+        * @param baseRef\r
+        * @return the changed refs\r
+        */\r
+       protected List<String> getChangedRefs(String baseRef) {\r
+               Set<String> refs = new HashSet<String>();\r
+               for (String ref : refUpdates.keySet()) {\r
+                       if (baseRef == null || ref.startsWith(baseRef)) {\r
+                               refs.add(ref);\r
+                       }\r
+               }\r
+               List<String> list = new ArrayList<String>(refs);\r
+               Collections.sort(list);\r
+               return list;\r
+       }\r
+       \r
+       public int getAuthorCount() {\r
+               if (authorCount == -1) {\r
+                       Set<String> authors = new HashSet<String>();\r
+                       for (RepositoryCommit commit : commits) {\r
+                               String name = commit.getAuthorIdent().getName();\r
+                               authors.add(name);\r
+                       }\r
+                       authorCount = authors.size();\r
+               }\r
+               return authorCount;\r
+       }\r
+       \r
+       /**\r
+        * The total number of commits in the push.\r
+        * \r
+        * @return the number of commits in the push\r
+        */\r
+       public int getCommitCount() {\r
+               return commits.size();\r
+       }\r
+       \r
+       /**\r
+        * Returns all commits in the push.\r
+        * \r
+        * @return a list of commits\r
+        */\r
+       public List<RepositoryCommit> getCommits() {\r
+               List<RepositoryCommit> list = new ArrayList<RepositoryCommit>(commits);\r
+               Collections.sort(list);\r
+               return list;\r
+       }\r
+       \r
+       /**\r
+        * Returns all commits that belong to a particular ref\r
+        * \r
+        * @param ref\r
+        * @return a list of commits\r
+        */\r
+       public List<RepositoryCommit> getCommits(String ref) {\r
+               List<RepositoryCommit> list = new ArrayList<RepositoryCommit>();\r
+               for (RepositoryCommit commit : commits) {\r
+                       if (commit.branch.equals(ref)) {\r
+                               list.add(commit);\r
+                       }\r
+               }\r
+               Collections.sort(list);\r
+               return list;\r
+       }\r
+       \r
+       public PersonIdent getCommitterIdent() {\r
+               return new PersonIdent(user.getDisplayName(), user.emailAddress == null ? user.username : user.emailAddress);\r
+       }\r
+\r
+       public PersonIdent getAuthorIdent() {\r
+               return getCommitterIdent();\r
+       }\r
+\r
+       @Override\r
+       public int compareTo(RefLogEntry o) {\r
+               // reverse chronological order\r
+               return o.date.compareTo(date);\r
+       }\r
+       \r
+       @Override\r
+       public String toString() {\r
+               StringBuilder sb = new StringBuilder();\r
+               sb.append(MessageFormat.format("{0,date,yyyy-MM-dd HH:mm}: {1} pushed {2,number,0} commit{3} to {4} ",\r
+                               date, user.getDisplayName(), commits.size(), commits.size() == 1 ? "":"s", repository));\r
+               for (Map.Entry<String, ReceiveCommand.Type> entry : refUpdates.entrySet()) {\r
+                       String ref = entry.getKey();\r
+                       ReceiveCommand.Type type = entry.getValue();\r
+                       sb.append("\n  ").append(ref).append(' ').append(type.name()).append('\n');\r
+                       for (RepositoryCommit commit : getCommits(ref)) {\r
+                               sb.append("    ").append(commit.toString()).append('\n');\r
+                       }\r
+               }\r
+               return sb.toString();\r
+       }\r
+}\r
diff --git a/src/main/java/com/gitblit/utils/PushLogUtils.java b/src/main/java/com/gitblit/utils/PushLogUtils.java
deleted file mode 100644 (file)
index fed5b19..0000000
+++ /dev/null
@@ -1,636 +0,0 @@
-/*
- * Copyright 2013 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.io.IOException;
-import java.text.DateFormat;
-import java.text.MessageFormat;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.TimeZone;
-import java.util.TreeSet;
-
-import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException;
-import org.eclipse.jgit.api.errors.JGitInternalException;
-import org.eclipse.jgit.dircache.DirCache;
-import org.eclipse.jgit.dircache.DirCacheBuilder;
-import org.eclipse.jgit.dircache.DirCacheEntry;
-import org.eclipse.jgit.internal.JGitText;
-import org.eclipse.jgit.lib.CommitBuilder;
-import org.eclipse.jgit.lib.FileMode;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectInserter;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.RefUpdate;
-import org.eclipse.jgit.lib.RefUpdate.Result;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.transport.ReceiveCommand;
-import org.eclipse.jgit.treewalk.CanonicalTreeParser;
-import org.eclipse.jgit.treewalk.TreeWalk;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.gitblit.Constants;
-import com.gitblit.models.DailyLogEntry;
-import com.gitblit.models.PathModel.PathChangeModel;
-import com.gitblit.models.PushLogEntry;
-import com.gitblit.models.RefModel;
-import com.gitblit.models.RepositoryCommit;
-import com.gitblit.models.UserModel;
-
-/**
- * Utility class for maintaining a pushlog within a git repository on an
- * orphan branch.
- * 
- * @author James Moger
- *
- */
-public class PushLogUtils {
-       
-       public static final String GB_PUSHES = "refs/gitblit/pushes";
-
-       static final Logger LOGGER = LoggerFactory.getLogger(PushLogUtils.class);
-
-       /**
-        * Log an error message and exception.
-        * 
-        * @param t
-        * @param repository
-        *            if repository is not null it MUST be the {0} parameter in the
-        *            pattern.
-        * @param pattern
-        * @param objects
-        */
-       private static void error(Throwable t, Repository repository, String pattern, Object... objects) {
-               List<Object> parameters = new ArrayList<Object>();
-               if (objects != null && objects.length > 0) {
-                       for (Object o : objects) {
-                               parameters.add(o);
-                       }
-               }
-               if (repository != null) {
-                       parameters.add(0, repository.getDirectory().getAbsolutePath());
-               }
-               LOGGER.error(MessageFormat.format(pattern, parameters.toArray()), t);
-       }
-
-       /**
-        * Returns a RefModel for the gb-pushes branch in the repository. If the
-        * branch can not be found, null is returned.
-        * 
-        * @param repository
-        * @return a refmodel for the gb-pushes branch or null
-        */
-       public static RefModel getPushLogBranch(Repository repository) {
-               List<RefModel> refs = JGitUtils.getRefs(repository, com.gitblit.Constants.R_GITBLIT);
-               for (RefModel ref : refs) {
-                       if (ref.reference.getName().equals(GB_PUSHES)) {
-                               return ref;
-                       }
-               }
-               return null;
-       }
-       
-       private static UserModel newUserModelFrom(PersonIdent ident) {
-               String name = ident.getName();
-               String username;
-               String displayname;
-               if (name.indexOf('/') > -1) {
-                       int slash = name.indexOf('/');
-                       displayname = name.substring(0, slash);
-                       username = name.substring(slash + 1);
-               } else {
-                       displayname = name;
-                       username = ident.getEmailAddress();
-               }
-               
-               UserModel user = new UserModel(username);
-               user.displayName = displayname;
-               user.emailAddress = ident.getEmailAddress();
-               return user;
-       }
-       
-       /**
-        * Updates a push log.
-        * 
-        * @param user
-        * @param repository
-        * @param commands
-        * @return true, if the update was successful
-        */
-       public static boolean updatePushLog(UserModel user, Repository repository,
-                       Collection<ReceiveCommand> commands) {
-               RefModel pushlogBranch = getPushLogBranch(repository);
-               if (pushlogBranch == null) {
-                       JGitUtils.createOrphanBranch(repository, GB_PUSHES, null);
-               }
-               
-               boolean success = false;
-               String message = "push";
-               
-               try {
-                       ObjectId headId = repository.resolve(GB_PUSHES + "^{commit}");
-                       ObjectInserter odi = repository.newObjectInserter();
-                       try {
-                               // Create the in-memory index of the push log entry
-                               DirCache index = createIndex(repository, headId, commands);
-                               ObjectId indexTreeId = index.writeTree(odi);
-
-                               PersonIdent ident;
-                               if (UserModel.ANONYMOUS.equals(user)) {
-                                       // anonymous push
-                                       ident = new PersonIdent("anonymous", "anonymous");
-                               } else {
-                                       // construct real pushing account
-                                       ident = new PersonIdent(MessageFormat.format("{0}/{1}", user.getDisplayName(), user.username),
-                                               user.emailAddress == null ? user.username : user.emailAddress);
-                               }
-
-                               // Create a commit object
-                               CommitBuilder commit = new CommitBuilder();
-                               commit.setAuthor(ident);
-                               commit.setCommitter(ident);
-                               commit.setEncoding(Constants.ENCODING);
-                               commit.setMessage(message);
-                               commit.setParentId(headId);
-                               commit.setTreeId(indexTreeId);
-
-                               // Insert the commit into the repository
-                               ObjectId commitId = odi.insert(commit);
-                               odi.flush();
-
-                               RevWalk revWalk = new RevWalk(repository);
-                               try {
-                                       RevCommit revCommit = revWalk.parseCommit(commitId);
-                                       RefUpdate ru = repository.updateRef(GB_PUSHES);
-                                       ru.setNewObjectId(commitId);
-                                       ru.setExpectedOldObjectId(headId);
-                                       ru.setRefLogMessage("commit: " + revCommit.getShortMessage(), false);
-                                       Result rc = ru.forceUpdate();
-                                       switch (rc) {
-                                       case NEW:
-                                       case FORCED:
-                                       case FAST_FORWARD:
-                                               success = true;
-                                               break;
-                                       case REJECTED:
-                                       case LOCK_FAILURE:
-                                               throw new ConcurrentRefUpdateException(JGitText.get().couldNotLockHEAD,
-                                                               ru.getRef(), rc);
-                                       default:
-                                               throw new JGitInternalException(MessageFormat.format(
-                                                               JGitText.get().updatingRefFailed, GB_PUSHES, commitId.toString(),
-                                                               rc));
-                                       }
-                               } finally {
-                                       revWalk.release();
-                               }
-                       } finally {
-                               odi.release();
-                       }
-               } catch (Throwable t) {
-                       error(t, repository, "Failed to commit pushlog entry to {0}");
-               }
-               return success;
-       }
-       
-       /**
-        * Creates an in-memory index of the push log entry.
-        * 
-        * @param repo
-        * @param headId
-        * @param commands
-        * @return an in-memory index
-        * @throws IOException
-        */
-       private static DirCache createIndex(Repository repo, ObjectId headId, 
-                       Collection<ReceiveCommand> commands) throws IOException {
-
-               DirCache inCoreIndex = DirCache.newInCore();
-               DirCacheBuilder dcBuilder = inCoreIndex.builder();
-               ObjectInserter inserter = repo.newObjectInserter();
-
-               long now = System.currentTimeMillis();
-               Set<String> ignorePaths = new TreeSet<String>();
-               try {
-                       // add receive commands to the temporary index
-                       for (ReceiveCommand command : commands) {
-                               // use the ref names as the path names
-                               String path = command.getRefName();
-                               ignorePaths.add(path);
-
-                               StringBuilder change = new StringBuilder();
-                               change.append(command.getType().name()).append(' ');
-                               switch (command.getType()) {
-                               case CREATE:
-                                       change.append(ObjectId.zeroId().getName());
-                                       change.append(' ');
-                                       change.append(command.getNewId().getName());
-                                       break;
-                               case UPDATE:
-                               case UPDATE_NONFASTFORWARD:
-                                       change.append(command.getOldId().getName());
-                                       change.append(' ');
-                                       change.append(command.getNewId().getName());
-                                       break;
-                               case DELETE:
-                                       change = null;
-                                       break;
-                               }
-                               if (change == null) {
-                                       // ref deleted
-                                       continue;
-                               }
-                               String content = change.toString();
-                               
-                               // create an index entry for this attachment
-                               final DirCacheEntry dcEntry = new DirCacheEntry(path);
-                               dcEntry.setLength(content.length());
-                               dcEntry.setLastModified(now);
-                               dcEntry.setFileMode(FileMode.REGULAR_FILE);
-
-                               // insert object
-                               dcEntry.setObjectId(inserter.insert(org.eclipse.jgit.lib.Constants.OBJ_BLOB, content.getBytes("UTF-8")));
-
-                               // add to temporary in-core index
-                               dcBuilder.add(dcEntry);
-                       }
-
-                       // Traverse HEAD to add all other paths
-                       TreeWalk treeWalk = new TreeWalk(repo);
-                       int hIdx = -1;
-                       if (headId != null)
-                               hIdx = treeWalk.addTree(new RevWalk(repo).parseTree(headId));
-                       treeWalk.setRecursive(true);
-
-                       while (treeWalk.next()) {
-                               String path = treeWalk.getPathString();
-                               CanonicalTreeParser hTree = null;
-                               if (hIdx != -1)
-                                       hTree = treeWalk.getTree(hIdx, CanonicalTreeParser.class);
-                               if (!ignorePaths.contains(path)) {
-                                       // add entries from HEAD for all other paths
-                                       if (hTree != null) {
-                                               // create a new DirCacheEntry with data retrieved from
-                                               // HEAD
-                                               final DirCacheEntry dcEntry = new DirCacheEntry(path);
-                                               dcEntry.setObjectId(hTree.getEntryObjectId());
-                                               dcEntry.setFileMode(hTree.getEntryFileMode());
-
-                                               // add to temporary in-core index
-                                               dcBuilder.add(dcEntry);
-                                       }
-                               }
-                       }
-
-                       // release the treewalk
-                       treeWalk.release();
-
-                       // finish temporary in-core index used for this commit
-                       dcBuilder.finish();
-               } finally {
-                       inserter.release();
-               }
-               return inCoreIndex;
-       }
-       
-       public static List<PushLogEntry> getPushLog(String repositoryName, Repository repository) {
-               return getPushLog(repositoryName, repository, null, 0, -1);
-       }
-
-       public static List<PushLogEntry> getPushLog(String repositoryName, Repository repository, int maxCount) {
-               return getPushLog(repositoryName, repository, null, 0, maxCount);
-       }
-
-       public static List<PushLogEntry> getPushLog(String repositoryName, Repository repository, int offset, int maxCount) {
-               return getPushLog(repositoryName, repository, null, offset, maxCount);
-       }
-
-       public static List<PushLogEntry> getPushLog(String repositoryName, Repository repository, Date minimumDate) {
-               return getPushLog(repositoryName, repository, minimumDate, 0, -1);
-       }
-       
-       /**
-        * Returns the list of push log entries as they were recorded by Gitblit.
-        * Each PushLogEntry may represent multiple ref updates.
-        * 
-        * @param repositoryName
-        * @param repository
-        * @param minimumDate
-        * @param offset
-        * @param maxCount
-        *                      if < 0, all pushes are returned.
-        * @return a list of push log entries
-        */
-       public static List<PushLogEntry> getPushLog(String repositoryName, Repository repository,
-                       Date minimumDate, int offset, int maxCount) {
-               List<PushLogEntry> list = new ArrayList<PushLogEntry>();
-               RefModel ref = getPushLogBranch(repository);
-               if (ref == null) {
-                       return list;
-               }
-               if (maxCount == 0) {
-                       return list;
-               }
-               
-               Map<ObjectId, List<RefModel>> allRefs = JGitUtils.getAllRefs(repository);
-               List<RevCommit> pushes;
-               if (minimumDate == null) {
-                       pushes = JGitUtils.getRevLog(repository, GB_PUSHES, offset, maxCount);
-               } else {
-                       pushes = JGitUtils.getRevLog(repository, GB_PUSHES, minimumDate);
-               }
-               for (RevCommit push : pushes) {
-                       if (push.getAuthorIdent().getName().equalsIgnoreCase("gitblit")) {
-                               // skip gitblit/internal commits
-                               continue;
-                       }
-
-                       UserModel user = newUserModelFrom(push.getAuthorIdent());
-                       Date date = push.getAuthorIdent().getWhen();
-                       
-                       PushLogEntry log = new PushLogEntry(repositoryName, date, user);
-                       list.add(log);
-                       List<PathChangeModel> changedRefs = JGitUtils.getFilesInCommit(repository, push);
-                       for (PathChangeModel change : changedRefs) {
-                               switch (change.changeType) {
-                               case DELETE:
-                                       log.updateRef(change.path, ReceiveCommand.Type.DELETE);
-                                       break;
-                               case ADD:
-                                       log.updateRef(change.path, ReceiveCommand.Type.CREATE);
-                               default:
-                                       String content = JGitUtils.getStringContent(repository, push.getTree(), change.path);
-                                       String [] fields = content.split(" ");
-                                       String oldId = fields[1];
-                                       String newId = fields[2];
-                                       log.updateRef(change.path, ReceiveCommand.Type.valueOf(fields[0]), oldId, newId);
-                                       List<RevCommit> pushedCommits = JGitUtils.getRevLog(repository, oldId, newId);
-                                       for (RevCommit pushedCommit : pushedCommits) {
-                                               RepositoryCommit repoCommit = log.addCommit(change.path, pushedCommit);
-                                               if (repoCommit != null) {
-                                                       repoCommit.setRefs(allRefs.get(pushedCommit.getId()));
-                                               }
-                                       }
-                               }
-                       }
-               }
-               Collections.sort(list);
-               return list;
-       }
-
-       /**
-        * Returns the list of pushes separated by ref (e.g. each ref has it's own
-        * PushLogEntry object).
-        *  
-        * @param repositoryName
-        * @param repository
-        * @param maxCount
-        * @return a list of push log entries separated by ref
-        */
-       public static List<PushLogEntry> getPushLogByRef(String repositoryName, Repository repository, int maxCount) {
-               return getPushLogByRef(repositoryName, repository, 0, maxCount);
-       }
-       
-       /**
-        * Returns the list of pushes separated by ref (e.g. each ref has it's own
-        * PushLogEntry object).
-        *  
-        * @param repositoryName
-        * @param repository
-        * @param offset
-        * @param maxCount
-        * @return a list of push log entries separated by ref
-        */
-       public static List<PushLogEntry> getPushLogByRef(String repositoryName, Repository repository,  int offset,
-                       int maxCount) {
-               // break the push log into ref push logs and then merge them back into a list
-               Map<String, List<PushLogEntry>> refMap = new HashMap<String, List<PushLogEntry>>();
-        List<PushLogEntry> pushes = getPushLog(repositoryName, repository, offset, maxCount);
-               for (PushLogEntry push : pushes) {
-                       for (String ref : push.getChangedRefs()) {
-                               if (!refMap.containsKey(ref)) {
-                                       refMap.put(ref, new ArrayList<PushLogEntry>());
-                               }
-                               
-                               // construct new ref-specific push log entry
-                               PushLogEntry refPush;
-                               if (push instanceof DailyLogEntry) {
-                                       // simulated push log from commits grouped by date
-                                       refPush = new DailyLogEntry(push.repository, push.date);
-                               } else {
-                                       // real push log entry
-                                       refPush = new PushLogEntry(push.repository, push.date, push.user);
-                               }
-                               refPush.updateRef(ref, push.getChangeType(ref), push.getOldId(ref), push.getNewId(ref));
-                               refPush.addCommits(push.getCommits(ref));
-                               refMap.get(ref).add(refPush);
-                       }
-               }
-               
-               // merge individual ref pushes into master list
-               List<PushLogEntry> refPushLog = new ArrayList<PushLogEntry>();
-               for (List<PushLogEntry> refPush : refMap.values()) {
-                       refPushLog.addAll(refPush);
-               }
-               
-               // sort ref push log
-               Collections.sort(refPushLog);
-               
-               return refPushLog;
-       }
-       
-       /**
-        * Returns the list of pushes separated by ref (e.g. each ref has it's own
-        * PushLogEntry object).
-        *  
-        * @param repositoryName
-        * @param repository
-        * @param minimumDate
-        * @return a list of push log entries separated by ref
-        */
-       public static List<PushLogEntry> getPushLogByRef(String repositoryName, Repository repository,  Date minimumDate) {
-               // break the push log into ref push logs and then merge them back into a list
-               Map<String, List<PushLogEntry>> refMap = new HashMap<String, List<PushLogEntry>>();
-               List<PushLogEntry> pushes = getPushLog(repositoryName, repository, minimumDate);
-               for (PushLogEntry push : pushes) {
-                       for (String ref : push.getChangedRefs()) {
-                               if (!refMap.containsKey(ref)) {
-                                       refMap.put(ref, new ArrayList<PushLogEntry>());
-                               }
-
-                // construct new ref-specific push log entry
-                PushLogEntry refPush = new PushLogEntry(push.repository, push.date, push.user);
-                refPush.updateRef(ref, push.getChangeType(ref), push.getOldId(ref), push.getNewId(ref));
-                               refPush.addCommits(push.getCommits(ref));
-                               refMap.get(ref).add(refPush);
-                       }
-               }
-               
-               // merge individual ref pushes into master list
-               List<PushLogEntry> refPushLog = new ArrayList<PushLogEntry>();
-               for (List<PushLogEntry> refPush : refMap.values()) {
-                       refPushLog.addAll(refPush);
-               }
-               
-               // sort ref push log
-               Collections.sort(refPushLog);
-               
-               return refPushLog;
-       }
-
-    /**
-     * Returns a commit log grouped by day.
-     *
-     * @param repositoryName
-     * @param repository
-     * @param minimumDate
-     * @param offset
-     * @param maxCount
-     *                         if < 0, all pushes are returned.
-     * @param the timezone to use when aggregating commits by date
-     * @return a list of grouped commit log entries
-     */
-    public static List<DailyLogEntry> getDailyLog(String repositoryName, Repository repository,
-                                                 Date minimumDate, int offset, int maxCount,
-                                                 TimeZone timezone) {
-        DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
-               df.setTimeZone(timezone);
-
-        Map<ObjectId, List<RefModel>> allRefs = JGitUtils.getAllRefs(repository);
-        Map<String, DailyLogEntry> tags = new HashMap<String, DailyLogEntry>();
-        Map<String, DailyLogEntry> pulls = new HashMap<String, DailyLogEntry>();
-        Map<String, DailyLogEntry> dailydigests = new HashMap<String, DailyLogEntry>();
-        String linearParent = null;
-        for (RefModel local : JGitUtils.getLocalBranches(repository, true, -1)) {
-            String branch = local.getName();
-            List<RevCommit> commits = JGitUtils.getRevLog(repository, branch, minimumDate);
-            for (RevCommit commit : commits) {
-               if (linearParent != null) {
-                       if (!commit.getName().equals(linearParent)) {
-                               // only follow linear branch commits
-                               continue;
-                       }
-               }
-                Date date = JGitUtils.getCommitDate(commit);
-                String dateStr = df.format(date);
-                if (!dailydigests.containsKey(dateStr)) {
-                    dailydigests.put(dateStr, new DailyLogEntry(repositoryName, date));
-                }
-                DailyLogEntry digest = dailydigests.get(dateStr);
-                if (commit.getParentCount() == 0) {
-                       linearParent = null;
-                       digest.updateRef(branch, ReceiveCommand.Type.CREATE);
-                } else {
-                       linearParent = commit.getParents()[0].getId().getName();
-                       digest.updateRef(branch, ReceiveCommand.Type.UPDATE, linearParent, commit.getName());
-                }
-                
-                RepositoryCommit repoCommit = digest.addCommit(branch, commit);
-                if (repoCommit != null) {
-                       List<RefModel> matchedRefs = allRefs.get(commit.getId());
-                    repoCommit.setRefs(matchedRefs);
-                    
-                    if (!ArrayUtils.isEmpty(matchedRefs)) {
-                        for (RefModel ref : matchedRefs) {
-                            if (ref.getName().startsWith(Constants.R_TAGS)) {
-                                // treat tags as special events in the log
-                                if (!tags.containsKey(dateStr)) {
-                                               UserModel tagUser = newUserModelFrom(commit.getAuthorIdent());
-                                               Date tagDate = commit.getAuthorIdent().getWhen();
-                                               tags.put(dateStr, new DailyLogEntry(repositoryName, tagDate, tagUser));
-                                }
-                                PushLogEntry tagEntry = tags.get(dateStr);
-                                tagEntry.updateRef(ref.getName(), ReceiveCommand.Type.CREATE);
-                                tagEntry.addCommit(ref.getName(), commit);
-                            } else if (ref.getName().startsWith(Constants.R_PULL)) {
-                                // treat pull requests as special events in the log
-                                if (!pulls.containsKey(dateStr)) {
-                                               UserModel commitUser = newUserModelFrom(commit.getAuthorIdent());
-                                               Date commitDate = commit.getAuthorIdent().getWhen();
-                                               pulls.put(dateStr, new DailyLogEntry(repositoryName, commitDate, commitUser));
-                                }
-                                PushLogEntry pullEntry = pulls.get(dateStr);
-                                pullEntry.updateRef(ref.getName(), ReceiveCommand.Type.CREATE);
-                                pullEntry.addCommit(ref.getName(), commit);
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        List<DailyLogEntry> list = new ArrayList<DailyLogEntry>(dailydigests.values());
-        list.addAll(tags.values());
-        //list.addAll(pulls.values());
-        Collections.sort(list);
-        return list;
-    }
-
-    /**
-     * Returns the list of commits separated by ref (e.g. each ref has it's own
-     * PushLogEntry object for each day).
-     *
-     * @param repositoryName
-     * @param repository
-     * @param minimumDate
-     * @param the timezone to use when aggregating commits by date
-     * @return a list of push log entries separated by ref and date
-     */
-    public static List<DailyLogEntry> getDailyLogByRef(String repositoryName, Repository repository,
-               Date minimumDate, TimeZone timezone) {
-        // break the push log into ref push logs and then merge them back into a list
-        Map<String, List<DailyLogEntry>> refMap = new HashMap<String, List<DailyLogEntry>>();
-        List<DailyLogEntry> pushes = getDailyLog(repositoryName, repository, minimumDate, 0, -1, timezone);
-        for (DailyLogEntry push : pushes) {
-            for (String ref : push.getChangedRefs()) {
-                if (!refMap.containsKey(ref)) {
-                    refMap.put(ref, new ArrayList<DailyLogEntry>());
-                }
-
-                // construct new ref-specific push log entry
-                DailyLogEntry refPush = new DailyLogEntry(push.repository, push.date, push.user);
-                refPush.updateRef(ref, push.getChangeType(ref), push.getOldId(ref), push.getNewId(ref));
-                refPush.addCommits(push.getCommits(ref));
-                refMap.get(ref).add(refPush);
-            }
-        }
-
-        // merge individual ref pushes into master list
-        List<DailyLogEntry> refPushLog = new ArrayList<DailyLogEntry>();
-        for (List<DailyLogEntry> refPush : refMap.values()) {
-               for (DailyLogEntry entry : refPush) {
-                       if (entry.getCommitCount() > 0) {
-                               refPushLog.add(entry);
-                       }
-               }
-        }
-
-        // sort ref push log
-        Collections.sort(refPushLog);
-
-        return refPushLog;
-    }
-}
diff --git a/src/main/java/com/gitblit/utils/RefLogUtils.java b/src/main/java/com/gitblit/utils/RefLogUtils.java
new file mode 100644 (file)
index 0000000..ce03a16
--- /dev/null
@@ -0,0 +1,659 @@
+/*
+ * Copyright 2013 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.io.IOException;
+import java.text.DateFormat;
+import java.text.MessageFormat;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TimeZone;
+import java.util.TreeSet;
+
+import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException;
+import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.dircache.DirCache;
+import org.eclipse.jgit.dircache.DirCacheBuilder;
+import org.eclipse.jgit.dircache.DirCacheEntry;
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.CommitBuilder;
+import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.RefRename;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.RefUpdate.Result;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.transport.ReceiveCommand;
+import org.eclipse.jgit.treewalk.CanonicalTreeParser;
+import org.eclipse.jgit.treewalk.TreeWalk;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.gitblit.Constants;
+import com.gitblit.models.DailyLogEntry;
+import com.gitblit.models.PathModel.PathChangeModel;
+import com.gitblit.models.RefLogEntry;
+import com.gitblit.models.RefModel;
+import com.gitblit.models.RepositoryCommit;
+import com.gitblit.models.UserModel;
+
+/**
+ * Utility class for maintaining a reflog within a git repository on an
+ * orphan branch.
+ * 
+ * @author James Moger
+ *
+ */
+public class RefLogUtils {
+       
+       private static final String GB_REFLOG = "refs/gitblit/reflog";
+
+       private static final Logger LOGGER = LoggerFactory.getLogger(RefLogUtils.class);
+
+       /**
+        * Log an error message and exception.
+        * 
+        * @param t
+        * @param repository
+        *            if repository is not null it MUST be the {0} parameter in the
+        *            pattern.
+        * @param pattern
+        * @param objects
+        */
+       private static void error(Throwable t, Repository repository, String pattern, Object... objects) {
+               List<Object> parameters = new ArrayList<Object>();
+               if (objects != null && objects.length > 0) {
+                       for (Object o : objects) {
+                               parameters.add(o);
+                       }
+               }
+               if (repository != null) {
+                       parameters.add(0, repository.getDirectory().getAbsolutePath());
+               }
+               LOGGER.error(MessageFormat.format(pattern, parameters.toArray()), t);
+       }
+
+       /**
+        * Returns a RefModel for the reflog branch in the repository. If the
+        * branch can not be found, null is returned.
+        * 
+        * @param repository
+        * @return a refmodel for the reflog branch or null
+        */
+       public static RefModel getRefLogBranch(Repository repository) {
+               List<RefModel> refs = JGitUtils.getRefs(repository, com.gitblit.Constants.R_GITBLIT);
+               RefModel pushLog = null;
+               final String GB_PUSHES = "refs/gitblit/pushes";
+               for (RefModel ref : refs) {
+                       if (ref.reference.getName().equals(GB_REFLOG)) {
+                               return ref;
+                       } else if (ref.reference.getName().equals(GB_PUSHES)) {
+                               pushLog = ref;
+                       }
+               }
+               if (pushLog != null) {
+                       // rename refs/gitblit/pushes to refs/gitblit/reflog
+                       RefRename cmd;
+                       try {
+                               cmd = repository.renameRef(GB_PUSHES, GB_REFLOG);
+                               cmd.setRefLogIdent(new PersonIdent("Gitblit", "gitblit@localhost"));
+                               cmd.setRefLogMessage("renamed " + GB_PUSHES + " => " + GB_REFLOG);
+                               Result res = cmd.rename();
+                               switch (res) {
+                               case RENAMED:
+                                       return getRefLogBranch(repository);
+                               default:
+                                       LOGGER.error("failed to rename " + GB_PUSHES + " => " + GB_REFLOG + " (" + res.name() + ")");
+                               }
+                       } catch (IOException e) {
+                               LOGGER.error("failed to rename pushlog", e);
+                       }
+               }
+               return null;
+       }
+       
+       private static UserModel newUserModelFrom(PersonIdent ident) {
+               String name = ident.getName();
+               String username;
+               String displayname;
+               if (name.indexOf('/') > -1) {
+                       int slash = name.indexOf('/');
+                       displayname = name.substring(0, slash);
+                       username = name.substring(slash + 1);
+               } else {
+                       displayname = name;
+                       username = ident.getEmailAddress();
+               }
+               
+               UserModel user = new UserModel(username);
+               user.displayName = displayname;
+               user.emailAddress = ident.getEmailAddress();
+               return user;
+       }
+       
+       /**
+        * Updates the reflog with the received commands.
+        * 
+        * @param user
+        * @param repository
+        * @param commands
+        * @return true, if the update was successful
+        */
+       public static boolean updateRefLog(UserModel user, Repository repository,
+                       Collection<ReceiveCommand> commands) {
+               RefModel reflogBranch = getRefLogBranch(repository);
+               if (reflogBranch == null) {
+                       JGitUtils.createOrphanBranch(repository, GB_REFLOG, null);
+               }
+               
+               boolean success = false;
+               String message = "push";
+               
+               try {
+                       ObjectId headId = repository.resolve(GB_REFLOG + "^{commit}");
+                       ObjectInserter odi = repository.newObjectInserter();
+                       try {
+                               // Create the in-memory index of the push log entry
+                               DirCache index = createIndex(repository, headId, commands);
+                               ObjectId indexTreeId = index.writeTree(odi);
+
+                               PersonIdent ident;
+                               if (UserModel.ANONYMOUS.equals(user)) {
+                                       // anonymous push
+                                       ident = new PersonIdent("anonymous", "anonymous");
+                               } else {
+                                       // construct real pushing account
+                                       ident = new PersonIdent(MessageFormat.format("{0}/{1}", user.getDisplayName(), user.username),
+                                               user.emailAddress == null ? user.username : user.emailAddress);
+                               }
+
+                               // Create a commit object
+                               CommitBuilder commit = new CommitBuilder();
+                               commit.setAuthor(ident);
+                               commit.setCommitter(ident);
+                               commit.setEncoding(Constants.ENCODING);
+                               commit.setMessage(message);
+                               commit.setParentId(headId);
+                               commit.setTreeId(indexTreeId);
+
+                               // Insert the commit into the repository
+                               ObjectId commitId = odi.insert(commit);
+                               odi.flush();
+
+                               RevWalk revWalk = new RevWalk(repository);
+                               try {
+                                       RevCommit revCommit = revWalk.parseCommit(commitId);
+                                       RefUpdate ru = repository.updateRef(GB_REFLOG);
+                                       ru.setNewObjectId(commitId);
+                                       ru.setExpectedOldObjectId(headId);
+                                       ru.setRefLogMessage("commit: " + revCommit.getShortMessage(), false);
+                                       Result rc = ru.forceUpdate();
+                                       switch (rc) {
+                                       case NEW:
+                                       case FORCED:
+                                       case FAST_FORWARD:
+                                               success = true;
+                                               break;
+                                       case REJECTED:
+                                       case LOCK_FAILURE:
+                                               throw new ConcurrentRefUpdateException(JGitText.get().couldNotLockHEAD,
+                                                               ru.getRef(), rc);
+                                       default:
+                                               throw new JGitInternalException(MessageFormat.format(
+                                                               JGitText.get().updatingRefFailed, GB_REFLOG, commitId.toString(),
+                                                               rc));
+                                       }
+                               } finally {
+                                       revWalk.release();
+                               }
+                       } finally {
+                               odi.release();
+                       }
+               } catch (Throwable t) {
+                       error(t, repository, "Failed to commit reflog entry to {0}");
+               }
+               return success;
+       }
+       
+       /**
+        * Creates an in-memory index of the push log entry.
+        * 
+        * @param repo
+        * @param headId
+        * @param commands
+        * @return an in-memory index
+        * @throws IOException
+        */
+       private static DirCache createIndex(Repository repo, ObjectId headId, 
+                       Collection<ReceiveCommand> commands) throws IOException {
+
+               DirCache inCoreIndex = DirCache.newInCore();
+               DirCacheBuilder dcBuilder = inCoreIndex.builder();
+               ObjectInserter inserter = repo.newObjectInserter();
+
+               long now = System.currentTimeMillis();
+               Set<String> ignorePaths = new TreeSet<String>();
+               try {
+                       // add receive commands to the temporary index
+                       for (ReceiveCommand command : commands) {
+                               // use the ref names as the path names
+                               String path = command.getRefName();
+                               ignorePaths.add(path);
+
+                               StringBuilder change = new StringBuilder();
+                               change.append(command.getType().name()).append(' ');
+                               switch (command.getType()) {
+                               case CREATE:
+                                       change.append(ObjectId.zeroId().getName());
+                                       change.append(' ');
+                                       change.append(command.getNewId().getName());
+                                       break;
+                               case UPDATE:
+                               case UPDATE_NONFASTFORWARD:
+                                       change.append(command.getOldId().getName());
+                                       change.append(' ');
+                                       change.append(command.getNewId().getName());
+                                       break;
+                               case DELETE:
+                                       change = null;
+                                       break;
+                               }
+                               if (change == null) {
+                                       // ref deleted
+                                       continue;
+                               }
+                               String content = change.toString();
+                               
+                               // create an index entry for this attachment
+                               final DirCacheEntry dcEntry = new DirCacheEntry(path);
+                               dcEntry.setLength(content.length());
+                               dcEntry.setLastModified(now);
+                               dcEntry.setFileMode(FileMode.REGULAR_FILE);
+
+                               // insert object
+                               dcEntry.setObjectId(inserter.insert(org.eclipse.jgit.lib.Constants.OBJ_BLOB, content.getBytes("UTF-8")));
+
+                               // add to temporary in-core index
+                               dcBuilder.add(dcEntry);
+                       }
+
+                       // Traverse HEAD to add all other paths
+                       TreeWalk treeWalk = new TreeWalk(repo);
+                       int hIdx = -1;
+                       if (headId != null)
+                               hIdx = treeWalk.addTree(new RevWalk(repo).parseTree(headId));
+                       treeWalk.setRecursive(true);
+
+                       while (treeWalk.next()) {
+                               String path = treeWalk.getPathString();
+                               CanonicalTreeParser hTree = null;
+                               if (hIdx != -1)
+                                       hTree = treeWalk.getTree(hIdx, CanonicalTreeParser.class);
+                               if (!ignorePaths.contains(path)) {
+                                       // add entries from HEAD for all other paths
+                                       if (hTree != null) {
+                                               // create a new DirCacheEntry with data retrieved from
+                                               // HEAD
+                                               final DirCacheEntry dcEntry = new DirCacheEntry(path);
+                                               dcEntry.setObjectId(hTree.getEntryObjectId());
+                                               dcEntry.setFileMode(hTree.getEntryFileMode());
+
+                                               // add to temporary in-core index
+                                               dcBuilder.add(dcEntry);
+                                       }
+                               }
+                       }
+
+                       // release the treewalk
+                       treeWalk.release();
+
+                       // finish temporary in-core index used for this commit
+                       dcBuilder.finish();
+               } finally {
+                       inserter.release();
+               }
+               return inCoreIndex;
+       }
+       
+       public static List<RefLogEntry> getRefLog(String repositoryName, Repository repository) {
+               return getRefLog(repositoryName, repository, null, 0, -1);
+       }
+
+       public static List<RefLogEntry> getRefLog(String repositoryName, Repository repository, int maxCount) {
+               return getRefLog(repositoryName, repository, null, 0, maxCount);
+       }
+
+       public static List<RefLogEntry> getRefLog(String repositoryName, Repository repository, int offset, int maxCount) {
+               return getRefLog(repositoryName, repository, null, offset, maxCount);
+       }
+
+       public static List<RefLogEntry> getRefLog(String repositoryName, Repository repository, Date minimumDate) {
+               return getRefLog(repositoryName, repository, minimumDate, 0, -1);
+       }
+       
+       /**
+        * Returns the list of reflog entries as they were recorded by Gitblit.
+        * Each RefLogEntry may represent multiple ref updates.
+        * 
+        * @param repositoryName
+        * @param repository
+        * @param minimumDate
+        * @param offset
+        * @param maxCount
+        *                      if < 0, all pushes are returned.
+        * @return a list of push log entries
+        */
+       public static List<RefLogEntry> getRefLog(String repositoryName, Repository repository,
+                       Date minimumDate, int offset, int maxCount) {
+               List<RefLogEntry> list = new ArrayList<RefLogEntry>();
+               RefModel ref = getRefLogBranch(repository);
+               if (ref == null) {
+                       return list;
+               }
+               if (maxCount == 0) {
+                       return list;
+               }
+               
+               Map<ObjectId, List<RefModel>> allRefs = JGitUtils.getAllRefs(repository);
+               List<RevCommit> pushes;
+               if (minimumDate == null) {
+                       pushes = JGitUtils.getRevLog(repository, GB_REFLOG, offset, maxCount);
+               } else {
+                       pushes = JGitUtils.getRevLog(repository, GB_REFLOG, minimumDate);
+               }
+               for (RevCommit push : pushes) {
+                       if (push.getAuthorIdent().getName().equalsIgnoreCase("gitblit")) {
+                               // skip gitblit/internal commits
+                               continue;
+                       }
+
+                       UserModel user = newUserModelFrom(push.getAuthorIdent());
+                       Date date = push.getAuthorIdent().getWhen();
+                       
+                       RefLogEntry log = new RefLogEntry(repositoryName, date, user);
+                       list.add(log);
+                       List<PathChangeModel> changedRefs = JGitUtils.getFilesInCommit(repository, push);
+                       for (PathChangeModel change : changedRefs) {
+                               switch (change.changeType) {
+                               case DELETE:
+                                       log.updateRef(change.path, ReceiveCommand.Type.DELETE);
+                                       break;
+                               case ADD:
+                                       log.updateRef(change.path, ReceiveCommand.Type.CREATE);
+                               default:
+                                       String content = JGitUtils.getStringContent(repository, push.getTree(), change.path);
+                                       String [] fields = content.split(" ");
+                                       String oldId = fields[1];
+                                       String newId = fields[2];
+                                       log.updateRef(change.path, ReceiveCommand.Type.valueOf(fields[0]), oldId, newId);
+                                       List<RevCommit> pushedCommits = JGitUtils.getRevLog(repository, oldId, newId);
+                                       for (RevCommit pushedCommit : pushedCommits) {
+                                               RepositoryCommit repoCommit = log.addCommit(change.path, pushedCommit);
+                                               if (repoCommit != null) {
+                                                       repoCommit.setRefs(allRefs.get(pushedCommit.getId()));
+                                               }
+                                       }
+                               }
+                       }
+               }
+               Collections.sort(list);
+               return list;
+       }
+
+       /**
+        * Returns the list of pushes separated by ref (e.g. each ref has it's own
+        * PushLogEntry object).
+        *  
+        * @param repositoryName
+        * @param repository
+        * @param maxCount
+        * @return a list of push log entries separated by ref
+        */
+       public static List<RefLogEntry> getLogByRef(String repositoryName, Repository repository, int maxCount) {
+               return getLogByRef(repositoryName, repository, 0, maxCount);
+       }
+       
+       /**
+        * Returns the list of pushes separated by ref (e.g. each ref has it's own
+        * PushLogEntry object).
+        *  
+        * @param repositoryName
+        * @param repository
+        * @param offset
+        * @param maxCount
+        * @return a list of push log entries separated by ref
+        */
+       public static List<RefLogEntry> getLogByRef(String repositoryName, Repository repository,  int offset,
+                       int maxCount) {
+               // break the push log into ref push logs and then merge them back into a list
+               Map<String, List<RefLogEntry>> refMap = new HashMap<String, List<RefLogEntry>>();
+        List<RefLogEntry> refLog = getRefLog(repositoryName, repository, offset, maxCount);
+               for (RefLogEntry entry : refLog) {
+                       for (String ref : entry.getChangedRefs()) {
+                               if (!refMap.containsKey(ref)) {
+                                       refMap.put(ref, new ArrayList<RefLogEntry>());
+                               }
+                               
+                               // construct new ref-specific ref change entry
+                               RefLogEntry refChange;
+                               if (entry instanceof DailyLogEntry) {
+                                       // simulated push log from commits grouped by date
+                                       refChange = new DailyLogEntry(entry.repository, entry.date);
+                               } else {
+                                       // real push log entry
+                                       refChange = new RefLogEntry(entry.repository, entry.date, entry.user);
+                               }
+                               refChange.updateRef(ref, entry.getChangeType(ref), entry.getOldId(ref), entry.getNewId(ref));
+                               refChange.addCommits(entry.getCommits(ref));
+                               refMap.get(ref).add(refChange);
+                       }
+               }
+               
+               // merge individual ref changes into master list
+               List<RefLogEntry> mergedRefLog = new ArrayList<RefLogEntry>();
+               for (List<RefLogEntry> refPush : refMap.values()) {
+                       mergedRefLog.addAll(refPush);
+               }
+               
+               // sort ref log
+               Collections.sort(mergedRefLog);
+               
+               return mergedRefLog;
+       }
+       
+       /**
+        * Returns the list of ref changes separated by ref (e.g. each ref has it's own
+        * RefLogEntry object).
+        *  
+        * @param repositoryName
+        * @param repository
+        * @param minimumDate
+        * @return a list of ref log entries separated by ref
+        */
+       public static List<RefLogEntry> getLogByRef(String repositoryName, Repository repository,  Date minimumDate) {
+               // break the push log into ref push logs and then merge them back into a list
+               Map<String, List<RefLogEntry>> refMap = new HashMap<String, List<RefLogEntry>>();
+               List<RefLogEntry> pushes = getRefLog(repositoryName, repository, minimumDate);
+               for (RefLogEntry push : pushes) {
+                       for (String ref : push.getChangedRefs()) {
+                               if (!refMap.containsKey(ref)) {
+                                       refMap.put(ref, new ArrayList<RefLogEntry>());
+                               }
+
+                // construct new ref-specific push log entry
+                RefLogEntry refPush = new RefLogEntry(push.repository, push.date, push.user);
+                refPush.updateRef(ref, push.getChangeType(ref), push.getOldId(ref), push.getNewId(ref));
+                               refPush.addCommits(push.getCommits(ref));
+                               refMap.get(ref).add(refPush);
+                       }
+               }
+               
+               // merge individual ref pushes into master list
+               List<RefLogEntry> refPushLog = new ArrayList<RefLogEntry>();
+               for (List<RefLogEntry> refPush : refMap.values()) {
+                       refPushLog.addAll(refPush);
+               }
+               
+               // sort ref push log
+               Collections.sort(refPushLog);
+               
+               return refPushLog;
+       }
+
+    /**
+     * Returns a commit log grouped by day.
+     *
+     * @param repositoryName
+     * @param repository
+     * @param minimumDate
+     * @param offset
+     * @param maxCount
+     *                         if < 0, all pushes are returned.
+     * @param the timezone to use when aggregating commits by date
+     * @return a list of grouped commit log entries
+     */
+    public static List<DailyLogEntry> getDailyLog(String repositoryName, Repository repository,
+                                                 Date minimumDate, int offset, int maxCount,
+                                                 TimeZone timezone) {
+        DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
+               df.setTimeZone(timezone);
+
+        Map<ObjectId, List<RefModel>> allRefs = JGitUtils.getAllRefs(repository);
+        Map<String, DailyLogEntry> tags = new HashMap<String, DailyLogEntry>();
+        Map<String, DailyLogEntry> pulls = new HashMap<String, DailyLogEntry>();
+        Map<String, DailyLogEntry> dailydigests = new HashMap<String, DailyLogEntry>();
+        String linearParent = null;
+        for (RefModel local : JGitUtils.getLocalBranches(repository, true, -1)) {
+            String branch = local.getName();
+            List<RevCommit> commits = JGitUtils.getRevLog(repository, branch, minimumDate);
+            for (RevCommit commit : commits) {
+               if (linearParent != null) {
+                       if (!commit.getName().equals(linearParent)) {
+                               // only follow linear branch commits
+                               continue;
+                       }
+               }
+                Date date = JGitUtils.getCommitDate(commit);
+                String dateStr = df.format(date);
+                if (!dailydigests.containsKey(dateStr)) {
+                    dailydigests.put(dateStr, new DailyLogEntry(repositoryName, date));
+                }
+                DailyLogEntry digest = dailydigests.get(dateStr);
+                if (commit.getParentCount() == 0) {
+                       linearParent = null;
+                       digest.updateRef(branch, ReceiveCommand.Type.CREATE);
+                } else {
+                       linearParent = commit.getParents()[0].getId().getName();
+                       digest.updateRef(branch, ReceiveCommand.Type.UPDATE, linearParent, commit.getName());
+                }
+                
+                RepositoryCommit repoCommit = digest.addCommit(branch, commit);
+                if (repoCommit != null) {
+                       List<RefModel> matchedRefs = allRefs.get(commit.getId());
+                    repoCommit.setRefs(matchedRefs);
+                    
+                    if (!ArrayUtils.isEmpty(matchedRefs)) {
+                        for (RefModel ref : matchedRefs) {
+                            if (ref.getName().startsWith(Constants.R_TAGS)) {
+                                // treat tags as special events in the log
+                                if (!tags.containsKey(dateStr)) {
+                                               UserModel tagUser = newUserModelFrom(commit.getAuthorIdent());
+                                               Date tagDate = commit.getAuthorIdent().getWhen();
+                                               tags.put(dateStr, new DailyLogEntry(repositoryName, tagDate, tagUser));
+                                }
+                                RefLogEntry tagEntry = tags.get(dateStr);
+                                tagEntry.updateRef(ref.getName(), ReceiveCommand.Type.CREATE);
+                                tagEntry.addCommit(ref.getName(), commit);
+                            } else if (ref.getName().startsWith(Constants.R_PULL)) {
+                                // treat pull requests as special events in the log
+                                if (!pulls.containsKey(dateStr)) {
+                                               UserModel commitUser = newUserModelFrom(commit.getAuthorIdent());
+                                               Date commitDate = commit.getAuthorIdent().getWhen();
+                                               pulls.put(dateStr, new DailyLogEntry(repositoryName, commitDate, commitUser));
+                                }
+                                RefLogEntry pullEntry = pulls.get(dateStr);
+                                pullEntry.updateRef(ref.getName(), ReceiveCommand.Type.CREATE);
+                                pullEntry.addCommit(ref.getName(), commit);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        List<DailyLogEntry> list = new ArrayList<DailyLogEntry>(dailydigests.values());
+        list.addAll(tags.values());
+        //list.addAll(pulls.values());
+        Collections.sort(list);
+        return list;
+    }
+
+    /**
+     * Returns the list of commits separated by ref (e.g. each ref has it's own
+     * PushLogEntry object for each day).
+     *
+     * @param repositoryName
+     * @param repository
+     * @param minimumDate
+     * @param the timezone to use when aggregating commits by date
+     * @return a list of push log entries separated by ref and date
+     */
+    public static List<DailyLogEntry> getDailyLogByRef(String repositoryName, Repository repository,
+               Date minimumDate, TimeZone timezone) {
+        // break the push log into ref push logs and then merge them back into a list
+        Map<String, List<DailyLogEntry>> refMap = new HashMap<String, List<DailyLogEntry>>();
+        List<DailyLogEntry> pushes = getDailyLog(repositoryName, repository, minimumDate, 0, -1, timezone);
+        for (DailyLogEntry push : pushes) {
+            for (String ref : push.getChangedRefs()) {
+                if (!refMap.containsKey(ref)) {
+                    refMap.put(ref, new ArrayList<DailyLogEntry>());
+                }
+
+                // construct new ref-specific push log entry
+                DailyLogEntry refPush = new DailyLogEntry(push.repository, push.date, push.user);
+                refPush.updateRef(ref, push.getChangeType(ref), push.getOldId(ref), push.getNewId(ref));
+                refPush.addCommits(push.getCommits(ref));
+                refMap.get(ref).add(refPush);
+            }
+        }
+
+        // merge individual ref pushes into master list
+        List<DailyLogEntry> refPushLog = new ArrayList<DailyLogEntry>();
+        for (List<DailyLogEntry> refPush : refMap.values()) {
+               for (DailyLogEntry entry : refPush) {
+                       if (entry.getCommitCount() > 0) {
+                               refPushLog.add(entry);
+                       }
+               }
+        }
+
+        // sort ref push log
+        Collections.sort(refPushLog);
+
+        return refPushLog;
+    }
+}
index f4180faa1f6aea6f30f852928f7f36044d7c2ecd..cdae09318805eb439fa5d98ba2d85657511ae0a9 100644 (file)
@@ -44,18 +44,18 @@ import com.gitblit.wicket.pages.ForksPage;
 import com.gitblit.wicket.pages.GitSearchPage;\r
 import com.gitblit.wicket.pages.GravatarProfilePage;\r
 import com.gitblit.wicket.pages.HistoryPage;\r
-import com.gitblit.wicket.pages.DashboardPage;\r
 import com.gitblit.wicket.pages.LogPage;\r
 import com.gitblit.wicket.pages.LogoutPage;\r
 import com.gitblit.wicket.pages.LuceneSearchPage;\r
 import com.gitblit.wicket.pages.MarkdownPage;\r
 import com.gitblit.wicket.pages.MetricsPage;\r
+import com.gitblit.wicket.pages.MyDashboardPage;\r
 import com.gitblit.wicket.pages.OverviewPage;\r
 import com.gitblit.wicket.pages.PatchPage;\r
 import com.gitblit.wicket.pages.ProjectPage;\r
 import com.gitblit.wicket.pages.ProjectsPage;\r
-import com.gitblit.wicket.pages.PushesPage;\r
 import com.gitblit.wicket.pages.RawPage;\r
+import com.gitblit.wicket.pages.ReflogPage;\r
 import com.gitblit.wicket.pages.RepositoriesPage;\r
 import com.gitblit.wicket.pages.ReviewProposalPage;\r
 import com.gitblit.wicket.pages.SummaryPage;\r
@@ -69,7 +69,7 @@ import com.gitblit.wicket.pages.UsersPage;
 \r
 public class GitBlitWebApp extends WebApplication {\r
 \r
-       public final static Class<? extends BasePage> HOME_PAGE_CLASS = DashboardPage.class;\r
+       public final static Class<? extends BasePage> HOME_PAGE_CLASS = MyDashboardPage.class;\r
        \r
        @Override\r
        public void init() {\r
@@ -98,7 +98,7 @@ public class GitBlitWebApp extends WebApplication {
                mount("/repositories", RepositoriesPage.class);\r
                mount("/overview", OverviewPage.class, "r", "h");\r
                mount("/summary", SummaryPage.class, "r");\r
-               mount("/pushes", PushesPage.class, "r", "h");\r
+               mount("/reflog", ReflogPage.class, "r", "h");\r
                mount("/commits", LogPage.class, "r", "h");\r
                mount("/log", LogPage.class, "r", "h");\r
                mount("/tags", TagsPage.class, "r");\r
index 2e17e605f1a094a4c25344767106313d1af7c106..f7e72ca99275ee4f84da1d4496d308567cc7a931 100644 (file)
@@ -462,8 +462,7 @@ gb.to = to
 gb.at = at
 gb.of = of
 gb.in = in
-gb.morePushes = all pushes...
-gb.pushes = pushes
+gb.moreChanges = all changes...
 gb.pushedNCommitsTo = pushed {0} commits to
 gb.pushedOneCommitTo = pushed 1 commit to
 gb.commitsTo = {0} commits to
@@ -488,8 +487,12 @@ gb.stargazers = stargazers
 gb.starredRepositories = starred repositories
 gb.failedToUpdateUser = Failed to update user account!
 gb.myRepositories = my repositories
-gb.noActivity = there has been no recent commit activity
+gb.noActivity = there has been no activity in the last {0} days
 gb.findSomeRepositories = find some repositories
 gb.metricAuthorExclusions = author metric exclusions
 gb.myDashboard = my dashboard
-gb.failedToFindAccount = failed to find user account ''{0}''
\ No newline at end of file
+gb.failedToFindAccount = failed to find user account ''{0}''
+gb.reflog = reflog
+gb.active = active
+gb.starred = starred
+gb.owned = owned
\ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/DashboardPage.html b/src/main/java/com/gitblit/wicket/pages/DashboardPage.html
deleted file mode 100644 (file)
index 1da7ef4..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-<!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:extend>\r
-<div class="container">\r
-       <div class="hidden-phone markdown" style="padding-bottom:5px;" wicket:id="repositoriesMessage">[repositories message]</div>\r
-       \r
-       <div class="row">\r
-               <div class="span7">\r
-                       <div style="color:#888;font-size:1.75em;padding-bottom:5px;" wicket:id="feedheader"></div>\r
-                       <div class="hidden-phone hidden-tablet"  style="text-align:center;">\r
-                               <table>\r
-                                       <tr>\r
-                                               <td><div id="chartRepositories" style="display:inline-block;width: 175px; height:175px"></div></td>\r
-                                               <td><div id="chartAuthors" style="display:inline-block;width: 175px; height: 175px;"></div></td>\r
-                                       </tr>\r
-                               </table>\r
-                       </div>\r
-                       <div wicket:id="digests"></div>\r
-               </div>\r
-               <div class="span5">\r
-                       <div wicket:id="active">[active]</div>\r
-                       <div wicket:id="starred">[starred]</div>\r
-                       <div wicket:id="owned">[owned]</div>\r
-               </div>\r
-               \r
-       </div>\r
-</div>\r
-\r
-<wicket:fragment wicket:id="starredListFragment">\r
-       <div ng-controller="starredCtrl" style="border: 1px solid #ddd;border-radius: 4px;margin-bottom: 20px;">\r
-               <div class="header" style="padding: 5px;border: none;"><i class="icon-star"></i> <wicket:message key="gb.starredRepositories"></wicket:message> ({{starred.length}})\r
-                       <div class="pull-right">\r
-                               <a class="btn btn-mini">more</a>\r
-                       </div>\r
-                       <div style="padding: 5px 0px 0px;">\r
-                               <input type="text" ng-model="query.r" class="input-large" wicket:message="placeholder:gb.filter" style="border-radius: 14px; padding: 3px 14px;margin: 0px;"></input>\r
-                       </div>\r
-               </div>\r
-               \r
-               <div ng-repeat="item in starred | limitTo: 20 | filter:query" style="padding: 3px;border-top: 1px solid #ddd;">\r
-                       <b><span class="repositorySwatch" style="background-color:{{item.c}};"><span ng-show="item.wc">!</span><span ng-show="!item.wc">&nbsp;</span></span></b>\r
-                       <a href="summary/?r={{item.r}}">{{item.p}}<b>{{item.n}}</b></a>\r
-                       <span class="link hidden-tablet hidden-phone" style="color: #aaa;" title="{{item.d}}">{{item.t}}</span>\r
-                       <span ng-show="item.s" class="pull-right">\r
-                               <span style="padding: 0px 5px;color: #888;font-weight:bold;vertical-align:middle;">{{item.s | number}} <i style="vertical-align:baseline;" class="iconic-star"></i></span>\r
-                       </span>\r
-               </div>\r
-               \r
-       </div>\r
-</wicket:fragment>\r
-\r
-<wicket:fragment wicket:id="ownedListFragment">\r
-       <div ng-controller="ownedCtrl" style="border: 1px solid #ddd;border-radius: 4px;">\r
-               <div class="header" style="padding: 5px;border: none;"><i class="icon-user"></i> <wicket:message key="gb.myRepositories"></wicket:message> ({{owned.length}})\r
-                       <div class="hidden-phone pull-right">\r
-                               <span wicket:id="create"></span>\r
-                       </div>\r
-                       <div style="padding: 5px 0px 0px;">\r
-                               <input type="text" ng-model="query.r" class="input-large" wicket:message="placeholder:gb.filter" style="border-radius: 14px; padding: 3px 14px;margin: 0px;"></input>\r
-                       </div>\r
-               </div>\r
-               \r
-               <div ng-repeat="item in owned | filter:query" style="padding: 3px;border-top: 1px solid #ddd;">\r
-                       <b><span class="repositorySwatch" style="background-color:{{item.c}};"><span ng-show="item.wc">!</span><span ng-show="!item.wc">&nbsp;</span></span></b>\r
-                       <a href="summary/?r={{item.r}}">{{item.p}}<b>{{item.n}}</b></a>\r
-                       <span class="link hidden-tablet hidden-phone" style="color: #bbb;" title="{{item.d}}">{{item.t}}</span>\r
-                       <span ng-show="item.s" class="pull-right">\r
-                               <span style="padding: 0px 5px;color: #888;font-weight:bold;vertical-align:middle;">{{item.s | number}} <i style="vertical-align:baseline;" class="iconic-star"></i></span>\r
-                       </span>\r
-               </div>          \r
-       </div>\r
-</wicket:fragment>\r
-\r
-<wicket:fragment wicket:id="activeListFragment">\r
-       <div ng-controller="activeCtrl" style="border: 1px solid #ddd;border-radius: 4px;margin-bottom: 20px;">\r
-               <div class="header" style="padding: 5px;border: none;"><i class="icon-user"></i> <wicket:message key="gb.activeRepositories"></wicket:message> ({{active.length}})\r
-                       <div style="padding: 5px 0px 0px;">\r
-                               <input type="text" ng-model="query.r" class="input-large" wicket:message="placeholder:gb.filter" style="border-radius: 14px; padding: 3px 14px;margin: 0px;"></input>\r
-                       </div>\r
-               </div>\r
-               \r
-               <div ng-repeat="item in active | filter:query" style="padding: 3px;border-top: 1px solid #ddd;">\r
-                       <b><span class="repositorySwatch" style="background-color:{{item.c}};"><span ng-show="item.wc">!</span><span ng-show="!item.wc">&nbsp;</span></span></b>\r
-                       <a href="summary/?r={{item.r}}">{{item.p}}<b>{{item.n}}</b></a>\r
-                       <span class="link hidden-tablet hidden-phone" style="color: #bbb;" title="{{item.d}}">{{item.t}}</span>\r
-                       <span ng-show="item.s" class="pull-right">\r
-                               <span style="padding: 0px 5px;color: #888;font-weight:bold;vertical-align:middle;">{{item.s | number}} <i style="vertical-align:baseline;" class="iconic-star"></i></span>\r
-                       </span>\r
-               </div>          \r
-       </div>\r
-</wicket:fragment>\r
-\r
-</wicket:extend>\r
-</body>\r
-</html>
\ No newline at end of file
index 6c328b10b6ee1a0963165faa2494935881988803..1c45b64a4238489c898c4e823926cbeae7d617ba 100644 (file)
  */\r
 package com.gitblit.wicket.pages;\r
 \r
-import java.io.File;\r
-import java.io.FileInputStream;\r
-import java.io.InputStream;\r
-import java.io.InputStreamReader;\r
 import java.io.Serializable;\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.Collection;\r
 import java.util.Collections;\r
-import java.util.Comparator;\r
 import java.util.Date;\r
 import java.util.HashMap;\r
-import java.util.HashSet;\r
 import java.util.List;\r
 import java.util.Map;\r
 import java.util.Set;\r
 import java.util.TimeZone;\r
 import java.util.TreeSet;\r
 \r
-import org.apache.wicket.Component;\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.apache.wicket.markup.html.panel.Fragment;\r
-import org.eclipse.jgit.lib.Constants;\r
 import org.eclipse.jgit.lib.Repository;\r
 \r
 import com.gitblit.GitBlit;\r
 import com.gitblit.Keys;\r
 import com.gitblit.models.DailyLogEntry;\r
 import com.gitblit.models.Metric;\r
-import com.gitblit.models.PushLogEntry;\r
+import com.gitblit.models.RefLogEntry;\r
 import com.gitblit.models.RepositoryCommit;\r
 import com.gitblit.models.RepositoryModel;\r
 import com.gitblit.models.UserModel;\r
 import com.gitblit.utils.ArrayUtils;\r
-import com.gitblit.utils.MarkdownUtils;\r
-import com.gitblit.utils.PushLogUtils;\r
+import com.gitblit.utils.RefLogUtils;\r
 import com.gitblit.utils.StringUtils;\r
 import com.gitblit.wicket.GitBlitWebApp;\r
-import com.gitblit.wicket.GitBlitWebSession;\r
 import com.gitblit.wicket.PageRegistration;\r
 import com.gitblit.wicket.PageRegistration.DropDownMenuItem;\r
 import com.gitblit.wicket.PageRegistration.DropDownMenuRegistration;\r
@@ -66,19 +57,17 @@ import com.gitblit.wicket.charting.GoogleChart;
 import com.gitblit.wicket.charting.GoogleCharts;\r
 import com.gitblit.wicket.charting.GooglePieChart;\r
 import com.gitblit.wicket.ng.NgController;\r
+import com.gitblit.wicket.panels.DigestsPanel;\r
 import com.gitblit.wicket.panels.LinkPanel;\r
-import com.gitblit.wicket.panels.PushesPanel;\r
 \r
-public class DashboardPage extends RootPage {\r
+public abstract class DashboardPage extends RootPage {\r
 \r
        public DashboardPage() {\r
                super();\r
-               setup(null);\r
        }\r
 \r
        public DashboardPage(PageParameters params) {\r
                super(params);\r
-               setup(params);\r
        }\r
 \r
        @Override\r
@@ -86,115 +75,50 @@ public class DashboardPage extends RootPage {
                return true;\r
        }\r
 \r
-       private void setup(PageParameters params) {\r
-               setupPage("", "");\r
-               // check to see if we should display a login message\r
-               boolean authenticateView = GitBlit.getBoolean(Keys.web.authenticateViewPages, true);\r
-               if (authenticateView && !GitBlitWebSession.get().isLoggedIn()) {\r
-                       String messageSource = GitBlit.getString(Keys.web.loginMessage, "gitblit");\r
-                       String message = readMarkdown(messageSource, "login.mkd");\r
-                       Component repositoriesMessage = new Label("repositoriesMessage", message);\r
-                       add(repositoriesMessage.setEscapeModelStrings(false));\r
-                       add(new Label("digests"));\r
-                       add(new Label("active").setVisible(false));\r
-                       add(new Label("starred").setVisible(false));\r
-                       add(new Label("owned").setVisible(false));\r
-                       add(new Label("feedheader").setVisible(false));\r
-                       return;\r
-               }\r
-\r
-               // Load the markdown welcome message\r
-               String messageSource = GitBlit.getString(Keys.web.repositoriesMessage, "gitblit");\r
-               String message = readMarkdown(messageSource, "welcome.mkd");\r
-               Component repositoriesMessage = new Label("repositoriesMessage", message)\r
-                               .setEscapeModelStrings(false).setVisible(message.length() > 0);\r
-               add(repositoriesMessage);\r
-\r
-               UserModel user = GitBlitWebSession.get().getUser();\r
-               if (user == null) {\r
-                       user = UserModel.ANONYMOUS;\r
-               }\r
-\r
-               Comparator<RepositoryModel> lastUpdateSort = new Comparator<RepositoryModel>() {\r
-                       @Override\r
-                       public int compare(RepositoryModel o1, RepositoryModel o2) {\r
-                               return o2.lastChange.compareTo(o1.lastChange);\r
-                       }\r
-               };\r
-               \r
-               // parameters\r
-               int daysBack = params == null ? 0 : WicketUtils.getDaysBack(params);\r
-               if (daysBack < 1) {\r
-                       daysBack = 7;\r
-               }\r
+       protected void addActivity(UserModel user, Collection<RepositoryModel> repositories, int daysBack) {\r
                Calendar c = Calendar.getInstance();\r
                c.add(Calendar.DATE, -1*daysBack);\r
                Date minimumDate = c.getTime();\r
                TimeZone timezone = getTimeZone();\r
                \r
-               // build repo lists \r
-               List<RepositoryModel> starred = new ArrayList<RepositoryModel>();\r
-               List<RepositoryModel> owned = new ArrayList<RepositoryModel>();\r
-               List<RepositoryModel> active = new ArrayList<RepositoryModel>();\r
-\r
-               for (RepositoryModel model : getRepositoryModels()) {\r
-                       if (model.isUsersPersonalRepository(user.username) || model.isOwner(user.username)) {\r
-                               owned.add(model);\r
-                       }\r
-                       \r
-                       if (user.getPreferences().isStarredRepository(model.name)) {\r
-                               starred.add(model);\r
-                       }\r
-                       \r
-                       if (model.isShowActivity() && model.lastChange.after(minimumDate)) {\r
-                               active.add(model);\r
-                       }\r
-               }\r
-               \r
-               Collections.sort(owned, lastUpdateSort);\r
-               Collections.sort(starred, lastUpdateSort);\r
-               Collections.sort(active, lastUpdateSort);\r
-               \r
-               Set<RepositoryModel> feedSources = new HashSet<RepositoryModel>();\r
-               feedSources.addAll(starred);\r
-               if (feedSources.isEmpty()) {\r
-                       feedSources.addAll(active);\r
-               }\r
-               \r
                // create daily commit digest feed\r
-               List<PushLogEntry> pushes = new ArrayList<PushLogEntry>();\r
-               for (RepositoryModel model : feedSources) {\r
+               List<DailyLogEntry> digests = new ArrayList<DailyLogEntry>();\r
+               for (RepositoryModel model : repositories) {\r
                        Repository repository = GitBlit.self().getRepository(model.name);\r
-                       List<DailyLogEntry> entries = PushLogUtils.getDailyLogByRef(model.name, repository, minimumDate, timezone);\r
-                       pushes.addAll(entries);\r
+                       List<DailyLogEntry> entries = RefLogUtils.getDailyLogByRef(model.name, repository, minimumDate, timezone);\r
+                       digests.addAll(entries);\r
                        repository.close();\r
                }\r
                \r
-               if (pushes.size() == 0) {\r
+               Fragment activityFragment = new Fragment("activity", "activityFragment", this);\r
+               add(activityFragment);\r
+               if (digests.size() == 0) {\r
                        // quiet or no starred repositories\r
-                       if (feedSources.size() == 0) {\r
+                       if (repositories.size() == 0) {\r
                                if (UserModel.ANONYMOUS.equals(user)) {\r
-                                       add(new Label("digests", getString("gb.noActivity")));  \r
+                                       activityFragment.add(new Label("digests", MessageFormat.format(getString("gb.noActivity"), daysBack))); \r
                                } else {\r
-                                       add(new LinkPanel("digests", null, getString("gb.findSomeRepositories"), RepositoriesPage.class));\r
+                                       activityFragment.add(new LinkPanel("digests", null, getString("gb.findSomeRepositories"), RepositoriesPage.class));\r
                                }\r
                        } else {\r
-                               add(new Label("digests", getString("gb.noActivity")));\r
+                               activityFragment.add(new Label("digests", MessageFormat.format(getString("gb.noActivity"), daysBack)));\r
                        }\r
                } else {\r
                        // show daily commit digest feed\r
-                       Collections.sort(pushes);\r
-                       add(new PushesPanel("digests", pushes));\r
+                       Collections.sort(digests);\r
+                       DigestsPanel digestsPanel = new DigestsPanel("digests", digests);\r
+                       WicketUtils.setCssStyle(digestsPanel,  "margin-top:-20px");\r
+                       activityFragment.add(digestsPanel);\r
                }\r
                \r
                // add the nifty charts\r
-               if (!ArrayUtils.isEmpty(pushes)) {\r
+               if (!ArrayUtils.isEmpty(digests)) {\r
                        // aggregate author exclusions\r
                        Set<String> authorExclusions = new TreeSet<String>();\r
                        for (String author : GitBlit.getStrings(Keys.web.metricAuthorExclusions)) {\r
                                authorExclusions.add(author.toLowerCase());\r
                        }\r
-                       for (RepositoryModel model : feedSources) {\r
+                       for (RepositoryModel model : repositories) {\r
                                if (!ArrayUtils.isEmpty(model.metricAuthorExclusions)) {\r
                                        for (String author : model.metricAuthorExclusions) {\r
                                                authorExclusions.add(author.toLowerCase());\r
@@ -202,40 +126,10 @@ public class DashboardPage extends RootPage {
                                }\r
                        }\r
 \r
-                       addCharts(pushes, authorExclusions, daysBack);\r
-               } else {\r
-                       add(new Label("feedheader").setVisible(false));\r
-               }\r
-               \r
-               // active repository list\r
-               if (starred.isEmpty()) {\r
-                       Fragment activeView = createNgList("active", "activeListFragment", "activeCtrl", active);\r
-                       add(activeView);\r
+                       addCharts(activityFragment, digests, authorExclusions, daysBack);\r
                } else {\r
-                       add(new Label("active").setVisible(false));\r
-               }\r
-               \r
-               // starred repository list\r
-               if (ArrayUtils.isEmpty(starred)) {\r
-                       add(new Label("starred").setVisible(false));\r
-               } else {\r
-                       Fragment starredView = createNgList("starred", "starredListFragment", "starredCtrl", starred);\r
-                       add(starredView);\r
-               }\r
-               \r
-               // owned repository list\r
-               if (ArrayUtils.isEmpty(owned)) {\r
-                       add(new Label("owned").setVisible(false));\r
-               } else {\r
-                       Fragment ownedView = createNgList("owned", "ownedListFragment", "ownedCtrl", owned);\r
-                       if (user.canCreate) {\r
-                               // create button\r
-                               ownedView.add(new LinkPanel("create", "btn btn-mini", getString("gb.newRepository"), EditRepositoryPage.class));\r
-                       } else {\r
-                               // no button\r
-                               ownedView.add(new Label("create").setVisible(false));\r
-                       }\r
-                       add(ownedView);\r
+                       activityFragment.add(new Label("charts").setVisible(false));\r
+                       activityFragment.add(new Label("feedheader").setVisible(false));\r
                }\r
        }\r
        \r
@@ -259,6 +153,7 @@ public class DashboardPage extends RootPage {
                        item.n = name;\r
                        item.p = path;\r
                        item.r = repo.name;\r
+                       item.i = repo.description;\r
                        item.s = GitBlit.self().getStarCount(repo);\r
                        item.t = getTimeUtils().timeAgo(repo.lastChange);\r
                        item.d = df.format(repo.lastChange);\r
@@ -295,104 +190,32 @@ public class DashboardPage extends RootPage {
                pages.add(menu);\r
        }\r
 \r
-       private String readMarkdown(String messageSource, String resource) {\r
-               String message = "";\r
-               if (messageSource.equalsIgnoreCase("gitblit")) {\r
-                       // Read default message\r
-                       message = readDefaultMarkdown(resource);\r
-               } else {\r
-                       // Read user-supplied message\r
-                       if (!StringUtils.isEmpty(messageSource)) {\r
-                               File file = GitBlit.getFileOrFolder(messageSource);\r
-                               if (file.exists()) {\r
-                                       try {\r
-                                               FileInputStream fis = new FileInputStream(file);\r
-                                               InputStreamReader reader = new InputStreamReader(fis,\r
-                                                               Constants.CHARACTER_ENCODING);\r
-                                               message = MarkdownUtils.transformMarkdown(reader);\r
-                                               reader.close();\r
-                                       } catch (Throwable t) {\r
-                                               message = getString("gb.failedToRead") + " " + file;\r
-                                               warn(message, t);\r
-                                       }\r
-                               } else {\r
-                                       message = messageSource + " " + getString("gb.isNotValidFile");\r
-                               }\r
-                       }\r
-               }\r
-               return message;\r
-       }\r
-\r
-       private String readDefaultMarkdown(String file) {\r
-               String base = file.substring(0, file.lastIndexOf('.'));\r
-               String ext = file.substring(file.lastIndexOf('.'));\r
-               String lc = getLanguageCode();\r
-               String cc = getCountryCode();\r
-\r
-               // try to read file_en-us.ext, file_en.ext, file.ext\r
-               List<String> files = new ArrayList<String>();\r
-               if (!StringUtils.isEmpty(lc)) {\r
-                       if (!StringUtils.isEmpty(cc)) {\r
-                               files.add(base + "_" + lc + "-" + cc + ext);\r
-                               files.add(base + "_" + lc + "_" + cc + ext);\r
-                       }\r
-                       files.add(base + "_" + lc + ext);\r
-               }\r
-               files.add(file);\r
 \r
-               for (String name : files) {\r
-                       String message;\r
-                       InputStreamReader reader = null;\r
-                       try {\r
-                               InputStream is = getClass().getResourceAsStream("/" + name);\r
-                               if (is == null) {\r
-                                       continue;\r
-                               }\r
-                               reader = new InputStreamReader(is, Constants.CHARACTER_ENCODING);\r
-                               message = MarkdownUtils.transformMarkdown(reader);\r
-                               reader.close();\r
-                               return message;\r
-                       } catch (Throwable t) {\r
-                               message = MessageFormat.format(getString("gb.failedToReadMessage"), file);\r
-                               error(message, t, false);\r
-                               return message;\r
-                       } finally {\r
-                               if (reader != null) {\r
-                                       try {\r
-                                               reader.close();\r
-                                       } catch (Exception e) {\r
-                                       }\r
-                               }\r
-                       }                       \r
-               }\r
-               return MessageFormat.format(getString("gb.failedToReadMessage"), file);\r
-       }\r
-       \r
        /**\r
         * Creates the daily activity line chart, the active repositories pie chart,\r
         * and the active authors pie chart\r
         * \r
-        * @param recentPushes\r
+        * @param recentChanges\r
         * @param authorExclusions\r
         * @param daysBack\r
         */\r
-       private void addCharts(List<PushLogEntry> recentPushes, Set<String> authorExclusions, int daysBack) {\r
+       protected void addCharts(Fragment frag, List<DailyLogEntry> recentChanges, Set<String> authorExclusions, int daysBack) {\r
                // activity metrics\r
                Map<String, Metric> repositoryMetrics = new HashMap<String, Metric>();\r
                Map<String, Metric> authorMetrics = new HashMap<String, Metric>();\r
 \r
                // aggregate repository and author metrics\r
                int totalCommits = 0;\r
-               for (PushLogEntry push : recentPushes) {\r
+               for (RefLogEntry change : recentChanges) {\r
 \r
                        // aggregate repository metrics\r
-                       String repository = StringUtils.stripDotGit(push.repository);\r
+                       String repository = StringUtils.stripDotGit(change.repository);\r
                        if (!repositoryMetrics.containsKey(repository)) {\r
                                repositoryMetrics.put(repository, new Metric(repository));\r
                        }\r
                        repositoryMetrics.get(repository).count += 1;\r
                        \r
-                       for (RepositoryCommit commit : push.getCommits()) {\r
+                       for (RepositoryCommit commit : change.getCommits()) {\r
                                totalCommits++;\r
                                String author = StringUtils.removeNewlines(commit.getAuthorIdent().getName());\r
                                String authorName = author.toLowerCase();\r
@@ -406,7 +229,7 @@ public class DashboardPage extends RootPage {
                        }\r
                }\r
                \r
-               add(new Label("feedheader", MessageFormat.format(getString("gb.recentActivityStats"),\r
+               frag.add(new Label("feedheader", MessageFormat.format(getString("gb.recentActivityStats"),\r
                                daysBack, totalCommits, authorMetrics.size())));\r
 \r
                // build google charts\r
@@ -430,10 +253,11 @@ public class DashboardPage extends RootPage {
                chart.setShowLegend(false);\r
                charts.addChart(chart);\r
 \r
-               add(new HeaderContributor(charts));\r
+               add(new HeaderContributor(charts));             \r
+               frag.add(new Fragment("charts", "chartsFragment", this));\r
        }\r
        \r
-       class RepoListItem implements Serializable {\r
+       protected class RepoListItem implements Serializable {\r
 \r
                private static final long serialVersionUID = 1L;\r
                \r
@@ -442,6 +266,7 @@ public class DashboardPage extends RootPage {
                String p; // project/path\r
                String t; // time ago\r
                String d; // last updated\r
+               String i; // information/description\r
                long s; // stars\r
                String c; // html color\r
                int wc; // working copy, 1 = true\r
diff --git a/src/main/java/com/gitblit/wicket/pages/MyDashboardPage.html b/src/main/java/com/gitblit/wicket/pages/MyDashboardPage.html
new file mode 100644 (file)
index 0000000..6b78b14
--- /dev/null
@@ -0,0 +1,158 @@
+<!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:extend>\r
+<div class="container">\r
+\r
+       <div class="row" style="padding-top:5px;">\r
+               <div class="span7">\r
+                       <div class="hidden-phone markdown" style="padding-bottom: 30px;" wicket:id="repositoriesMessage">[repositories message]</div>\r
+                       <div wicket:id="activity"></div>\r
+               </div>\r
+               <div class="span5">\r
+                       <div wicket:id="repositoryTabs"></div>\r
+               </div>          \r
+       </div>\r
+</div>\r
+\r
+<wicket:fragment wicket:id="anonymousTabsFragment">\r
+       <ul class="nav nav-pills">\r
+               <li class="active"><a href="#recent" data-toggle="tab"><wicket:message key="gb.active">[active]</wicket:message></a></li>\r
+               <li><a href="#projects" data-toggle="tab"><wicket:message key="gb.projects">[projects]</wicket:message></a></li>\r
+       </ul>\r
+       <div class="tab-content">\r
+               <div class="tab-pane active" id="recent">\r
+                       <div wicket:id="active">[recently active]</div>\r
+               </div>\r
+               <div class="tab-pane" id="projects">\r
+                       <div wicket:id="projectList">[all projects]</div>\r
+               </div>\r
+       </div>\r
+</wicket:fragment>\r
+\r
+<wicket:fragment wicket:id="authenticatedTabsFragment">\r
+       <ul class="nav nav-pills">\r
+               <li class="active"><a href="#starred" data-toggle="tab"><wicket:message key="gb.starred">[starred]</wicket:message></a></li>\r
+               <li><a href="#owned" data-toggle="tab"><wicket:message key="gb.owned">[owned]</wicket:message></a></li>\r
+               <li><a href="#recent" data-toggle="tab"><wicket:message key="gb.active">[active]</wicket:message></a></li>\r
+               <li><a href="#projects" data-toggle="tab"><wicket:message key="gb.projects">[projects]</wicket:message></a></li>\r
+       </ul>\r
+       <div class="tab-content">\r
+               <div class="tab-pane active" id="starred">\r
+                       <div wicket:id="starred">[starred repositories]</div>\r
+               </div>\r
+               <div class="tab-pane" id="owned">\r
+                       <div wicket:id="owned">[my repositories]</div>\r
+               </div>\r
+               <div class="tab-pane" id="recent">\r
+                       <div wicket:id="active">[recently active]</div>\r
+               </div>\r
+               <div class="tab-pane" id="projects">\r
+                       <div wicket:id="projectList">[all projects]</div>\r
+               </div>\r
+       </div>\r
+</wicket:fragment>\r
+\r
+<wicket:fragment wicket:id="activityFragment">\r
+       <div class="dashboardTitle"><wicket:message key="gb.recentActivity"></wicket:message> <small><span wicket:id="feedheader"></span></small></div>\r
+       <div class="hidden-phone hidden-tablet"  style="text-align:center;">\r
+               <div wicket:id="charts"></div>\r
+       </div>\r
+       <div wicket:id="digests"></div>\r
+</wicket:fragment>\r
+\r
+<wicket:fragment wicket:id="chartsFragment">\r
+       <table>\r
+               <tr>\r
+                       <td><div id="chartRepositories" style="display:inline-block;width: 175px; height:175px"></div></td>\r
+                       <td><div id="chartAuthors" style="display:inline-block;width: 175px; height: 175px;"></div></td>\r
+               </tr>\r
+       </table>\r
+</wicket:fragment>\r
+\r
+<wicket:fragment wicket:id="starredListFragment">\r
+       <div ng-controller="starredCtrl" style="border: 1px solid #ddd;border-radius: 4px;margin-bottom: 20px;">\r
+               <div class="header" style="padding: 5px;border: none;"><i class="icon-star"></i> <wicket:message key="gb.starredRepositories"></wicket:message> ({{starred.length}})\r
+                       <div style="padding: 5px 0px 0px;">\r
+                               <input type="text" ng-model="query.r" class="input-large" wicket:message="placeholder:gb.filter" style="border-radius: 14px; padding: 3px 14px;margin: 0px;"></input>\r
+                       </div>\r
+               </div>\r
+               \r
+               <div ng-repeat="item in starred | filter:query" style="padding: 3px;border-top: 1px solid #ddd;">\r
+                       <b><span class="repositorySwatch" style="background-color:{{item.c}};"><span ng-show="item.wc">!</span><span ng-show="!item.wc">&nbsp;</span></span></b>\r
+                       <a href="summary/?r={{item.r}}" title="{{item.i}}">{{item.p}}<b>{{item.n}}</b></a>\r
+                       <span class="link hidden-tablet hidden-phone" style="color: #aaa;" title="{{item.d}}">{{item.t}}</span>\r
+                       <span ng-show="item.s" class="pull-right">\r
+                               <span style="padding: 0px 5px;color: #888;font-weight:bold;vertical-align:middle;">{{item.s | number}} <i style="vertical-align:baseline;" class="iconic-star"></i></span>\r
+                       </span>\r
+               </div>\r
+               \r
+       </div>\r
+</wicket:fragment>\r
+\r
+<wicket:fragment wicket:id="ownedListFragment">\r
+       <div ng-controller="ownedCtrl" style="border: 1px solid #ddd;border-radius: 4px;">\r
+               <div class="header" style="padding: 5px;border: none;"><i class="icon-user"></i> <wicket:message key="gb.myRepositories"></wicket:message> ({{owned.length}})\r
+                       <div class="hidden-phone pull-right">\r
+                               <span wicket:id="create"></span>\r
+                       </div>\r
+                       <div style="padding: 5px 0px 0px;">\r
+                               <input type="text" ng-model="query.r" class="input-large" wicket:message="placeholder:gb.filter" style="border-radius: 14px; padding: 3px 14px;margin: 0px;"></input>\r
+                       </div>\r
+               </div>\r
+               \r
+               <div ng-repeat="item in owned | filter:query" style="padding: 3px;border-top: 1px solid #ddd;">\r
+                       <b><span class="repositorySwatch" style="background-color:{{item.c}};"><span ng-show="item.wc">!</span><span ng-show="!item.wc">&nbsp;</span></span></b>\r
+                       <a href="summary/?r={{item.r}}" title="{{item.i}}">{{item.p}}<b>{{item.n}}</b></a>\r
+                       <span class="link hidden-tablet hidden-phone" style="color: #bbb;" title="{{item.d}}">{{item.t}}</span>\r
+                       <span ng-show="item.s" class="pull-right">\r
+                               <span style="padding: 0px 5px;color: #888;font-weight:bold;vertical-align:middle;">{{item.s | number}} <i style="vertical-align:baseline;" class="iconic-star"></i></span>\r
+                       </span>\r
+               </div>          \r
+       </div>\r
+</wicket:fragment>\r
+\r
+<wicket:fragment wicket:id="activeListFragment">\r
+       <div ng-controller="activeCtrl" style="border: 1px solid #ddd;border-radius: 4px;margin-bottom: 20px;">\r
+               <div class="header" style="padding: 5px;border: none;"><i class="icon-user"></i> <wicket:message key="gb.activeRepositories"></wicket:message> ({{active.length}})\r
+                       <div style="padding: 5px 0px 0px;">\r
+                               <input type="text" ng-model="query.r" class="input-large" wicket:message="placeholder:gb.filter" style="border-radius: 14px; padding: 3px 14px;margin: 0px;"></input>\r
+                       </div>\r
+               </div>\r
+               \r
+               <div ng-repeat="item in active | filter:query" style="padding: 3px;border-top: 1px solid #ddd;">\r
+                       <b><span class="repositorySwatch" style="background-color:{{item.c}};"><span ng-show="item.wc">!</span><span ng-show="!item.wc">&nbsp;</span></span></b>\r
+                       <a href="summary/?r={{item.r}}" title="{{item.i}}">{{item.p}}<b>{{item.n}}</b></a>\r
+                       <span class="link hidden-tablet hidden-phone" style="color: #bbb;" title="{{item.d}}">{{item.t}}</span>\r
+                       <span ng-show="item.s" class="pull-right">\r
+                               <span style="padding: 0px 5px;color: #888;font-weight:bold;vertical-align:middle;">{{item.s | number}} <i style="vertical-align:baseline;" class="iconic-star"></i></span>\r
+                       </span>\r
+               </div>          \r
+       </div>\r
+</wicket:fragment>\r
+\r
+<wicket:fragment wicket:id="projectListFragment">\r
+       <div ng-controller="projectListCtrl" style="border: 1px solid #ddd;border-radius: 4px;margin-bottom: 20px;">\r
+               <div class="header" style="padding: 5px;border: none;"><i class="icon-folder-close"></i> <wicket:message key="gb.projects"></wicket:message> ({{projectList.length}})\r
+                       <div style="padding: 5px 0px 0px;">\r
+                               <input type="text" ng-model="query.n" class="input-large" wicket:message="placeholder:gb.filter" style="border-radius: 14px; padding: 3px 14px;margin: 0px;"></input>\r
+                       </div>\r
+               </div>\r
+               \r
+               <div ng-repeat="item in projectList | filter:query" style="padding: 3px;border-top: 1px solid #ddd;">\r
+                       <a href="project/{{item.p}}" title="{{item.i}}"><b>{{item.n}}</b></a>\r
+                       <span class="link hidden-tablet hidden-phone" style="color: #bbb;" title="{{item.d}}">{{item.t}}</span>\r
+                       <span class="pull-right">\r
+                               <span style="padding: 0px 5px;color: #888;font-weight:bold;vertical-align:middle;" wicket:message="title:gb.repositories">{{item.c | number}}</span>\r
+                       </span>\r
+               </div>\r
+       </div>\r
+</wicket:fragment>\r
+\r
+</wicket:extend>\r
+</body>\r
+</html>
\ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/MyDashboardPage.java b/src/main/java/com/gitblit/wicket/pages/MyDashboardPage.java
new file mode 100644 (file)
index 0000000..b0c89b7
--- /dev/null
@@ -0,0 +1,311 @@
+/*\r
+ * Copyright 2013 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.io.File;\r
+import java.io.FileInputStream;\r
+import java.io.InputStream;\r
+import java.io.InputStreamReader;\r
+import java.io.Serializable;\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.Comparator;\r
+import java.util.Date;\r
+import java.util.HashSet;\r
+import java.util.List;\r
+import java.util.Set;\r
+\r
+import org.apache.wicket.Component;\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.apache.wicket.markup.html.panel.Fragment;\r
+import org.eclipse.jgit.lib.Constants;\r
+\r
+import com.gitblit.GitBlit;\r
+import com.gitblit.Keys;\r
+import com.gitblit.models.ProjectModel;\r
+import com.gitblit.models.RepositoryModel;\r
+import com.gitblit.models.UserModel;\r
+import com.gitblit.utils.ArrayUtils;\r
+import com.gitblit.utils.MarkdownUtils;\r
+import com.gitblit.utils.StringUtils;\r
+import com.gitblit.wicket.GitBlitWebSession;\r
+import com.gitblit.wicket.WicketUtils;\r
+import com.gitblit.wicket.ng.NgController;\r
+import com.gitblit.wicket.panels.LinkPanel;\r
+\r
+public class MyDashboardPage extends DashboardPage {\r
+\r
+       public MyDashboardPage() {\r
+               super();\r
+               setup(null);\r
+       }\r
+\r
+       public MyDashboardPage(PageParameters params) {\r
+               super(params);\r
+               setup(params);\r
+       }\r
+\r
+       @Override\r
+       protected boolean reusePageParameters() {\r
+               return true;\r
+       }\r
+\r
+       private void setup(PageParameters params) {\r
+               setupPage("", "");\r
+               // check to see if we should display a login message\r
+               boolean authenticateView = GitBlit.getBoolean(Keys.web.authenticateViewPages, true);\r
+               if (authenticateView && !GitBlitWebSession.get().isLoggedIn()) {\r
+                       String messageSource = GitBlit.getString(Keys.web.loginMessage, "gitblit");\r
+                       String message = readMarkdown(messageSource, "login.mkd");\r
+                       Component repositoriesMessage = new Label("repositoriesMessage", message);\r
+                       add(repositoriesMessage.setEscapeModelStrings(false));\r
+                       add(new Label("activity").setVisible(false));\r
+                       add(new Label("repositoryTabs").setVisible(false));\r
+                       return;\r
+               }\r
+\r
+               // Load the markdown welcome message\r
+               String messageSource = GitBlit.getString(Keys.web.repositoriesMessage, "gitblit");\r
+               String message = readMarkdown(messageSource, "welcome.mkd");\r
+               Component repositoriesMessage = new Label("repositoriesMessage", message)\r
+                               .setEscapeModelStrings(false).setVisible(message.length() > 0);\r
+               add(repositoriesMessage);\r
+\r
+               UserModel user = GitBlitWebSession.get().getUser();\r
+               if (user == null) {\r
+                       user = UserModel.ANONYMOUS;\r
+               }\r
+\r
+               // parameters\r
+               int daysBack = params == null ? 0 : WicketUtils.getDaysBack(params);\r
+               if (daysBack < 1) {\r
+                       daysBack = 7;\r
+               }\r
+               Calendar c = Calendar.getInstance();\r
+               c.add(Calendar.DATE, -1*daysBack);\r
+               Date minimumDate = c.getTime();\r
+               \r
+               // build repo lists \r
+               List<RepositoryModel> starred = new ArrayList<RepositoryModel>();\r
+               List<RepositoryModel> owned = new ArrayList<RepositoryModel>();\r
+               List<RepositoryModel> active = new ArrayList<RepositoryModel>();\r
+\r
+               for (RepositoryModel model : getRepositoryModels()) {\r
+                       if (model.isUsersPersonalRepository(user.username) || model.isOwner(user.username)) {\r
+                               owned.add(model);\r
+                       }\r
+                       \r
+                       if (user.getPreferences().isStarredRepository(model.name)) {\r
+                               starred.add(model);\r
+                       }\r
+                       \r
+                       if (model.isShowActivity() && model.lastChange.after(minimumDate)) {\r
+                               active.add(model);\r
+                       }\r
+               }\r
+               \r
+               Comparator<RepositoryModel> lastUpdateSort = new Comparator<RepositoryModel>() {\r
+                       @Override\r
+                       public int compare(RepositoryModel o1, RepositoryModel o2) {\r
+                               return o2.lastChange.compareTo(o1.lastChange);\r
+                       }\r
+               };\r
+               \r
+               Collections.sort(owned, lastUpdateSort);\r
+               Collections.sort(starred, lastUpdateSort);\r
+               Collections.sort(active, lastUpdateSort);\r
+               \r
+               Set<RepositoryModel> feed = new HashSet<RepositoryModel>();\r
+               feed.addAll(starred);\r
+               feed.addAll(owned);\r
+               if (feed.isEmpty()) {\r
+                       feed.addAll(active);\r
+               }\r
+               \r
+               addActivity(user, feed, daysBack);\r
+               \r
+               Fragment repositoryTabs;\r
+               if (UserModel.ANONYMOUS.equals(user)) {\r
+                       repositoryTabs = new Fragment("repositoryTabs", "anonymousTabsFragment", this);\r
+               } else {\r
+                       repositoryTabs = new Fragment("repositoryTabs", "authenticatedTabsFragment", this);\r
+               }\r
+               \r
+               add(repositoryTabs);\r
+               \r
+               Fragment projectList = createProjectList();\r
+               repositoryTabs.add(projectList);\r
+               \r
+               // active repository list\r
+               if (active.isEmpty()) {\r
+                       repositoryTabs.add(new Label("active").setVisible(false));\r
+               } else {\r
+                       Fragment activeView = createNgList("active", "activeListFragment", "activeCtrl", active);\r
+                       repositoryTabs.add(activeView);\r
+               }\r
+               \r
+               // starred repository list\r
+               if (ArrayUtils.isEmpty(starred)) {\r
+                       repositoryTabs.add(new Label("starred").setVisible(false));\r
+               } else {\r
+                       Fragment starredView = createNgList("starred", "starredListFragment", "starredCtrl", starred);\r
+                       repositoryTabs.add(starredView);\r
+               }\r
+               \r
+               // owned repository list\r
+               if (ArrayUtils.isEmpty(owned)) {\r
+                       repositoryTabs.add(new Label("owned").setVisible(false));\r
+               } else {\r
+                       Fragment ownedView = createNgList("owned", "ownedListFragment", "ownedCtrl", owned);\r
+                       if (user.canCreate) {\r
+                               // create button\r
+                               ownedView.add(new LinkPanel("create", "btn btn-mini", getString("gb.newRepository"), EditRepositoryPage.class));\r
+                       } else {\r
+                               // no button\r
+                               ownedView.add(new Label("create").setVisible(false));\r
+                       }\r
+                       repositoryTabs.add(ownedView);\r
+               }\r
+       }\r
+       \r
+       private String readMarkdown(String messageSource, String resource) {\r
+               String message = "";\r
+               if (messageSource.equalsIgnoreCase("gitblit")) {\r
+                       // Read default message\r
+                       message = readDefaultMarkdown(resource);\r
+               } else {\r
+                       // Read user-supplied message\r
+                       if (!StringUtils.isEmpty(messageSource)) {\r
+                               File file = GitBlit.getFileOrFolder(messageSource);\r
+                               if (file.exists()) {\r
+                                       try {\r
+                                               FileInputStream fis = new FileInputStream(file);\r
+                                               InputStreamReader reader = new InputStreamReader(fis,\r
+                                                               Constants.CHARACTER_ENCODING);\r
+                                               message = MarkdownUtils.transformMarkdown(reader);\r
+                                               reader.close();\r
+                                       } catch (Throwable t) {\r
+                                               message = getString("gb.failedToRead") + " " + file;\r
+                                               warn(message, t);\r
+                                       }\r
+                               } else {\r
+                                       message = messageSource + " " + getString("gb.isNotValidFile");\r
+                               }\r
+                       }\r
+               }\r
+               return message;\r
+       }\r
+\r
+       private String readDefaultMarkdown(String file) {\r
+               String base = file.substring(0, file.lastIndexOf('.'));\r
+               String ext = file.substring(file.lastIndexOf('.'));\r
+               String lc = getLanguageCode();\r
+               String cc = getCountryCode();\r
+\r
+               // try to read file_en-us.ext, file_en.ext, file.ext\r
+               List<String> files = new ArrayList<String>();\r
+               if (!StringUtils.isEmpty(lc)) {\r
+                       if (!StringUtils.isEmpty(cc)) {\r
+                               files.add(base + "_" + lc + "-" + cc + ext);\r
+                               files.add(base + "_" + lc + "_" + cc + ext);\r
+                       }\r
+                       files.add(base + "_" + lc + ext);\r
+               }\r
+               files.add(file);\r
+\r
+               for (String name : files) {\r
+                       String message;\r
+                       InputStreamReader reader = null;\r
+                       try {\r
+                               InputStream is = getClass().getResourceAsStream("/" + name);\r
+                               if (is == null) {\r
+                                       continue;\r
+                               }\r
+                               reader = new InputStreamReader(is, Constants.CHARACTER_ENCODING);\r
+                               message = MarkdownUtils.transformMarkdown(reader);\r
+                               reader.close();\r
+                               return message;\r
+                       } catch (Throwable t) {\r
+                               message = MessageFormat.format(getString("gb.failedToReadMessage"), file);\r
+                               error(message, t, false);\r
+                               return message;\r
+                       } finally {\r
+                               if (reader != null) {\r
+                                       try {\r
+                                               reader.close();\r
+                                       } catch (Exception e) {\r
+                                       }\r
+                               }\r
+                       }                       \r
+               }\r
+               return MessageFormat.format(getString("gb.failedToReadMessage"), file);\r
+       }\r
+       \r
+       protected Fragment createProjectList() {\r
+               String format = GitBlit.getString(Keys.web.datestampShortFormat, "MM/dd/yy");\r
+               final DateFormat df = new SimpleDateFormat(format);\r
+               df.setTimeZone(getTimeZone());\r
+               List<ProjectModel> projects = GitBlit.self().getProjectModels(getRepositoryModels(), false);\r
+               Collections.sort(projects, new Comparator<ProjectModel>() {\r
+                       @Override\r
+                       public int compare(ProjectModel o1, ProjectModel o2) {\r
+                               return o2.lastChange.compareTo(o1.lastChange);\r
+                       }\r
+               });\r
+\r
+               List<ProjectListItem> list = new ArrayList<ProjectListItem>();\r
+               for (ProjectModel proj : projects) {\r
+                       if (proj.isUserProject() || proj.repositories.isEmpty()) {\r
+                               // exclude user projects from list\r
+                               continue;\r
+                       }\r
+                       ProjectListItem item = new ProjectListItem();\r
+                       item.p = proj.name;\r
+                       item.n = StringUtils.isEmpty(proj.title) ? proj.name : proj.title;\r
+                       item.i = proj.description;\r
+                       item.t = getTimeUtils().timeAgo(proj.lastChange);\r
+                       item.d = df.format(proj.lastChange);\r
+                       item.c = proj.repositories.size();\r
+                       list.add(item);\r
+               }\r
+               \r
+               // inject an AngularJS controller with static data\r
+               NgController ctrl = new NgController("projectListCtrl");\r
+               ctrl.addVariable("projectList", list);\r
+               add(new HeaderContributor(ctrl));\r
+               \r
+               Fragment fragment = new Fragment("projectList", "projectListFragment", this);\r
+               return fragment;\r
+       }\r
+       \r
+       protected class ProjectListItem implements Serializable {\r
+\r
+               private static final long serialVersionUID = 1L;\r
+               \r
+               String p; // path\r
+               String n; // name\r
+               String t; // time ago\r
+               String d; // last updated\r
+               String i; // information/description\r
+               long c;   // repository count\r
+       }\r
+}\r
index 3340e3185a4fddce742f700e7af085c7bf5d6244..995f8df6834398f0eb5b34490c9dcf993bc56208 100644 (file)
@@ -48,7 +48,7 @@
                <div class="span6">\r
                        <div class="hidden-tablet" style="padding-bottom: 10px; margin-bottom: 10px; border-bottom: 1px solid #ddd;" wicket:id="repositoryUrlPanel">[repository url panel]</div>\r
                \r
-                       <div wicket:id="pushesPanel">[pushes panel]</div>       \r
+                       <div wicket:id="reflogPanel">[reflog panel]</div>       \r
                </div>\r
        </div>\r
 \r
index 42d20c5bb0b512b98b332ce52318290f73d69c43..88487670bd0ee78599dcccb1e62d161a5ffa7e8a 100644 (file)
@@ -41,7 +41,7 @@ import com.gitblit.wicket.charting.GoogleCharts;
 import com.gitblit.wicket.charting.GoogleLineChart;\r
 import com.gitblit.wicket.panels.BranchesPanel;\r
 import com.gitblit.wicket.panels.LinkPanel;\r
-import com.gitblit.wicket.panels.PushesPanel;\r
+import com.gitblit.wicket.panels.ReflogPanel;\r
 import com.gitblit.wicket.panels.RepositoryUrlPanel;\r
 import com.gitblit.wicket.panels.TagsPanel;\r
 \r
@@ -113,9 +113,9 @@ public class OverviewPage extends RepositoryPage {
 \r
                add(new RepositoryUrlPanel("repositoryUrlPanel", false, user, model));\r
 \r
-               int pushCount = GitBlit.getInteger(Keys.web.overviewPushCount, 5);\r
-               PushesPanel pushes = new PushesPanel("pushesPanel", getRepositoryModel(), r, pushCount, 0, false);\r
-               add(pushes);\r
+               int reflogCount = GitBlit.getInteger(Keys.web.overviewReflogCount, 5);\r
+               ReflogPanel reflog = new ReflogPanel("reflogPanel", getRepositoryModel(), r, reflogCount, 0);\r
+               add(reflog);\r
                add(new TagsPanel("tagsPanel", repositoryName, r, numberRefs).hideIfEmpty());\r
                add(new BranchesPanel("branchesPanel", getRepositoryModel(), r, numberRefs, false).hideIfEmpty());\r
 \r
index 3b5d4e6a947e7c25c9618a04d021ba7b1086c8bc..9fbe1b2d69069ad6f8f6fb5d8913e2c064532012 100644 (file)
@@ -6,66 +6,70 @@
 \r
 <body>\r
 <wicket:extend>\r
-\r
-<div class="container">\r
-       <div class="row">\r
-               <div class="span12">\r
-                       <h2><span wicket:id="projectTitle"></span> <small><span wicket:id="projectDescription"></span></small>\r
-                               <a class="hidden-phone hidden-tablet brand" style="text-decoration: none;" wicket:id="syndication" wicket:message="title:gb.feed">\r
-                                       <img style="border:0px;vertical-align:middle;" src="feed_16x16.png"></img>\r
-                               </a>\r
-                       </h2>\r
-                       <div class="markdown" wicket:id="projectMessage">[project message]</div>\r
-               </div>\r
-       </div>\r
-\r
-       <div class="tabbable">\r
-               <!-- tab titles -->\r
-               <ul class="nav nav-tabs">\r
-                       <li class="active"><a href="#repositories" data-toggle="tab"><wicket:message key="gb.repositories"></wicket:message></a></li>\r
-                       <li ><a href="#activity" data-toggle="tab"><wicket:message key="gb.activity"></wicket:message></a></li>\r
-               </ul>\r
-       \r
-               <!-- tab content -->\r
-               <div class="tab-content">\r
-\r
-                       <!-- repositories tab -->\r
-                       <div class="tab-pane active" id="repositories">\r
-                               <!-- markdown -->\r
-                               <div class="row">\r
-                                       <div class="span12">\r
-                                               <div class="markdown" wicket:id="repositoriesMessage">[repositories message]</div>\r
+               <div class="container">\r
+                       <div class="row" style="padding-top:5px;">\r
+                               <div class="span12">\r
+                                       <div class="dashboardTitle">\r
+                                               <span wicket:id="projectTitle"></span>\r
+                                               <small><span wicket:id="projectDescription"></span></small>\r
+                                               \r
+                                               <a\r
+                                                       class="hidden-phone hidden-tablet brand"\r
+                                                       style="text-decoration: none;" wicket:id="syndication"\r
+                                                       wicket:message="title:gb.feed"> <img\r
+                                                       style="border: 0px; vertical-align: middle;" src="feed_16x16.png"></img>\r
+                                               </a>\r
                                        </div>\r
                                </div>\r
-                               <div class="row">\r
-                                       <div class="span6" wicket:id="repositoryList">\r
-                                               <span wicket:id="repository"></span>\r
-                                       </div>\r
-                               </div>                          \r
                        </div>\r
                        \r
-                       <!-- activity tab -->\r
-                       <div class="tab-pane" id="activity">\r
-                               <div class="pageTitle">\r
-                                       <h2><wicket:message key="gb.recentActivity"></wicket:message><small> <span class="hidden-phone">/ <span wicket:id="subheader">[days back]</span></span></small></h2>\r
+                       <div class="row">\r
+                               <div class="span7">                                     \r
+                                       <div class="markdown" style="padding-bottom: 30px;" wicket:id="projectMessage">[project message]</div>\r
+                                       <div wicket:id="activity">[activity panel]</div>\r
                                </div>\r
-                       \r
-                               <div class="hidden-phone" style="height: 155px;text-align: center;">\r
-                                       <table>\r
-                                       <tr>\r
-                                               <td><span class="hidden-tablet" id="chartDaily"></span></td>\r
-                                               <td><span id="chartRepositories"></span></td>\r
-                                               <td><span id="chartAuthors"></span></td>\r
-                                       </tr>\r
-                                       </table>\r
+                               <div class="span5">\r
+                                       <div class="markdown" wicket:id="repositoriesMessage">[repositories message]</div>\r
+                                       <div wicket:id="repositoryList">[repository list]</div>\r
                                </div>\r
-                       \r
-                               <div wicket:id="activityPanel">[activity panel]</div>\r
                        </div>\r
-               \r
                </div>\r
+\r
+<wicket:fragment wicket:id="activityFragment">\r
+       <div class="dashboardTitle"><wicket:message key="gb.recentActivity"></wicket:message> <small><span wicket:id="feedheader"></span></small></div>\r
+       <div class="hidden-phone hidden-tablet"  style="text-align:center;">\r
+               <div wicket:id="charts"></div>\r
        </div>\r
+       <div wicket:id="digests"></div>\r
+</wicket:fragment>\r
+\r
+<wicket:fragment wicket:id="chartsFragment">\r
+       <table>\r
+               <tr>\r
+                       <td><div id="chartRepositories" style="display:inline-block;width: 175px; height:175px"></div></td>\r
+                       <td><div id="chartAuthors" style="display:inline-block;width: 175px; height: 175px;"></div></td>\r
+               </tr>\r
+       </table>\r
+</wicket:fragment>\r
+               \r
+<wicket:fragment wicket:id="repositoryListFragment">\r
+       <div ng-controller="repositoryListCtrl" style="border: 1px solid #ddd;border-radius: 4px;margin-bottom: 20px;">\r
+               <div class="header" style="padding: 5px;border: none;"><img style="vertical-align: middle;" src="git-black-16x16.png"/> <wicket:message key="gb.repositories"></wicket:message> ({{repositoryList.length}})\r
+                       <div style="padding: 5px 0px 0px;">\r
+                               <input type="text" ng-model="query.r" class="input-large" wicket:message="placeholder:gb.filter" style="border-radius: 14px; padding: 3px 14px;margin: 0px;"></input>\r
+                       </div>\r
+               </div>\r
+               \r
+               <div ng-repeat="item in repositoryList | filter:query" style="padding: 3px;border-top: 1px solid #ddd;">\r
+                       <b><span class="repositorySwatch" style="background-color:{{item.c}};"><span ng-show="item.wc">!</span><span ng-show="!item.wc">&nbsp;</span></span></b>\r
+                       <a href="summary/?r={{item.r}}" title="{{item.i}}">{{item.p}}<b>{{item.n}}</b></a>\r
+                       <span class="link hidden-tablet hidden-phone" style="color: #bbb;" title="{{item.d}}">{{item.t}}</span>\r
+                       <span ng-show="item.s" class="pull-right">\r
+                               <span style="padding: 0px 5px;color: #888;font-weight:bold;vertical-align:middle;">{{item.s | number}} <i style="vertical-align:baseline;" class="iconic-star"></i></span>\r
+                       </span>\r
+               </div>          \r
        </div>\r
-</wicket:extend>\r
+</wicket:fragment>             \r
+       </wicket:extend>\r
 </body>\r
 </html>
\ No newline at end of file
index 7eba03315b467c9817d36063f61314875535c8d9..c64e900a54ea459f64f8e46168b1eadecfb4ccea 100644 (file)
  */\r
 package com.gitblit.wicket.pages;\r
 \r
-import java.text.MessageFormat;\r
-import java.text.SimpleDateFormat;\r
 import java.util.ArrayList;\r
 import java.util.Collections;\r
 import java.util.Comparator;\r
-import java.util.HashMap;\r
-import java.util.HashSet;\r
 import java.util.List;\r
-import java.util.Map;\r
-import java.util.Set;\r
 \r
 import org.apache.wicket.Component;\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.apache.wicket.markup.html.link.ExternalLink;\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
+import org.apache.wicket.markup.html.panel.Fragment;\r
 \r
 import com.gitblit.GitBlit;\r
 import com.gitblit.Keys;\r
 import com.gitblit.SyndicationServlet;\r
-import com.gitblit.models.Activity;\r
-import com.gitblit.models.Metric;\r
 import com.gitblit.models.ProjectModel;\r
 import com.gitblit.models.RepositoryModel;\r
-import com.gitblit.utils.ActivityUtils;\r
+import com.gitblit.models.UserModel;\r
 import com.gitblit.utils.MarkdownUtils;\r
 import com.gitblit.utils.StringUtils;\r
 import com.gitblit.wicket.GitBlitWebApp;\r
@@ -52,14 +41,8 @@ import com.gitblit.wicket.PageRegistration;
 import com.gitblit.wicket.PageRegistration.DropDownMenuItem;\r
 import com.gitblit.wicket.PageRegistration.DropDownMenuRegistration;\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
-import com.gitblit.wicket.panels.ProjectRepositoryPanel;\r
 \r
-public class ProjectPage extends RootPage {\r
+public class ProjectPage extends DashboardPage {\r
        \r
        List<ProjectModel> projectModels = new ArrayList<ProjectModel>();\r
 \r
@@ -72,10 +55,9 @@ public class ProjectPage extends RootPage {
                super(params);\r
                setup(params);\r
        }\r
-\r
-       @Override\r
-       protected boolean reusePageParameters() {\r
-               return true;\r
+       \r
+       protected Class<? extends BasePage> getRootNavPageClass() {\r
+               return RepositoriesPage.class;\r
        }\r
 \r
        private void setup(PageParameters params) {\r
@@ -118,8 +100,20 @@ public class ProjectPage extends RootPage {
                                .setEscapeModelStrings(false).setVisible(rmessage.length() > 0);\r
                add(repositoriesMessage);\r
 \r
-               List<RepositoryModel> repositories = getRepositories(params);\r
+               UserModel user = GitBlitWebSession.get().getUser();\r
+               if (user == null) {\r
+                       user = UserModel.ANONYMOUS;\r
+               }\r
+               int daysBack = params == null ? 0 : WicketUtils.getDaysBack(params);\r
+               if (daysBack < 1) {\r
+                       daysBack = 7;\r
+               }\r
+               // reset the daysback parameter so that we have a complete project\r
+               // repository list.  the recent activity will be built up by the\r
+               // reflog utils.\r
+               params.put("db", 0);\r
                \r
+               List<RepositoryModel> repositories = getRepositories(params);\r
                Collections.sort(repositories, new Comparator<RepositoryModel>() {\r
                        @Override\r
                        public int compare(RepositoryModel o1, RepositoryModel o2) {\r
@@ -128,145 +122,21 @@ public class ProjectPage extends RootPage {
                        }\r
                });\r
 \r
-               final ListDataProvider<RepositoryModel> dp = new ListDataProvider<RepositoryModel>(repositories);\r
-               DataView<RepositoryModel> dataView = new DataView<RepositoryModel>("repositoryList", dp) {\r
-                       private static final long serialVersionUID = 1L;\r
-\r
-                       public void populateItem(final Item<RepositoryModel> item) {\r
-                               final RepositoryModel entry = item.getModelObject();\r
-                               \r
-                               ProjectRepositoryPanel row = new ProjectRepositoryPanel("repository", \r
-                                               getLocalizer(), this, showAdmin, entry, getAccessRestrictions());\r
-                               item.add(row);\r
-                       }\r
-               };\r
-               add(dataView);\r
-\r
-               // project activity\r
-               // parameters\r
-               int daysBack = WicketUtils.getDaysBack(params);\r
-               if (daysBack < 1) {\r
-                       daysBack = 14;\r
-               }\r
-               String objectId = WicketUtils.getObject(params);\r
-\r
-               List<Activity> recentActivity = ActivityUtils.getRecentActivity(repositories, \r
-                               daysBack, objectId, getTimeZone());\r
-               if (recentActivity.size() == 0) {\r
-                       // no activity, skip graphs and activity panel\r
-                       add(new Label("subheader", MessageFormat.format(getString("gb.recentActivityNone"),\r
-                                       daysBack)));\r
-                       add(new Label("activityPanel"));\r
+               \r
+               addActivity(user, repositories, daysBack);\r
+               \r
+               if (repositories.isEmpty()) {\r
+                       add(new Label("repositoryList").setVisible(false));\r
                } else {\r
-                       // calculate total commits and total authors\r
-                       int totalCommits = 0;\r
-                       Set<String> uniqueAuthors = new HashSet<String>();\r
-                       for (Activity activity : recentActivity) {\r
-                               totalCommits += activity.getCommitCount();\r
-                               uniqueAuthors.addAll(activity.getAuthorMetrics().keySet());\r
-                       }\r
-                       int totalAuthors = uniqueAuthors.size();\r
-\r
-                       // add the subheader with stat numbers\r
-                       add(new Label("subheader", MessageFormat.format(getString("gb.recentActivityStats"),\r
-                                       daysBack, totalCommits, totalAuthors)));\r
-\r
-                       // create the activity charts\r
-                       GoogleCharts charts = createCharts(recentActivity);\r
-                       add(new HeaderContributor(charts));\r
-\r
-                       // add activity panel\r
-                       add(new ActivityPanel("activityPanel", recentActivity));\r
+                       Fragment activeView = createNgList("repositoryList", "repositoryListFragment", "repositoryListCtrl", repositories);\r
+                       add(activeView);\r
                }\r
        }\r
        \r
-       /**\r
-        * Creates the daily activity line chart, the active repositories pie chart,\r
-        * and the active authors pie chart\r
-        * \r
-        * @param recentActivity\r
-        * @return\r
-        */\r
-       private GoogleCharts createCharts(List<Activity> recentActivity) {\r
-               // activity metrics\r
-               Map<String, Metric> repositoryMetrics = new HashMap<String, Metric>();\r
-               Map<String, Metric> authorMetrics = new HashMap<String, Metric>();\r
-\r
-               // aggregate repository and author metrics\r
-               for (Activity activity : recentActivity) {\r
-\r
-                       // aggregate author metrics\r
-                       for (Map.Entry<String, Metric> entry : activity.getAuthorMetrics().entrySet()) {\r
-                               String author = entry.getKey();\r
-                               if (!authorMetrics.containsKey(author)) {\r
-                                       authorMetrics.put(author, new Metric(author));\r
-                               }\r
-                               authorMetrics.get(author).count += entry.getValue().count;\r
-                       }\r
-\r
-                       // aggregate repository metrics\r
-                       for (Map.Entry<String, Metric> entry : activity.getRepositoryMetrics().entrySet()) {\r
-                               String repository = StringUtils.stripDotGit(entry.getKey());\r
-                               if (!repositoryMetrics.containsKey(repository)) {\r
-                                       repositoryMetrics.put(repository, new Metric(repository));\r
-                               }\r
-                               repositoryMetrics.get(repository).count += entry.getValue().count;\r
-                       }\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
-               SimpleDateFormat df = new SimpleDateFormat("MMM dd");\r
-               df.setTimeZone(getTimeZone());\r
-               for (Activity metric : recentActivity) {\r
-                       chart.addValue(df.format(metric.startDate), metric.getCommitCount());\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
-               return charts;\r
-       }\r
-\r
        @Override\r
        protected void addDropDownMenus(List<PageRegistration> pages) {\r
                PageParameters params = getPageParameters();\r
 \r
-               DropDownMenuRegistration projects = new DropDownMenuRegistration("gb.projects",\r
-                               ProjectPage.class);\r
-               projects.menuItems.addAll(getProjectsMenu());\r
-               pages.add(0, projects);\r
-\r
                DropDownMenuRegistration menu = new DropDownMenuRegistration("gb.filters",\r
                                ProjectPage.class);\r
                // preserve time filter option on repository choices\r
@@ -277,10 +147,15 @@ public class ProjectPage extends RootPage {
 \r
                if (menu.menuItems.size() > 0) {\r
                        // Reset Filter\r
-                       menu.menuItems.add(new DropDownMenuItem(getString("gb.reset"), null, null));\r
+                       menu.menuItems.add(new DropDownMenuItem(getString("gb.reset"), "p", WicketUtils.getProjectName(params)));\r
                }\r
 \r
                pages.add(menu);\r
+               \r
+               DropDownMenuRegistration projects = new DropDownMenuRegistration("gb.projects",\r
+                               ProjectPage.class);\r
+               projects.menuItems.addAll(getProjectsMenu());\r
+               pages.add(projects);\r
        }\r
        \r
        @Override\r
index caa0f819a262a576f7a9734eb3a33d29e8c1e06a..2d446eccf9da4ee0a1d6a5877a9cab20e5fa87b1 100644 (file)
@@ -7,7 +7,6 @@
 <body>\r
 <wicket:extend>\r
 <div class="container">\r
-       <div class="markdown" style="padding-bottom:5px;" wicket:id="projectsMessage">[projects message]</div>\r
        \r
        <table class="repositories">\r
                <thead>\r
index 7f0b002eacd9b566f35cb2217ce3c79b696c8dcd..d0001ecb2508456205691eae0b2adbaff838ff4f 100644 (file)
  */\r
 package com.gitblit.wicket.pages;\r
 \r
-import java.io.File;\r
-import java.io.FileInputStream;\r
-import java.io.InputStream;\r
-import java.io.InputStreamReader;\r
-import java.text.MessageFormat;\r
-import java.util.ArrayList;\r
 import java.util.List;\r
 \r
-import org.apache.wicket.Component;\r
 import org.apache.wicket.PageParameters;\r
 import org.apache.wicket.markup.html.basic.Label;\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
-import org.apache.wicket.resource.ContextRelativeResource;\r
-import org.apache.wicket.util.resource.ResourceStreamNotFoundException;\r
-import org.eclipse.jgit.lib.Constants;\r
 \r
 import com.gitblit.GitBlit;\r
 import com.gitblit.Keys;\r
 import com.gitblit.models.ProjectModel;\r
-import com.gitblit.utils.MarkdownUtils;\r
-import com.gitblit.utils.StringUtils;\r
 import com.gitblit.wicket.GitBlitWebSession;\r
 import com.gitblit.wicket.PageRegistration;\r
 import com.gitblit.wicket.PageRegistration.DropDownMenuItem;\r
@@ -62,6 +50,11 @@ public class ProjectsPage extends RootPage {
                return true;\r
        }\r
        \r
+       @Override\r
+       protected Class<? extends BasePage> getRootNavPageClass() {\r
+               return RepositoriesPage.class;\r
+       }\r
+\r
        @Override\r
        protected List<ProjectModel> getProjectModels() {\r
                return GitBlit.self().getProjectModels(getRepositoryModels(), false);\r
@@ -72,21 +65,10 @@ public class ProjectsPage extends RootPage {
                // check to see if we should display a login message\r
                boolean authenticateView = GitBlit.getBoolean(Keys.web.authenticateViewPages, true);\r
                if (authenticateView && !GitBlitWebSession.get().isLoggedIn()) {\r
-                       String messageSource = GitBlit.getString(Keys.web.loginMessage, "gitblit");\r
-                       String message = readMarkdown(messageSource, "login.mkd");\r
-                       Component repositoriesMessage = new Label("projectsMessage", message);\r
-                       add(repositoriesMessage.setEscapeModelStrings(false));\r
                        add(new Label("projectsPanel"));\r
                        return;\r
                }\r
 \r
-               // Load the markdown welcome message\r
-               String messageSource = GitBlit.getString(Keys.web.repositoriesMessage, "gitblit");\r
-               String message = readMarkdown(messageSource, "welcome.mkd");\r
-               Component projectsMessage = new Label("projectsMessage", message).setEscapeModelStrings(\r
-                               false).setVisible(message.length() > 0);\r
-               add(projectsMessage);\r
-\r
                List<ProjectModel> projects = getProjects(params);\r
 \r
                ListDataProvider<ProjectModel> dp = new ListDataProvider<ProjectModel>(projects);\r
@@ -130,20 +112,12 @@ public class ProjectsPage extends RootPage {
                        }\r
                };\r
                add(dataView);\r
-\r
-               // push the panel down if we are hiding the admin controls and the\r
-               // welcome message\r
-               if (!showAdmin && !projectsMessage.isVisible()) {\r
-                       WicketUtils.setCssStyle(dataView, "padding-top:5px;");\r
-               }\r
        }\r
 \r
        @Override\r
        protected void addDropDownMenus(List<PageRegistration> pages) {\r
                PageParameters params = getPageParameters();\r
                \r
-               pages.add(0, new PageRegistration("gb.projects", ProjectsPage.class, params));\r
-\r
                DropDownMenuRegistration menu = new DropDownMenuRegistration("gb.filters",\r
                                ProjectsPage.class);\r
                // preserve time filter option on repository choices\r
@@ -159,77 +133,4 @@ public class ProjectsPage extends RootPage {
 \r
                pages.add(menu);\r
        }\r
-\r
-       private String readMarkdown(String messageSource, String resource) {\r
-               String message = "";\r
-               if (messageSource.equalsIgnoreCase("gitblit")) {\r
-                       // Read default message\r
-                       message = readDefaultMarkdown(resource);\r
-               } else {\r
-                       // Read user-supplied message\r
-                       if (!StringUtils.isEmpty(messageSource)) {\r
-                               File file = new File(messageSource);\r
-                               if (file.exists()) {\r
-                                       try {\r
-                                               FileInputStream fis = new FileInputStream(file);\r
-                                               InputStreamReader reader = new InputStreamReader(fis,\r
-                                                               Constants.CHARACTER_ENCODING);\r
-                                               message = MarkdownUtils.transformMarkdown(reader);\r
-                                               reader.close();\r
-                                       } catch (Throwable t) {\r
-                                               message = getString("gb.failedToRead") + " " + file;\r
-                                               warn(message, t);\r
-                                       }\r
-                               } else {\r
-                                       message = messageSource + " " + getString("gb.isNotValidFile");\r
-                               }\r
-                       }\r
-               }\r
-               return message;\r
-       }\r
-\r
-       private String readDefaultMarkdown(String file) {\r
-               String base = file.substring(0, file.lastIndexOf('.'));\r
-               String ext = file.substring(file.lastIndexOf('.'));\r
-               String lc = getLanguageCode();\r
-               String cc = getCountryCode();\r
-\r
-               // try to read file_en-us.ext, file_en.ext, file.ext\r
-               List<String> files = new ArrayList<String>();\r
-               if (!StringUtils.isEmpty(lc)) {\r
-                       if (!StringUtils.isEmpty(cc)) {\r
-                               files.add(base + "_" + lc + "-" + cc + ext);\r
-                               files.add(base + "_" + lc + "_" + cc + ext);\r
-                       }\r
-                       files.add(base + "_" + lc + ext);\r
-               }\r
-               files.add(file);\r
-               \r
-               for (String name : files) {\r
-                       String message;\r
-                       InputStreamReader reader = null;\r
-                       try {\r
-                               ContextRelativeResource res = WicketUtils.getResource(name);\r
-                               InputStream is = res.getResourceStream().getInputStream();\r
-                               reader = new InputStreamReader(is, Constants.CHARACTER_ENCODING);\r
-                               message = MarkdownUtils.transformMarkdown(reader);\r
-                               reader.close();\r
-                               return message;\r
-                       } catch (ResourceStreamNotFoundException t) {\r
-                               continue;\r
-                       } catch (Throwable t) {\r
-                               message = MessageFormat.format(getString("gb.failedToReadMessage"), file);\r
-                               error(message, t, false);\r
-                               return message;\r
-                       } finally {\r
-                               if (reader != null) {\r
-                                       try {\r
-                                               reader.close();\r
-                                       } catch (Exception e) {\r
-                                       }\r
-                               }\r
-                       }                       \r
-               }\r
-               return MessageFormat.format(getString("gb.failedToReadMessage"), file);\r
-       }\r
 }\r
diff --git a/src/main/java/com/gitblit/wicket/pages/PushesPage.html b/src/main/java/com/gitblit/wicket/pages/PushesPage.html
deleted file mode 100644 (file)
index 145db6f..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-<!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:extend>\r
-\r
-       <!-- pager links -->\r
-       <div class="page_nav2">\r
-               <a wicket:id="firstPage"><wicket:message key="gb.pageFirst"></wicket:message></a> | <a wicket:id="prevPage">&laquo; <wicket:message key="gb.pagePrevious"></wicket:message></a> | <a wicket:id="nextPage"><wicket:message key="gb.pageNext"></wicket:message> &raquo;</a> \r
-       </div>\r
-       \r
-       <!-- push log -->\r
-       <div style="margin-top:5px;" wicket:id="pushesPanel">[push log panel]</div>\r
-\r
-       <!-- pager links -->\r
-       <div style="padding-bottom:5px;">\r
-               <a wicket:id="firstPage"><wicket:message key="gb.pageFirst"></wicket:message></a> | <a wicket:id="prevPage">&laquo; <wicket:message key="gb.pagePrevious"></wicket:message></a> | <a wicket:id="nextPage"><wicket:message key="gb.pageNext"></wicket:message> &raquo;</a> \r
-       </div>\r
-       \r
-</wicket:extend>\r
-</body>\r
-</html>
\ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/PushesPage.java b/src/main/java/com/gitblit/wicket/pages/PushesPage.java
deleted file mode 100644 (file)
index 866964a..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-/*\r
- * Copyright 2013 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 org.apache.wicket.PageParameters;\r
-import org.apache.wicket.markup.html.link.BookmarkablePageLink;\r
-\r
-import com.gitblit.wicket.WicketUtils;\r
-import com.gitblit.wicket.panels.PushesPanel;\r
-\r
-public class PushesPage extends RepositoryPage {\r
-\r
-       public PushesPage(PageParameters params) {\r
-               super(params);\r
-\r
-               addSyndicationDiscoveryLink();\r
-\r
-               int pageNumber = WicketUtils.getPage(params);\r
-               int prevPage = Math.max(0, pageNumber - 1);\r
-               int nextPage = pageNumber + 1;\r
-\r
-               PushesPanel pushesPanel = new PushesPanel("pushesPanel", getRepositoryModel(), getRepository(), -1,\r
-                               pageNumber - 1, false);\r
-               boolean hasMore = pushesPanel.hasMore();\r
-               add(pushesPanel);\r
-\r
-               add(new BookmarkablePageLink<Void>("firstPage", PushesPage.class,\r
-                               WicketUtils.newObjectParameter(repositoryName, objectId))\r
-                               .setEnabled(pageNumber > 1));\r
-               add(new BookmarkablePageLink<Void>("prevPage", PushesPage.class,\r
-                               WicketUtils.newLogPageParameter(repositoryName, objectId, prevPage))\r
-                               .setEnabled(pageNumber > 1));\r
-               add(new BookmarkablePageLink<Void>("nextPage", PushesPage.class,\r
-                               WicketUtils.newLogPageParameter(repositoryName, objectId, nextPage))\r
-                               .setEnabled(hasMore));\r
-       }\r
-\r
-       @Override\r
-       protected String getPageName() {\r
-               return getString("gb.pushes");\r
-       }\r
-}\r
diff --git a/src/main/java/com/gitblit/wicket/pages/ReflogPage.html b/src/main/java/com/gitblit/wicket/pages/ReflogPage.html
new file mode 100644 (file)
index 0000000..c0ac7eb
--- /dev/null
@@ -0,0 +1,25 @@
+<!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:extend>\r
+\r
+       <!-- pager links -->\r
+       <div class="page_nav2">\r
+               <a wicket:id="firstPage"><wicket:message key="gb.pageFirst"></wicket:message></a> | <a wicket:id="prevPage">&laquo; <wicket:message key="gb.pagePrevious"></wicket:message></a> | <a wicket:id="nextPage"><wicket:message key="gb.pageNext"></wicket:message> &raquo;</a> \r
+       </div>\r
+       \r
+       <!-- ref log -->\r
+       <div style="margin-top:5px;" wicket:id="reflogPanel">[reflog panel]</div>\r
+\r
+       <!-- pager links -->\r
+       <div style="padding-bottom:5px;">\r
+               <a wicket:id="firstPage"><wicket:message key="gb.pageFirst"></wicket:message></a> | <a wicket:id="prevPage">&laquo; <wicket:message key="gb.pagePrevious"></wicket:message></a> | <a wicket:id="nextPage"><wicket:message key="gb.pageNext"></wicket:message> &raquo;</a> \r
+       </div>\r
+       \r
+</wicket:extend>\r
+</body>\r
+</html>
\ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/ReflogPage.java b/src/main/java/com/gitblit/wicket/pages/ReflogPage.java
new file mode 100644 (file)
index 0000000..884f616
--- /dev/null
@@ -0,0 +1,55 @@
+/*\r
+ * Copyright 2013 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 org.apache.wicket.PageParameters;\r
+import org.apache.wicket.markup.html.link.BookmarkablePageLink;\r
+\r
+import com.gitblit.wicket.WicketUtils;\r
+import com.gitblit.wicket.panels.ReflogPanel;\r
+\r
+public class ReflogPage extends RepositoryPage {\r
+\r
+       public ReflogPage(PageParameters params) {\r
+               super(params);\r
+\r
+               addSyndicationDiscoveryLink();\r
+\r
+               int pageNumber = WicketUtils.getPage(params);\r
+               int prevPage = Math.max(0, pageNumber - 1);\r
+               int nextPage = pageNumber + 1;\r
+\r
+               ReflogPanel reflogPanel = new ReflogPanel("reflogPanel", getRepositoryModel(), getRepository(), -1,\r
+                               pageNumber - 1);\r
+               boolean hasMore = reflogPanel.hasMore();\r
+               add(reflogPanel);\r
+\r
+               add(new BookmarkablePageLink<Void>("firstPage", ReflogPage.class,\r
+                               WicketUtils.newObjectParameter(repositoryName, objectId))\r
+                               .setEnabled(pageNumber > 1));\r
+               add(new BookmarkablePageLink<Void>("prevPage", ReflogPage.class,\r
+                               WicketUtils.newLogPageParameter(repositoryName, objectId, prevPage))\r
+                               .setEnabled(pageNumber > 1));\r
+               add(new BookmarkablePageLink<Void>("nextPage", ReflogPage.class,\r
+                               WicketUtils.newLogPageParameter(repositoryName, objectId, nextPage))\r
+                               .setEnabled(hasMore));\r
+       }\r
+\r
+       @Override\r
+       protected String getPageName() {\r
+               return getString("gb.reflog");\r
+       }\r
+}\r
index a15dd91b63f3138378bbdb9a558386a4f5c15268..96573012411332a513c217dc5ed90134ea8482f9 100644 (file)
@@ -60,7 +60,7 @@ import com.gitblit.models.UserRepositoryPreferences;
 import com.gitblit.utils.ArrayUtils;\r
 import com.gitblit.utils.DeepCopier;\r
 import com.gitblit.utils.JGitUtils;\r
-import com.gitblit.utils.PushLogUtils;\r
+import com.gitblit.utils.RefLogUtils;\r
 import com.gitblit.utils.StringUtils;\r
 import com.gitblit.utils.TicgitUtils;\r
 import com.gitblit.wicket.GitBlitWebSession;\r
@@ -187,12 +187,12 @@ public abstract class RepositoryPage extends RootPage {
                RepositoryModel model = getRepositoryModel();\r
 \r
                // standard links\r
-               if (PushLogUtils.getPushLogBranch(r) == null) {\r
+               if (RefLogUtils.getRefLogBranch(r) == null) {\r
                        pages.put("summary", new PageRegistration("gb.summary", SummaryPage.class, params));\r
                } else {\r
                        pages.put("summary", new PageRegistration("gb.summary", SummaryPage.class, params));\r
 //                     pages.put("overview", new PageRegistration("gb.overview", OverviewPage.class, params));\r
-                       pages.put("pushes", new PageRegistration("gb.pushes", PushesPage.class, params));\r
+                       pages.put("reflog", new PageRegistration("gb.reflog", ReflogPage.class, params));\r
                }               \r
                pages.put("commits", new PageRegistration("gb.commits", LogPage.class, params));\r
                pages.put("tree", new PageRegistration("gb.tree", TreePage.class, params));\r
index d21be36640e02c73ff24c7838d9ad7e10cdfe572..8c1a56857570a78106711083f0af4fb13b4a171c 100644 (file)
@@ -119,7 +119,7 @@ public abstract class RootPage extends BasePage {
                // navigation links\r
                List<PageRegistration> pages = new ArrayList<PageRegistration>();\r
                if (!authenticateView || (authenticateView && GitBlitWebSession.get().isLoggedIn())) {\r
-                       pages.add(new PageRegistration(GitBlitWebSession.get().isLoggedIn() ? "gb.myDashboard" : "gb.dashboard", DashboardPage.class,\r
+                       pages.add(new PageRegistration(GitBlitWebSession.get().isLoggedIn() ? "gb.myDashboard" : "gb.dashboard", MyDashboardPage.class,\r
                                        getRootPageParameters()));\r
                        pages.add(new PageRegistration("gb.repositories", RepositoriesPage.class,\r
                                        getRootPageParameters()));\r
diff --git a/src/main/java/com/gitblit/wicket/panels/DigestsPanel.html b/src/main/java/com/gitblit/wicket/panels/DigestsPanel.html
new file mode 100644 (file)
index 0000000..53a8104
--- /dev/null
@@ -0,0 +1,42 @@
+<!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
+<div wicket:id="change" class="reflog">\r
+       <table style="padding: 3px 0px;">\r
+       <tr>\r
+               <td class="icon hidden-phone"><i wicket:id="changeIcon"></i></td>\r
+               <td style="padding-left: 7px;vertical-align:middle;">\r
+                       <div>\r
+                               <span class="when" wicket:id="whenChanged"></span>\r
+                       </div>\r
+                       <div style="font-weight:bold;"><span wicket:id="whoChanged">[who changed]</span> <span wicket:id="whatChanged"></span><span wicket:id="refChanged"></span> <span wicket:id="repoPreposition"></span> <span wicket:id="repoChanged"></span> <span wicket:id="byAuthors"></span></div>\r
+               </td>\r
+       </tr>\r
+       <tr>\r
+               <td class="hidden-phone"></td>\r
+               <td style="padding-left: 7px;">\r
+                       <div>\r
+                               <table>\r
+                                       <tr wicket:id="commit">\r
+                                               <td class="hidden-phone hidden-tablet" style="vertical-align:top;padding-left:7px;"><span wicket:id="commitAuthor"></span></td>\r
+                                               <td style="vertical-align:top;"><span wicket:id="hashLink" style="padding-left: 5px;">[hash link]</span></td>\r
+                                               <td style="vertical-align:top;padding-left:5px;"><img wicket:id="commitIcon" /></td>\r
+                                               <td style="vertical-align:top;">                                                        \r
+                                                       <span wicket:id="commitShortMessage">[commit short message]</span>\r
+                                               </td>\r
+                                       </tr>\r
+                               </table>\r
+                               <span class="link" wicket:id="compareLink"></span>\r
+                       </div>\r
+               </td>\r
+       </tr>   \r
+       </table>\r
+</div>\r
+</wicket:panel>\r
+</body>\r
+</html>
\ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/panels/DigestsPanel.java b/src/main/java/com/gitblit/wicket/panels/DigestsPanel.java
new file mode 100644 (file)
index 0000000..0f380a4
--- /dev/null
@@ -0,0 +1,273 @@
+/*\r
+ * Copyright 2013 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.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.Date;\r
+import java.util.List;\r
+import java.util.TimeZone;\r
+\r
+import org.apache.wicket.markup.html.basic.Label;\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.GitBlit;\r
+import com.gitblit.Keys;\r
+import com.gitblit.models.DailyLogEntry;\r
+import com.gitblit.models.RepositoryCommit;\r
+import com.gitblit.utils.StringUtils;\r
+import com.gitblit.utils.TimeUtils;\r
+import com.gitblit.wicket.WicketUtils;\r
+import com.gitblit.wicket.pages.CommitPage;\r
+import com.gitblit.wicket.pages.ComparePage;\r
+import com.gitblit.wicket.pages.SummaryPage;\r
+import com.gitblit.wicket.pages.TagPage;\r
+import com.gitblit.wicket.pages.TreePage;\r
+import com.gitblit.wicket.pages.UserPage;\r
+\r
+public class DigestsPanel extends BasePanel {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       private final boolean hasChanges;\r
+       \r
+       private boolean hasMore;\r
+\r
+       public DigestsPanel(String wicketId, List<DailyLogEntry> digests) {\r
+               super(wicketId);\r
+               hasChanges = digests.size() > 0;\r
+\r
+               final int hashLen = GitBlit.getInteger(Keys.web.shortCommitIdLength, 6);\r
+\r
+               String dateFormat = GitBlit.getString(Keys.web.datestampLongFormat, "EEEE, MMMM d, yyyy");\r
+               final TimeZone timezone = getTimeZone();\r
+               final DateFormat df = new SimpleDateFormat(dateFormat);\r
+               df.setTimeZone(timezone);\r
+               final Calendar cal = Calendar.getInstance(timezone);\r
+               \r
+               ListDataProvider<DailyLogEntry> dp = new ListDataProvider<DailyLogEntry>(digests);\r
+               DataView<DailyLogEntry> pushView = new DataView<DailyLogEntry>("change", dp) {\r
+                       private static final long serialVersionUID = 1L;\r
+\r
+                       public void populateItem(final Item<DailyLogEntry> logItem) {\r
+                               final DailyLogEntry change = logItem.getModelObject();\r
+                               String fullRefName = change.getChangedRefs().get(0);\r
+                               String shortRefName = fullRefName;\r
+                               boolean isTag = false;\r
+                               if (shortRefName.startsWith(Constants.R_HEADS)) {\r
+                                       shortRefName = shortRefName.substring(Constants.R_HEADS.length());\r
+                               } else if (shortRefName.startsWith(Constants.R_TAGS)) {\r
+                                       shortRefName = shortRefName.substring(Constants.R_TAGS.length());\r
+                                       isTag = true;\r
+                               }\r
+                               \r
+                               String fuzzydate;\r
+                               TimeUtils tu = getTimeUtils();\r
+                               Date pushDate = change.date;\r
+                               if (TimeUtils.isToday(pushDate, timezone)) {\r
+                                       fuzzydate = tu.today();\r
+                               } else if (TimeUtils.isYesterday(pushDate, timezone)) {\r
+                                       fuzzydate = tu.yesterday();\r
+                               } else {\r
+                                       // calculate a fuzzy time ago date\r
+                       cal.setTime(pushDate);\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
+                       pushDate = cal.getTime();\r
+                                       fuzzydate = getTimeUtils().timeAgo(pushDate);\r
+                               }\r
+                               logItem.add(new Label("whenChanged", fuzzydate + ", " + df.format(pushDate)));\r
+\r
+                               Label changeIcon = new Label("changeIcon");\r
+                               // use the repository hash color to differentiate the icon.\r
+                String color = StringUtils.getColor(StringUtils.stripDotGit(change.repository));\r
+                WicketUtils.setCssStyle(changeIcon, "color: " + color);\r
+\r
+                               if (isTag) {\r
+                                       WicketUtils.setCssClass(changeIcon, "iconic-tag");\r
+                               } else {\r
+                                       WicketUtils.setCssClass(changeIcon, "iconic-loop");\r
+                               }\r
+                               logItem.add(changeIcon);\r
+\r
+                if (!isTag) {\r
+                       logItem.add(new Label("whoChanged").setVisible(false));\r
+                } else {\r
+                       if (change.user.username.equals(change.user.emailAddress) && change.user.emailAddress.indexOf('@') > -1) {\r
+                               // username is an email address can not link - 1.2.1 push log bug\r
+                               logItem.add(new Label("whoChanged", change.user.getDisplayName()));\r
+                       } else {\r
+                               // link to user account page\r
+                               logItem.add(new LinkPanel("whoChanged", null, change.user.getDisplayName(),\r
+                                               UserPage.class, WicketUtils.newUsernameParameter(change.user.username)));\r
+                       }\r
+                }\r
+                               \r
+                               String preposition = "gb.of";\r
+                               boolean isDelete = false;\r
+                               String what;\r
+                               String by = null;\r
+                               switch(change.getChangeType(fullRefName)) {\r
+                               case CREATE:\r
+                                       if (isTag) {\r
+                                               // new tag\r
+                                               what = getString("gb.createdNewTag");\r
+                                               preposition = "gb.in";\r
+                                       } else {\r
+                                               // new branch\r
+                                               what = getString("gb.createdNewBranch");\r
+                                               preposition = "gb.in";\r
+                                       }\r
+                                       break;\r
+                               case DELETE:\r
+                                       isDelete = true;\r
+                                       if (isTag) {\r
+                                               what = getString("gb.deletedTag");\r
+                                       } else {\r
+                                               what = getString("gb.deletedBranch");\r
+                                       }\r
+                                       preposition = "gb.from";\r
+                                       break;\r
+                               default:\r
+                                       what = MessageFormat.format(change.getCommitCount() > 1 ? getString("gb.commitsTo") : getString("gb.oneCommitTo"), change.getCommitCount());\r
+                                       \r
+                                       if (change.getAuthorCount() == 1) {\r
+                                               by = MessageFormat.format(getString("gb.byOneAuthor"), change.getAuthorIdent().getName());\r
+                                       } else {\r
+                                               by = MessageFormat.format(getString("gb.byNAuthors"), change.getAuthorCount()); \r
+                                       }\r
+                                       break;\r
+                               }\r
+                               logItem.add(new Label("whatChanged", what));\r
+                               logItem.add(new Label("byAuthors", by).setVisible(!StringUtils.isEmpty(by)));\r
+                               \r
+                               if (isDelete) {\r
+                                       // can't link to deleted ref\r
+                                       logItem.add(new Label("refChanged", shortRefName));\r
+                               } else if (isTag) {\r
+                                       // link to tag\r
+                                       logItem.add(new LinkPanel("refChanged", null, shortRefName,\r
+                                                       TagPage.class, WicketUtils.newObjectParameter(change.repository, fullRefName)));\r
+                               } else {\r
+                                       // link to tree\r
+                                       logItem.add(new LinkPanel("refChanged", null, shortRefName,\r
+                                               TreePage.class, WicketUtils.newObjectParameter(change.repository, fullRefName)));\r
+                               }\r
+                               \r
+                               // to/from/etc\r
+                               logItem.add(new Label("repoPreposition", getString(preposition)));\r
+                               String repoName = StringUtils.stripDotGit(change.repository);\r
+                               logItem.add(new LinkPanel("repoChanged", null, repoName,\r
+                                               SummaryPage.class, WicketUtils.newRepositoryParameter(change.repository)));\r
+                               \r
+                               int maxCommitCount = 5;\r
+                               List<RepositoryCommit> commits = change.getCommits();\r
+                               if (commits.size() > maxCommitCount) {\r
+                                       commits = new ArrayList<RepositoryCommit>(commits.subList(0,  maxCommitCount));                                 \r
+                               }\r
+                               \r
+                               // compare link\r
+                               String compareLinkText = null;\r
+                               if ((change.getCommitCount() <= maxCommitCount) && (change.getCommitCount() > 1)) {\r
+                                       compareLinkText = MessageFormat.format(getString("gb.viewComparison"), commits.size());\r
+                               } else if (change.getCommitCount() > maxCommitCount) {\r
+                                       int diff = change.getCommitCount() - maxCommitCount;\r
+                                       compareLinkText = MessageFormat.format(diff > 1 ? getString("gb.nMoreCommits") : getString("gb.oneMoreCommit"), diff);\r
+                               }\r
+                               if (StringUtils.isEmpty(compareLinkText)) {\r
+                                       logItem.add(new Label("compareLink").setVisible(false));\r
+                               } else {\r
+                                       String endRangeId = change.getNewId(fullRefName);\r
+                                       String startRangeId = change.getOldId(fullRefName);\r
+                                       logItem.add(new LinkPanel("compareLink", null, compareLinkText, ComparePage.class, WicketUtils.newRangeParameter(change.repository, startRangeId, endRangeId)));\r
+                               }\r
+                               \r
+                               final boolean showSwatch = GitBlit.getBoolean(Keys.web.repositoryListSwatches, true);\r
+                               \r
+                               ListDataProvider<RepositoryCommit> cdp = new ListDataProvider<RepositoryCommit>(commits);\r
+                               DataView<RepositoryCommit> commitsView = new DataView<RepositoryCommit>("commit", cdp) {\r
+                                       private static final long serialVersionUID = 1L;\r
+\r
+                                       public void populateItem(final Item<RepositoryCommit> commitItem) {\r
+                                               final RepositoryCommit commit = commitItem.getModelObject();\r
+\r
+                                               // author gravatar\r
+                                               commitItem.add(new GravatarImage("commitAuthor", commit.getAuthorIdent().getName(),\r
+                                                               commit.getAuthorIdent().getEmailAddress(), null, 16, false, false));\r
+                                               \r
+                                               // merge icon\r
+                                               if (commit.getParentCount() > 1) {\r
+                                                       commitItem.add(WicketUtils.newImage("commitIcon", "commit_merge_16x16.png"));\r
+                                               } else {\r
+                                                       commitItem.add(WicketUtils.newBlankImage("commitIcon"));\r
+                                               }\r
+\r
+                                               // short message\r
+                                               String shortMessage = commit.getShortMessage();\r
+                                               String trimmedMessage = shortMessage;\r
+                                               if (commit.getRefs() != null && commit.getRefs().size() > 0) {\r
+                                                       trimmedMessage = StringUtils.trimString(shortMessage, Constants.LEN_SHORTLOG_REFS);\r
+                                               } else {\r
+                                                       trimmedMessage = StringUtils.trimString(shortMessage, Constants.LEN_SHORTLOG);\r
+                                               }\r
+                                               LinkPanel shortlog = new LinkPanel("commitShortMessage", "list",\r
+                                                               trimmedMessage, CommitPage.class, WicketUtils.newObjectParameter(\r
+                                                                               change.repository, commit.getName()));\r
+                                               if (!shortMessage.equals(trimmedMessage)) {\r
+                                                       WicketUtils.setHtmlTooltip(shortlog, shortMessage);\r
+                                               }\r
+                                               commitItem.add(shortlog);\r
+\r
+                                               // commit hash link\r
+                                               LinkPanel commitHash = new LinkPanel("hashLink", null, commit.getName().substring(0, hashLen),\r
+                                                               CommitPage.class, WicketUtils.newObjectParameter(\r
+                                                                               change.repository, commit.getName()));\r
+                                               WicketUtils.setCssClass(commitHash, "shortsha1");\r
+                                               WicketUtils.setHtmlTooltip(commitHash, commit.getName());\r
+                                               commitItem.add(commitHash);\r
+                                               \r
+                                               if (showSwatch) {\r
+                                                       // set repository color\r
+                                                       String color = StringUtils.getColor(StringUtils.stripDotGit(change.repository));\r
+                                                       WicketUtils.setCssStyle(commitItem, MessageFormat.format("border-left: 2px solid {0};", color));\r
+                                               }\r
+                                       }\r
+                               };\r
+\r
+                               logItem.add(commitsView);\r
+                       }\r
+               };\r
+               \r
+               add(pushView);\r
+       }\r
+\r
+       public boolean hasMore() {\r
+               return hasMore;\r
+       }\r
+       \r
+       public boolean hideIfEmpty() {\r
+               setVisible(hasChanges);\r
+               return hasChanges;\r
+       }\r
+}\r
index e9196cda96335ad6a10b535a549c070be4164778..02d67e30f7e1cc119a61efb3fada9fed212d78a0 100644 (file)
@@ -70,8 +70,6 @@
                                        <wicket:message key="gb.lastChange">[last change]</wicket:message> <span wicket:id="repositoryLastChange">[last change]</span>,\r
                                        <span style="font-size:0.8em;" wicket:id="repositorySize">[repository size]</span>\r
                                </div>\r
-        \r
-                               <div class="hidden-phone hidden-tablet" style="padding-top: 5px;" wicket:id="repositoryPrimaryUrl">[repository primary url]</div>\r
                        </div>\r
                </div>\r
        </div>\r
index e7fe017e37ecb23d8f8700ee14a5b13ed53cd597..37641d3945c587cdda07280899ad494b690da49a 100644 (file)
@@ -192,7 +192,5 @@ public class ProjectRepositoryPanel extends BasePanel {
                }\r
 \r
                add(new ExternalLink("syndication", SyndicationServlet.asLink("", entry.name, null, 0)));\r
-\r
-               add(new RepositoryUrlPanel("repositoryPrimaryUrl", true, user, entry));\r
        }\r
 }\r
diff --git a/src/main/java/com/gitblit/wicket/panels/PushesPanel.html b/src/main/java/com/gitblit/wicket/panels/PushesPanel.html
deleted file mode 100644 (file)
index fb67cfc..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-<!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
-<div wicket:id="push" class="push">\r
-       <table style="padding: 3px 0px;">\r
-       <tr>\r
-               <td class="icon hidden-phone"><i wicket:id="pushIcon"></i></td>\r
-               <td style="padding-left: 7px;vertical-align:middle;">\r
-                       <div>\r
-                               <span style="color:#aaa;" wicket:id="whenPushed"></span> <span wicket:id="refRewind" class="alert alert-error" style="padding: 1px 5px;font-size: 10px;font-weight: bold;margin-left: 10px;">[rewind]</span>\r
-                       </div>\r
-                       <div style="font-weight:bold;"><span wicket:id="whoPushed">[pusher]</span> <span wicket:id="whatPushed"></span><span wicket:id="refPushed"></span> <span wicket:id="repoPreposition"></span> <span wicket:id="repoPushed"></span> <span wicket:id="byAuthors"></span></div>\r
-               </td>\r
-       </tr>\r
-       <tr>\r
-               <td class="hidden-phone"></td>\r
-               <td style="padding-left: 7px;">\r
-                       <div>\r
-                               <table>\r
-                                       <tr wicket:id="commit">\r
-                                               <td class="hidden-phone hidden-tablet" style="vertical-align:top;padding-left:7px;"><span wicket:id="commitAuthor"></span></td>\r
-                                               <td style="vertical-align:top;"><span wicket:id="hashLink" style="padding-left: 5px;">[hash link]</span></td>\r
-                                               <td style="vertical-align:top;padding-left:5px;"><img wicket:id="commitIcon" /></td>\r
-                                               <td style="vertical-align:top;">                                                        \r
-                                                       <span wicket:id="commitShortMessage">[commit short message]</span>\r
-                                               </td>\r
-                                       </tr>\r
-                               </table>\r
-                               <span class="link" wicket:id="compareLink"></span>\r
-                       </div>\r
-               </td>\r
-       </tr>   \r
-       </table>\r
-</div>\r
-<div wicket:id="morePushes">[more...]</div>\r
-</wicket:panel>\r
-</body>\r
-</html>
\ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/panels/PushesPanel.java b/src/main/java/com/gitblit/wicket/panels/PushesPanel.java
deleted file mode 100644 (file)
index 5c473f7..0000000
+++ /dev/null
@@ -1,381 +0,0 @@
-/*\r
- * Copyright 2013 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.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.Date;\r
-import java.util.List;\r
-import java.util.TimeZone;\r
-\r
-import org.apache.wicket.markup.html.basic.Label;\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
-import org.apache.wicket.model.StringResourceModel;\r
-import org.eclipse.jgit.lib.Repository;\r
-\r
-import com.gitblit.Constants;\r
-import com.gitblit.GitBlit;\r
-import com.gitblit.Keys;\r
-import com.gitblit.models.DailyLogEntry;\r
-import com.gitblit.models.PushLogEntry;\r
-import com.gitblit.models.RepositoryCommit;\r
-import com.gitblit.models.RepositoryModel;\r
-import com.gitblit.utils.PushLogUtils;\r
-import com.gitblit.utils.StringUtils;\r
-import com.gitblit.utils.TimeUtils;\r
-import com.gitblit.wicket.WicketUtils;\r
-import com.gitblit.wicket.pages.CommitPage;\r
-import com.gitblit.wicket.pages.ComparePage;\r
-import com.gitblit.wicket.pages.PushesPage;\r
-import com.gitblit.wicket.pages.SummaryPage;\r
-import com.gitblit.wicket.pages.TagPage;\r
-import com.gitblit.wicket.pages.TreePage;\r
-import com.gitblit.wicket.pages.UserPage;\r
-\r
-public class PushesPanel extends BasePanel {\r
-\r
-       private static final long serialVersionUID = 1L;\r
-\r
-       private final boolean hasPushes;\r
-       \r
-       private boolean hasMore;\r
-\r
-       public PushesPanel(String wicketId, final RepositoryModel model, Repository r, int limit, int pageOffset, boolean showRepo) {\r
-               super(wicketId);\r
-               boolean pageResults = limit <= 0;\r
-               int pushesPerPage = GitBlit.getInteger(Keys.web.pushesPerPage, 10);\r
-               if (pushesPerPage <= 1) {\r
-                       pushesPerPage = 10;\r
-               }\r
-\r
-               List<PushLogEntry> pushes;\r
-               if (pageResults) {\r
-                       pushes = PushLogUtils.getPushLogByRef(model.name, r, pageOffset * pushesPerPage, pushesPerPage);\r
-               } else {\r
-                       pushes = PushLogUtils.getPushLogByRef(model.name, r, limit);\r
-               }\r
-\r
-               // inaccurate way to determine if there are more commits.\r
-               // works unless commits.size() represents the exact end.\r
-               hasMore = pushes.size() >= pushesPerPage;\r
-               hasPushes = pushes.size() > 0;\r
-               \r
-               setup(pushes, showRepo);\r
-               \r
-               // determine to show pager, more, or neither\r
-               if (limit <= 0) {\r
-                       // no display limit\r
-                       add(new Label("morePushes").setVisible(false));\r
-               } else {\r
-                       if (pageResults) {\r
-                               // paging\r
-                               add(new Label("morePushes").setVisible(false));\r
-                       } else {\r
-                               // more\r
-                               if (pushes.size() == limit) {\r
-                                       // show more\r
-                                       add(new LinkPanel("morePushes", "link", new StringResourceModel("gb.morePushes",\r
-                                                       this, null), PushesPage.class,\r
-                                                       WicketUtils.newRepositoryParameter(model.name)));\r
-                               } else {\r
-                                       // no more\r
-                                       add(new Label("morePushes").setVisible(false));\r
-                               }\r
-                       }\r
-               }\r
-       }\r
-       \r
-       public PushesPanel(String wicketId, List<PushLogEntry> pushes) {\r
-               super(wicketId);\r
-               hasPushes = pushes.size() > 0;\r
-               setup(pushes, true);\r
-               add(new Label("morePushes").setVisible(false));\r
-       }\r
-       \r
-       protected void setup(List<PushLogEntry> pushes, final boolean showRepo) {\r
-               final int hashLen = GitBlit.getInteger(Keys.web.shortCommitIdLength, 6);\r
-\r
-               String dateFormat = GitBlit.getString(Keys.web.datestampLongFormat, "EEEE, MMMM d, yyyy");\r
-               final TimeZone timezone = getTimeZone();\r
-               final DateFormat df = new SimpleDateFormat(dateFormat);\r
-               df.setTimeZone(timezone);\r
-               final Calendar cal = Calendar.getInstance(timezone);\r
-               \r
-               ListDataProvider<PushLogEntry> dp = new ListDataProvider<PushLogEntry>(pushes);\r
-               DataView<PushLogEntry> pushView = new DataView<PushLogEntry>("push", dp) {\r
-                       private static final long serialVersionUID = 1L;\r
-\r
-                       public void populateItem(final Item<PushLogEntry> pushItem) {\r
-                               final PushLogEntry push = pushItem.getModelObject();\r
-                               String fullRefName = push.getChangedRefs().get(0);\r
-                               String shortRefName = fullRefName;\r
-                               boolean isTag = false;\r
-                               boolean isPull = false;\r
-                               if (shortRefName.startsWith(Constants.R_HEADS)) {\r
-                                       shortRefName = shortRefName.substring(Constants.R_HEADS.length());\r
-                               } else if (shortRefName.startsWith(Constants.R_TAGS)) {\r
-                                       shortRefName = shortRefName.substring(Constants.R_TAGS.length());\r
-                                       isTag = true;\r
-                               } else if (shortRefName.startsWith(Constants.R_PULL)) {\r
-                                       shortRefName = "#" + shortRefName.substring(Constants.R_PULL.length());\r
-                                       if (shortRefName.endsWith("/head")) {\r
-                                               // strip pull request head from name \r
-                                               shortRefName = shortRefName.substring(0, shortRefName.length() - "/head".length());\r
-                                       }                                       \r
-                                       isPull = true;\r
-                               }\r
-                               boolean isDigest = push instanceof DailyLogEntry;\r
-                               \r
-                               String fuzzydate;\r
-                               TimeUtils tu = getTimeUtils();\r
-                               Date pushDate = push.date;\r
-                               if (TimeUtils.isToday(pushDate, timezone)) {\r
-                                       fuzzydate = tu.today();\r
-                               } else if (TimeUtils.isYesterday(pushDate, timezone)) {\r
-                                       fuzzydate = tu.yesterday();\r
-                               } else {\r
-                                       // calculate a fuzzy time ago date\r
-                       cal.setTime(pushDate);\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
-                       pushDate = cal.getTime();\r
-                                       fuzzydate = getTimeUtils().timeAgo(pushDate);\r
-                               }\r
-                               pushItem.add(new Label("whenPushed", fuzzydate + ", " + df.format(pushDate)));\r
-\r
-                               Label pushIcon = new Label("pushIcon");\r
-                               if (showRepo) {\r
-                                       // if we are showing the repo, we are showing multiple\r
-                                       // repos.  use the repository hash color to differentiate\r
-                                       // the icon.\r
-                       String color = StringUtils.getColor(StringUtils.stripDotGit(push.repository));\r
-                       WicketUtils.setCssStyle(pushIcon, "color: " + color);\r
-                               }\r
-                               if (isTag) {\r
-                                       WicketUtils.setCssClass(pushIcon, "iconic-tag");\r
-                               } else if (isPull) {\r
-                                       WicketUtils.setCssClass(pushIcon, "iconic-share");\r
-                               } else if (isDigest) {\r
-                                       WicketUtils.setCssClass(pushIcon, "iconic-loop");\r
-                               } else {\r
-                                       WicketUtils.setCssClass(pushIcon, "iconic-upload");\r
-                               }\r
-                               pushItem.add(pushIcon);\r
-\r
-                if (isDigest && !isTag) {\r
-                       pushItem.add(new Label("whoPushed").setVisible(false));\r
-                } else {\r
-                       if (push.user.username.equals(push.user.emailAddress) && push.user.emailAddress.indexOf('@') > -1) {\r
-                               // username is an email address - 1.2.1 push log bug\r
-                               pushItem.add(new Label("whoPushed", push.user.getDisplayName()));\r
-                       } else {\r
-                               // link to user account page\r
-                               pushItem.add(new LinkPanel("whoPushed", null, push.user.getDisplayName(),\r
-                                               UserPage.class, WicketUtils.newUsernameParameter(push.user.username)));\r
-                       }\r
-                }\r
-                               \r
-                               String preposition = "gb.of";\r
-                               boolean isDelete = false;\r
-                               boolean isRewind = false;\r
-                               String what;\r
-                               String by = null;\r
-                               switch(push.getChangeType(fullRefName)) {\r
-                               case CREATE:\r
-                                       if (isTag) {\r
-                                               // new tag\r
-                                               if (isDigest) {\r
-                                                       what = getString("gb.createdNewTag");\r
-                                                       preposition = "gb.in";\r
-                                               } else {\r
-                                                       what = getString("gb.pushedNewTag");\r
-                                                       preposition = "gb.to";\r
-                                               }\r
-                                       } else if (isPull) {\r
-                                               // merged pull request\r
-                                               what = getString("gb.mergedPullRequest");\r
-                                               preposition = "gb.in";\r
-                                       } else {\r
-                                               // new branch\r
-                                               if (isDigest) {\r
-                                                       what = getString("gb.createdNewBranch");\r
-                                                       preposition = "gb.in";\r
-                                               } else {\r
-                                                       what = getString("gb.pushedNewBranch");\r
-                                                       preposition = "gb.to";\r
-                                               }\r
-                                       }\r
-                                       break;\r
-                               case DELETE:\r
-                                       isDelete = true;\r
-                                       if (isTag) {\r
-                                               what = getString("gb.deletedTag");\r
-                                       } if (isPull) {\r
-                                               what = getString("gb.deletedTag");\r
-                                       } else {\r
-                                               what = getString("gb.deletedBranch");\r
-                                       }\r
-                                       preposition = "gb.from";\r
-                                       break;\r
-                               case UPDATE_NONFASTFORWARD:\r
-                                       isRewind = true;\r
-                               default:\r
-                                       if (isDigest) {\r
-                                               what = MessageFormat.format(push.getCommitCount() > 1 ? getString("gb.commitsTo") : getString("gb.oneCommitTo"), push.getCommitCount());\r
-                                       } else {\r
-                                               what = MessageFormat.format(push.getCommitCount() > 1 ? getString("gb.pushedNCommitsTo") : getString("gb.pushedOneCommitTo") , push.getCommitCount());\r
-                                       }\r
-                                       \r
-                                       if (push.getAuthorCount() == 1) {\r
-                                               by = MessageFormat.format(getString("gb.byOneAuthor"), push.getAuthorIdent().getName());\r
-                                       } else {\r
-                                               by = MessageFormat.format(getString("gb.byNAuthors"), push.getAuthorCount());   \r
-                                       }\r
-                                       break;\r
-                               }\r
-                               pushItem.add(new Label("whatPushed", what));\r
-                               pushItem.add(new Label("byAuthors", by).setVisible(!StringUtils.isEmpty(by)));\r
-                               \r
-                               pushItem.add(new Label("refRewind", getString("gb.rewind")).setVisible(isRewind));\r
-                               \r
-                               if (isDelete) {\r
-                                       // can't link to deleted ref\r
-                                       pushItem.add(new Label("refPushed", shortRefName));\r
-                               } else if (isTag) {\r
-                                       // link to tag\r
-                                       pushItem.add(new LinkPanel("refPushed", null, shortRefName,\r
-                                                       TagPage.class, WicketUtils.newObjectParameter(push.repository, fullRefName)));\r
-                               } else if (isPull) {\r
-                                       // link to pull request\r
-                                       pushItem.add(new LinkPanel("refPushed", null, shortRefName,\r
-                                                       TagPage.class, WicketUtils.newObjectParameter(push.repository, fullRefName)));\r
-                               } else {\r
-                                       // link to tree\r
-                                       pushItem.add(new LinkPanel("refPushed", null, shortRefName,\r
-                                               TreePage.class, WicketUtils.newObjectParameter(push.repository, fullRefName)));\r
-                               }\r
-                               \r
-                               if (showRepo) {\r
-                                       // to/from/etc\r
-                                       pushItem.add(new Label("repoPreposition", getString(preposition)));\r
-\r
-                                       String repoName = StringUtils.stripDotGit(push.repository);\r
-                                       pushItem.add(new LinkPanel("repoPushed", null, repoName,\r
-                                                       SummaryPage.class, WicketUtils.newRepositoryParameter(push.repository)));\r
-                               } else {\r
-                                       // do not display repository name if we are viewing the push\r
-                                       // log of a repository.\r
-                                       pushItem.add(new Label("repoPreposition").setVisible(false));\r
-                                       pushItem.add(new Label("repoPushed").setVisible(false));\r
-                               }\r
-                               \r
-                               int maxCommitCount = 5;\r
-                               List<RepositoryCommit> commits = push.getCommits();\r
-                               if (commits.size() > maxCommitCount) {\r
-                                       commits = new ArrayList<RepositoryCommit>(commits.subList(0,  maxCommitCount));                                 \r
-                               }\r
-                               \r
-                               // compare link\r
-                               String compareLinkText = null;\r
-                               if ((push.getCommitCount() <= maxCommitCount) && (push.getCommitCount() > 1)) {\r
-                                       compareLinkText = MessageFormat.format(getString("gb.viewComparison"), commits.size());\r
-                               } else if (push.getCommitCount() > maxCommitCount) {\r
-                                       int diff = push.getCommitCount() - maxCommitCount;\r
-                                       compareLinkText = MessageFormat.format(diff > 1 ? getString("gb.nMoreCommits") : getString("gb.oneMoreCommit"), diff);\r
-                               }\r
-                               if (StringUtils.isEmpty(compareLinkText)) {\r
-                                       pushItem.add(new Label("compareLink").setVisible(false));\r
-                               } else {\r
-                                       String endRangeId = push.getNewId(fullRefName);\r
-                                       String startRangeId = push.getOldId(fullRefName);\r
-                                       pushItem.add(new LinkPanel("compareLink", null, compareLinkText, ComparePage.class, WicketUtils.newRangeParameter(push.repository, startRangeId, endRangeId)));\r
-                               }\r
-                               \r
-                               final boolean showSwatch = showRepo && GitBlit.getBoolean(Keys.web.repositoryListSwatches, true);\r
-                               \r
-                               ListDataProvider<RepositoryCommit> cdp = new ListDataProvider<RepositoryCommit>(commits);\r
-                               DataView<RepositoryCommit> commitsView = new DataView<RepositoryCommit>("commit", cdp) {\r
-                                       private static final long serialVersionUID = 1L;\r
-\r
-                                       public void populateItem(final Item<RepositoryCommit> commitItem) {\r
-                                               final RepositoryCommit commit = commitItem.getModelObject();\r
-\r
-                                               // author gravatar\r
-                                               commitItem.add(new GravatarImage("commitAuthor", commit.getAuthorIdent().getName(),\r
-                                                               commit.getAuthorIdent().getEmailAddress(), null, 16, false, false));\r
-                                               \r
-                                               // merge icon\r
-                                               if (commit.getParentCount() > 1) {\r
-                                                       commitItem.add(WicketUtils.newImage("commitIcon", "commit_merge_16x16.png"));\r
-                                               } else {\r
-                                                       commitItem.add(WicketUtils.newBlankImage("commitIcon"));\r
-                                               }\r
-\r
-                                               // short message\r
-                                               String shortMessage = commit.getShortMessage();\r
-                                               String trimmedMessage = shortMessage;\r
-                                               if (commit.getRefs() != null && commit.getRefs().size() > 0) {\r
-                                                       trimmedMessage = StringUtils.trimString(shortMessage, Constants.LEN_SHORTLOG_REFS);\r
-                                               } else {\r
-                                                       trimmedMessage = StringUtils.trimString(shortMessage, Constants.LEN_SHORTLOG);\r
-                                               }\r
-                                               LinkPanel shortlog = new LinkPanel("commitShortMessage", "list",\r
-                                                               trimmedMessage, CommitPage.class, WicketUtils.newObjectParameter(\r
-                                                                               push.repository, commit.getName()));\r
-                                               if (!shortMessage.equals(trimmedMessage)) {\r
-                                                       WicketUtils.setHtmlTooltip(shortlog, shortMessage);\r
-                                               }\r
-                                               commitItem.add(shortlog);\r
-\r
-                                               // commit hash link\r
-                                               LinkPanel commitHash = new LinkPanel("hashLink", null, commit.getName().substring(0, hashLen),\r
-                                                               CommitPage.class, WicketUtils.newObjectParameter(\r
-                                                                               push.repository, commit.getName()));\r
-                                               WicketUtils.setCssClass(commitHash, "shortsha1");\r
-                                               WicketUtils.setHtmlTooltip(commitHash, commit.getName());\r
-                                               commitItem.add(commitHash);\r
-                                               \r
-                                               if (showSwatch) {\r
-                                                       // set repository color\r
-                                                       String color = StringUtils.getColor(StringUtils.stripDotGit(push.repository));\r
-                                                       WicketUtils.setCssStyle(commitItem, MessageFormat.format("border-left: 2px solid {0};", color));\r
-                                               }\r
-                                       }\r
-                               };\r
-\r
-                               pushItem.add(commitsView);\r
-                       }\r
-               };\r
-               \r
-               add(pushView);\r
-       }\r
-\r
-       public boolean hasMore() {\r
-               return hasMore;\r
-       }\r
-       \r
-       public boolean hideIfEmpty() {\r
-               setVisible(hasPushes);\r
-               return hasPushes;\r
-       }\r
-}\r
diff --git a/src/main/java/com/gitblit/wicket/panels/ReflogPanel.html b/src/main/java/com/gitblit/wicket/panels/ReflogPanel.html
new file mode 100644 (file)
index 0000000..0148d89
--- /dev/null
@@ -0,0 +1,43 @@
+<!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
+<div wicket:id="change" class="reflog">\r
+       <table style="padding: 3px 0px;">\r
+       <tr>\r
+               <td class="icon hidden-phone"><i wicket:id="changeIcon"></i></td>\r
+               <td style="padding-left: 7px;vertical-align:middle;">\r
+                       <div>\r
+                               <span class="when" wicket:id="whenChanged"></span> <span wicket:id="refRewind" class="alert alert-error" style="padding: 1px 5px;font-size: 10px;font-weight: bold;margin-left: 10px;">[rewind]</span>\r
+                       </div>\r
+                       <div style="font-weight:bold;"><span wicket:id="whoChanged">[change author]</span> <span wicket:id="whatChanged"></span><span wicket:id="refChanged"></span> <span wicket:id="byAuthors"></span></div>\r
+               </td>\r
+       </tr>\r
+       <tr>\r
+               <td class="hidden-phone"></td>\r
+               <td style="padding-left: 7px;">\r
+                       <div>\r
+                               <table>\r
+                                       <tr wicket:id="commit">\r
+                                               <td class="hidden-phone hidden-tablet" style="vertical-align:top;padding-left:7px;"><span wicket:id="commitAuthor"></span></td>\r
+                                               <td style="vertical-align:top;"><span wicket:id="hashLink" style="padding-left: 5px;">[hash link]</span></td>\r
+                                               <td style="vertical-align:top;padding-left:5px;"><img wicket:id="commitIcon" /></td>\r
+                                               <td style="vertical-align:top;">                                                        \r
+                                                       <span wicket:id="commitShortMessage">[commit short message]</span>\r
+                                               </td>\r
+                                       </tr>\r
+                               </table>\r
+                               <span class="link" wicket:id="compareLink"></span>\r
+                       </div>\r
+               </td>\r
+       </tr>   \r
+       </table>\r
+</div>\r
+<div wicket:id="moreChanges">[more...]</div>\r
+</wicket:panel>\r
+</body>\r
+</html>
\ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/panels/ReflogPanel.java b/src/main/java/com/gitblit/wicket/panels/ReflogPanel.java
new file mode 100644 (file)
index 0000000..048ce1b
--- /dev/null
@@ -0,0 +1,305 @@
+/*\r
+ * Copyright 2013 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.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.Date;\r
+import java.util.List;\r
+import java.util.TimeZone;\r
+\r
+import org.apache.wicket.markup.html.basic.Label;\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
+import org.apache.wicket.model.StringResourceModel;\r
+import org.eclipse.jgit.lib.Repository;\r
+\r
+import com.gitblit.Constants;\r
+import com.gitblit.GitBlit;\r
+import com.gitblit.Keys;\r
+import com.gitblit.models.RefLogEntry;\r
+import com.gitblit.models.RepositoryCommit;\r
+import com.gitblit.models.RepositoryModel;\r
+import com.gitblit.utils.RefLogUtils;\r
+import com.gitblit.utils.StringUtils;\r
+import com.gitblit.utils.TimeUtils;\r
+import com.gitblit.wicket.WicketUtils;\r
+import com.gitblit.wicket.pages.CommitPage;\r
+import com.gitblit.wicket.pages.ComparePage;\r
+import com.gitblit.wicket.pages.ReflogPage;\r
+import com.gitblit.wicket.pages.TagPage;\r
+import com.gitblit.wicket.pages.TreePage;\r
+import com.gitblit.wicket.pages.UserPage;\r
+\r
+public class ReflogPanel extends BasePanel {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       private final boolean hasChanges;\r
+       \r
+       private boolean hasMore;\r
+\r
+       public ReflogPanel(String wicketId, final RepositoryModel model, Repository r, int limit, int pageOffset) {\r
+               super(wicketId);\r
+               boolean pageResults = limit <= 0;\r
+               int changesPerPage = GitBlit.getInteger(Keys.web.reflogChangesPerPage, 10);\r
+               if (changesPerPage <= 1) {\r
+                       changesPerPage = 10;\r
+               }\r
+\r
+               List<RefLogEntry> changes;\r
+               if (pageResults) {\r
+                       changes = RefLogUtils.getLogByRef(model.name, r, pageOffset * changesPerPage, changesPerPage);\r
+               } else {\r
+                       changes = RefLogUtils.getLogByRef(model.name, r, limit);\r
+               }\r
+\r
+               // inaccurate way to determine if there are more commits.\r
+               // works unless commits.size() represents the exact end.\r
+               hasMore = changes.size() >= changesPerPage;\r
+               hasChanges = changes.size() > 0;\r
+               \r
+               setup(changes);\r
+               \r
+               // determine to show pager, more, or neither\r
+               if (limit <= 0) {\r
+                       // no display limit\r
+                       add(new Label("moreChanges").setVisible(false));\r
+               } else {\r
+                       if (pageResults) {\r
+                               // paging\r
+                               add(new Label("moreChanges").setVisible(false));\r
+                       } else {\r
+                               // more\r
+                               if (changes.size() == limit) {\r
+                                       // show more\r
+                                       add(new LinkPanel("moreChanges", "link", new StringResourceModel("gb.moreChanges",\r
+                                                       this, null), ReflogPage.class,\r
+                                                       WicketUtils.newRepositoryParameter(model.name)));\r
+                               } else {\r
+                                       // no more\r
+                                       add(new Label("moreChanges").setVisible(false));\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       \r
+       public ReflogPanel(String wicketId, List<RefLogEntry> changes) {\r
+               super(wicketId);\r
+               hasChanges = changes.size() > 0;\r
+               setup(changes);\r
+               add(new Label("moreChanges").setVisible(false));\r
+       }\r
+       \r
+       protected void setup(List<RefLogEntry> changes) {\r
+               final int hashLen = GitBlit.getInteger(Keys.web.shortCommitIdLength, 6);\r
+\r
+               String dateFormat = GitBlit.getString(Keys.web.datestampLongFormat, "EEEE, MMMM d, yyyy");\r
+               final TimeZone timezone = getTimeZone();\r
+               final DateFormat df = new SimpleDateFormat(dateFormat);\r
+               df.setTimeZone(timezone);\r
+               final Calendar cal = Calendar.getInstance(timezone);\r
+               \r
+               ListDataProvider<RefLogEntry> dp = new ListDataProvider<RefLogEntry>(changes);\r
+               DataView<RefLogEntry> changeView = new DataView<RefLogEntry>("change", dp) {\r
+                       private static final long serialVersionUID = 1L;\r
+\r
+                       public void populateItem(final Item<RefLogEntry> changeItem) {\r
+                               final RefLogEntry change = changeItem.getModelObject();\r
+                               String fullRefName = change.getChangedRefs().get(0);\r
+                               String shortRefName = fullRefName;\r
+                               boolean isTag = false;\r
+                               if (shortRefName.startsWith(Constants.R_HEADS)) {\r
+                                       shortRefName = shortRefName.substring(Constants.R_HEADS.length());\r
+                               } else if (shortRefName.startsWith(Constants.R_TAGS)) {\r
+                                       shortRefName = shortRefName.substring(Constants.R_TAGS.length());\r
+                                       isTag = true;\r
+                               }\r
+                               \r
+                               String fuzzydate;\r
+                               TimeUtils tu = getTimeUtils();\r
+                               Date changeDate = change.date;\r
+                               if (TimeUtils.isToday(changeDate, timezone)) {\r
+                                       fuzzydate = tu.today();\r
+                               } else if (TimeUtils.isYesterday(changeDate, timezone)) {\r
+                                       fuzzydate = tu.yesterday();\r
+                               } else {\r
+                                       // calculate a fuzzy time ago date\r
+                       cal.setTime(changeDate);\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
+                       changeDate = cal.getTime();\r
+                                       fuzzydate = getTimeUtils().timeAgo(changeDate);\r
+                               }\r
+                               changeItem.add(new Label("whenChanged", fuzzydate + ", " + df.format(changeDate)));\r
+\r
+                               Label changeIcon = new Label("changeIcon");\r
+                               if (isTag) {\r
+                                       WicketUtils.setCssClass(changeIcon, "iconic-tag");\r
+                               } else {\r
+                                       WicketUtils.setCssClass(changeIcon, "iconic-upload");\r
+                               }\r
+                               changeItem.add(changeIcon);\r
+\r
+                               if (change.user.username.equals(change.user.emailAddress) && change.user.emailAddress.indexOf('@') > -1) {\r
+                                       // username is an email address - 1.2.1 push log bug\r
+                                       changeItem.add(new Label("whoChanged", change.user.getDisplayName()));\r
+                               } else {\r
+                                       // link to user account page\r
+                                       changeItem.add(new LinkPanel("whoChanged", null, change.user.getDisplayName(),\r
+                                                       UserPage.class, WicketUtils.newUsernameParameter(change.user.username)));\r
+                               }\r
+                               \r
+                               boolean isDelete = false;\r
+                               boolean isRewind = false;\r
+                               String what;\r
+                               String by = null;\r
+                               switch(change.getChangeType(fullRefName)) {\r
+                               case CREATE:\r
+                                       if (isTag) {\r
+                                               // new tag\r
+                                               what = getString("gb.pushedNewTag");\r
+                                       } else {\r
+                                               // new branch\r
+                                               what = getString("gb.pushedNewBranch");\r
+                                       }\r
+                                       break;\r
+                               case DELETE:\r
+                                       isDelete = true;\r
+                                       if (isTag) {\r
+                                               what = getString("gb.deletedTag");\r
+                                       } else {\r
+                                               what = getString("gb.deletedBranch");\r
+                                       }\r
+                                       break;\r
+                               case UPDATE_NONFASTFORWARD:\r
+                                       isRewind = true;\r
+                               default:\r
+                                       what = MessageFormat.format(change.getCommitCount() > 1 ? getString("gb.pushedNCommitsTo") : getString("gb.pushedOneCommitTo") , change.getCommitCount());\r
+                                       \r
+                                       if (change.getAuthorCount() == 1) {\r
+                                               by = MessageFormat.format(getString("gb.byOneAuthor"), change.getAuthorIdent().getName());\r
+                                       } else {\r
+                                               by = MessageFormat.format(getString("gb.byNAuthors"), change.getAuthorCount()); \r
+                                       }\r
+                                       break;\r
+                               }\r
+                               changeItem.add(new Label("whatChanged", what));\r
+                               changeItem.add(new Label("byAuthors", by).setVisible(!StringUtils.isEmpty(by)));\r
+                               \r
+                               changeItem.add(new Label("refRewind", getString("gb.rewind")).setVisible(isRewind));\r
+                               \r
+                               if (isDelete) {\r
+                                       // can't link to deleted ref\r
+                                       changeItem.add(new Label("refChanged", shortRefName));\r
+                               } else if (isTag) {\r
+                                       // link to tag\r
+                                       changeItem.add(new LinkPanel("refChanged", null, shortRefName,\r
+                                                       TagPage.class, WicketUtils.newObjectParameter(change.repository, fullRefName)));\r
+                               } else {\r
+                                       // link to tree\r
+                                       changeItem.add(new LinkPanel("refChanged", null, shortRefName,\r
+                                               TreePage.class, WicketUtils.newObjectParameter(change.repository, fullRefName)));\r
+                               }\r
+                               \r
+                               int maxCommitCount = 5;\r
+                               List<RepositoryCommit> commits = change.getCommits();\r
+                               if (commits.size() > maxCommitCount) {\r
+                                       commits = new ArrayList<RepositoryCommit>(commits.subList(0,  maxCommitCount));                                 \r
+                               }\r
+                               \r
+                               // compare link\r
+                               String compareLinkText = null;\r
+                               if ((change.getCommitCount() <= maxCommitCount) && (change.getCommitCount() > 1)) {\r
+                                       compareLinkText = MessageFormat.format(getString("gb.viewComparison"), commits.size());\r
+                               } else if (change.getCommitCount() > maxCommitCount) {\r
+                                       int diff = change.getCommitCount() - maxCommitCount;\r
+                                       compareLinkText = MessageFormat.format(diff > 1 ? getString("gb.nMoreCommits") : getString("gb.oneMoreCommit"), diff);\r
+                               }\r
+                               if (StringUtils.isEmpty(compareLinkText)) {\r
+                                       changeItem.add(new Label("compareLink").setVisible(false));\r
+                               } else {\r
+                                       String endRangeId = change.getNewId(fullRefName);\r
+                                       String startRangeId = change.getOldId(fullRefName);\r
+                                       changeItem.add(new LinkPanel("compareLink", null, compareLinkText, ComparePage.class, WicketUtils.newRangeParameter(change.repository, startRangeId, endRangeId)));\r
+                               }\r
+                               \r
+                               ListDataProvider<RepositoryCommit> cdp = new ListDataProvider<RepositoryCommit>(commits);\r
+                               DataView<RepositoryCommit> commitsView = new DataView<RepositoryCommit>("commit", cdp) {\r
+                                       private static final long serialVersionUID = 1L;\r
+\r
+                                       public void populateItem(final Item<RepositoryCommit> commitItem) {\r
+                                               final RepositoryCommit commit = commitItem.getModelObject();\r
+\r
+                                               // author gravatar\r
+                                               commitItem.add(new GravatarImage("commitAuthor", commit.getAuthorIdent().getName(),\r
+                                                               commit.getAuthorIdent().getEmailAddress(), null, 16, false, false));\r
+                                               \r
+                                               // merge icon\r
+                                               if (commit.getParentCount() > 1) {\r
+                                                       commitItem.add(WicketUtils.newImage("commitIcon", "commit_merge_16x16.png"));\r
+                                               } else {\r
+                                                       commitItem.add(WicketUtils.newBlankImage("commitIcon"));\r
+                                               }\r
+\r
+                                               // short message\r
+                                               String shortMessage = commit.getShortMessage();\r
+                                               String trimmedMessage = shortMessage;\r
+                                               if (commit.getRefs() != null && commit.getRefs().size() > 0) {\r
+                                                       trimmedMessage = StringUtils.trimString(shortMessage, Constants.LEN_SHORTLOG_REFS);\r
+                                               } else {\r
+                                                       trimmedMessage = StringUtils.trimString(shortMessage, Constants.LEN_SHORTLOG);\r
+                                               }\r
+                                               LinkPanel shortlog = new LinkPanel("commitShortMessage", "list",\r
+                                                               trimmedMessage, CommitPage.class, WicketUtils.newObjectParameter(\r
+                                                                               change.repository, commit.getName()));\r
+                                               if (!shortMessage.equals(trimmedMessage)) {\r
+                                                       WicketUtils.setHtmlTooltip(shortlog, shortMessage);\r
+                                               }\r
+                                               commitItem.add(shortlog);\r
+\r
+                                               // commit hash link\r
+                                               LinkPanel commitHash = new LinkPanel("hashLink", null, commit.getName().substring(0, hashLen),\r
+                                                               CommitPage.class, WicketUtils.newObjectParameter(\r
+                                                                               change.repository, commit.getName()));\r
+                                               WicketUtils.setCssClass(commitHash, "shortsha1");\r
+                                               WicketUtils.setHtmlTooltip(commitHash, commit.getName());\r
+                                               commitItem.add(commitHash);\r
+                                       }\r
+                               };\r
+\r
+                               changeItem.add(commitsView);\r
+                       }\r
+               };\r
+               \r
+               add(changeView);\r
+       }\r
+\r
+       public boolean hasMore() {\r
+               return hasMore;\r
+       }\r
+       \r
+       public boolean hideIfEmpty() {\r
+               setVisible(hasChanges);\r
+               return hasChanges;\r
+       }\r
+}\r
index 351d43fe5fb0c63f3a69d7e9fcfd8e4cce18ce6e..6074b36900eb2ca0915f65c0334b00b3d718a47d 100644 (file)
@@ -129,23 +129,39 @@ navbar div>ul .menu-dropdown li a:hover,.nav .menu-dropdown li a:hover,.navbar d
     background-color: #002060;\r
 }\r
 \r
-div.push {\r
+div.reflog {\r
     border-bottom: 1px solid #ddd;\r
        margin-bottom: 5px;\r
        padding-bottom: 5px;\r
 }\r
 \r
-div.push .icon {\r
+div.reflog .icon {\r
     font-size: 42px;\r
     line-height: 42px;\r
 }\r
 \r
-div.push i {\r
+div.reflog .when {\r
+       color: #aaa;\r
+}\r
+\r
+div.reflog i {\r
     font-size: 42px;\r
     color: #bbb;\r
     vertical-align: middle;\r
 }\r
 \r
+div.dashboardTitle {\r
+       font-size: 1.75em;\r
+       padding-bottom: 5px;\r
+       margin-bottom: 10px;\r
+       border-bottom: 1px solid #ccc;\r
+}\r
+\r
+div.dashboardTitle small {\r
+       color: #888;\r
+       font-size: 0.7em;\r
+}\r
+\r
 .repositorynavbar {\r
        background-color: #fbfbfb;\r
        border-bottom: 1px solid #ccc;\r
index c1aaf1a3571dc131c0aac8fecfd6bdf4d911e29f..7607fbf7985e2d5f96a460747dcebdaf32b837ea 100644 (file)
@@ -38,12 +38,12 @@ import com.gitblit.Constants.AccessRestrictionType;
 import com.gitblit.Constants.AuthorizationControl;\r
 import com.gitblit.GitBlit;\r
 import com.gitblit.Keys;\r
-import com.gitblit.models.PushLogEntry;\r
+import com.gitblit.models.RefLogEntry;\r
 import com.gitblit.models.RepositoryModel;\r
 import com.gitblit.models.UserModel;\r
 import com.gitblit.utils.ArrayUtils;\r
 import com.gitblit.utils.JGitUtils;\r
-import com.gitblit.utils.PushLogUtils;\r
+import com.gitblit.utils.RefLogUtils;\r
 \r
 public class GitServletTest {\r
 \r
@@ -788,7 +788,7 @@ public class GitServletTest {
                String name = "refchecks/ticgit.git";\r
                File refChecks = new File(GitBlitSuite.REPOSITORIES, name);\r
                Repository repository = new FileRepositoryBuilder().setGitDir(refChecks).build();\r
-               List<PushLogEntry> pushes = PushLogUtils.getPushLog(name, repository);\r
+               List<RefLogEntry> pushes = RefLogUtils.getRefLog(name, repository);\r
                GitBlitSuite.close(repository);\r
                assertTrue("Repository has an empty push log!", pushes.size() > 0);\r
        }\r
index 0f46b53bfbdc01f891c5d775d919b9858296dc1d..f5d5965bc0714b02d156de1ef626fb5248784756 100644 (file)
@@ -25,8 +25,8 @@ import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
 import org.eclipse.jgit.util.FS;\r
 import org.junit.Test;\r
 \r
-import com.gitblit.models.PushLogEntry;\r
-import com.gitblit.utils.PushLogUtils;\r
+import com.gitblit.models.RefLogEntry;\r
+import com.gitblit.utils.RefLogUtils;\r
 \r
 public class PushLogTest {\r
 \r
@@ -35,7 +35,7 @@ public class PushLogTest {
                String name = "~james/helloworld.git";\r
                File gitDir = FileKey.resolve(new File(GitBlitSuite.REPOSITORIES, name), FS.DETECTED);\r
                Repository repository = new FileRepositoryBuilder().setGitDir(gitDir).build();\r
-               List<PushLogEntry> pushes = PushLogUtils.getPushLog(name, repository);\r
+               List<RefLogEntry> pushes = RefLogUtils.getRefLog(name, repository);\r
                GitBlitSuite.close(repository);\r
        }\r
 }
\ No newline at end of file