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.
# 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
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
\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
*
* @author James Moger
*/
-public class DailyLogEntry extends PushLogEntry implements Serializable {
+public class DailyLogEntry extends RefLogEntry implements Serializable {
private static final long serialVersionUID = 1L;
+++ /dev/null
-/*\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
--- /dev/null
+/*\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
+++ /dev/null
-/*
- * 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;
- }
-}
--- /dev/null
+/*
+ * 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;
+ }
+}
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
\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
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
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
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
+++ /dev/null
-<!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"> </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"> </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"> </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
*/\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
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
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
}\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
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
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
}\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
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
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
--- /dev/null
+<!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"> </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"> </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"> </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
--- /dev/null
+/*\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
<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
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
\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
\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"> </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
*/\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
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
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
.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
}\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
\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
<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
*/\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
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
// 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
}\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
\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
+++ /dev/null
-<!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">« <wicket:message key="gb.pagePrevious"></wicket:message></a> | <a wicket:id="nextPage"><wicket:message key="gb.pageNext"></wicket:message> »</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">« <wicket:message key="gb.pagePrevious"></wicket:message></a> | <a wicket:id="nextPage"><wicket:message key="gb.pageNext"></wicket:message> »</a> \r
- </div>\r
- \r
-</wicket:extend>\r
-</body>\r
-</html>
\ No newline at end of file
+++ /dev/null
-/*\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
--- /dev/null
+<!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">« <wicket:message key="gb.pagePrevious"></wicket:message></a> | <a wicket:id="nextPage"><wicket:message key="gb.pageNext"></wicket:message> »</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">« <wicket:message key="gb.pagePrevious"></wicket:message></a> | <a wicket:id="nextPage"><wicket:message key="gb.pageNext"></wicket:message> »</a> \r
+ </div>\r
+ \r
+</wicket:extend>\r
+</body>\r
+</html>
\ No newline at end of file
--- /dev/null
+/*\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
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
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
// 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
--- /dev/null
+<!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
--- /dev/null
+/*\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
<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
}\r
\r
add(new ExternalLink("syndication", SyndicationServlet.asLink("", entry.name, null, 0)));\r
-\r
- add(new RepositoryUrlPanel("repositoryPrimaryUrl", true, user, entry));\r
}\r
}\r
+++ /dev/null
-<!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
+++ /dev/null
-/*\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
--- /dev/null
+<!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
--- /dev/null
+/*\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
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
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
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
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
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