Refinements to push log display and daily digests

* Properly support timezone-based date groupings
* Polish css for the major browsers on Win & Linux
* Use Gitblit constants for refs instead of JGit constants
diff --git a/src/main/java/com/gitblit/Constants.java b/src/main/java/com/gitblit/Constants.java
index 0514045..cd0fea6 100644
--- a/src/main/java/com/gitblit/Constants.java
+++ b/src/main/java/com/gitblit/Constants.java
@@ -80,13 +80,27 @@
 	

 	public static final String ISO8601 = "yyyy-MM-dd'T'HH:mm:ssZ";

 	

-	public static final String R_GITBLIT = "refs/gitblit/";

-	

 	public static final String baseFolder = "baseFolder";

 	

 	public static final String baseFolder$ = "${" + baseFolder + "}";

 	

 	public static final String contextFolder$ = "${contextFolder}";

+	

+	public static final String HEAD = "HEAD";

+

+	public static final String R_GITBLIT = "refs/gitblit/";

+	

+	public static final String R_HEADS = "refs/heads/";

+	

+	public static final String R_NOTES = "refs/notes/";

+	

+	public static final String R_CHANGES = "refs/changes/";

+	

+	public static final String R_PULL= "refs/pull/";

+

+	public static final String R_TAGS = "refs/tags/";

+	

+	public static final String R_REMOTES = "refs/remotes/";

 

 	public static String getVersion() {

 		String v = Constants.class.getPackage().getImplementationVersion();

diff --git a/src/main/java/com/gitblit/client/Translation.java b/src/main/java/com/gitblit/client/Translation.java
index 16ef20d..9f643db 100644
--- a/src/main/java/com/gitblit/client/Translation.java
+++ b/src/main/java/com/gitblit/client/Translation.java
@@ -43,7 +43,7 @@
 		}

 		translation = bundle;

 		

-		timeUtils = new TimeUtils(translation);

+		timeUtils = new TimeUtils(translation, null);

 	}

 

 	public static String get(String key) {

diff --git a/src/main/java/com/gitblit/models/DailyLogEntry.java b/src/main/java/com/gitblit/models/DailyLogEntry.java
index db60526..a459d76 100644
--- a/src/main/java/com/gitblit/models/DailyLogEntry.java
+++ b/src/main/java/com/gitblit/models/DailyLogEntry.java
@@ -19,6 +19,7 @@
 import java.util.Date;
 
 import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.transport.ReceiveCommand;
 
 /**
  * Model class to simulate a push for presentation in the push log news feed
@@ -56,4 +57,25 @@
 		
 		return super.getAuthorIdent();
 	}
+	
+	/**
+	 * Tracks the change type for the specified ref.
+	 * 
+	 * @param ref
+	 * @param type
+	 * @param oldId
+	 * @param newId
+	 */
+	public void updateRef(String ref, ReceiveCommand.Type type, String oldId, String newId) {
+		// daily digests are filled from most recent to oldest 
+		String preservedNewId = getNewId(ref);
+		if (preservedNewId == null) {
+			// no preserved new id, this is newest commit
+			// for this ref
+			preservedNewId = newId;
+		}
+		refUpdates.put(ref, type);
+		refIdChanges.put(ref, oldId + "-" + preservedNewId);
+	}
+
 }
diff --git a/src/main/java/com/gitblit/models/PushLogEntry.java b/src/main/java/com/gitblit/models/PushLogEntry.java
index 8b006d9..77bed38 100644
--- a/src/main/java/com/gitblit/models/PushLogEntry.java
+++ b/src/main/java/com/gitblit/models/PushLogEntry.java
@@ -51,9 +51,9 @@
 

 	private final Set<RepositoryCommit> commits;

 	

-	private final Map<String, ReceiveCommand.Type> refUpdates;

+	protected final Map<String, ReceiveCommand.Type> refUpdates;

 	

-	private final Map<String, String> refIdChanges;

+	protected final Map<String, String> refIdChanges;

 	

 	private int authorCount;

 

diff --git a/src/main/java/com/gitblit/models/RepositoryModel.java b/src/main/java/com/gitblit/models/RepositoryModel.java
index 6e1e226..0e39d91 100644
--- a/src/main/java/com/gitblit/models/RepositoryModel.java
+++ b/src/main/java/com/gitblit/models/RepositoryModel.java
@@ -186,6 +186,10 @@
 		return !accessRestriction.atLeast(AccessRestrictionType.VIEW);

 	}

 	

+	public boolean isShowActivity() {

+		return maxActivityCommits > -1;

+	}

+	

 	public boolean isSparkleshared() {

 		return !StringUtils.isEmpty(sparkleshareId);

 	}

diff --git a/src/main/java/com/gitblit/utils/ActivityUtils.java b/src/main/java/com/gitblit/utils/ActivityUtils.java
index 1792bf2..015e8d3 100644
--- a/src/main/java/com/gitblit/utils/ActivityUtils.java
+++ b/src/main/java/com/gitblit/utils/ActivityUtils.java
@@ -81,7 +81,7 @@
 

 		Map<String, Activity> activity = new HashMap<String, Activity>();

 		for (RepositoryModel model : models) {

-			if (model.maxActivityCommits == -1) {

+			if (!model.isShowActivity()) {

 				// skip this repository

 				continue;

 			}

diff --git a/src/main/java/com/gitblit/utils/PushLogUtils.java b/src/main/java/com/gitblit/utils/PushLogUtils.java
index 6e77169..fed5b19 100644
--- a/src/main/java/com/gitblit/utils/PushLogUtils.java
+++ b/src/main/java/com/gitblit/utils/PushLogUtils.java
@@ -20,7 +20,6 @@
 import java.text.MessageFormat;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
@@ -28,6 +27,7 @@
 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;
@@ -37,7 +37,6 @@
 import org.eclipse.jgit.dircache.DirCacheEntry;
 import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.lib.CommitBuilder;
-import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.FileMode;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectInserter;
@@ -53,6 +52,7 @@
 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;
@@ -172,7 +172,7 @@
 				CommitBuilder commit = new CommitBuilder();
 				commit.setAuthor(ident);
 				commit.setCommitter(ident);
-				commit.setEncoding(Constants.CHARACTER_ENCODING);
+				commit.setEncoding(Constants.ENCODING);
 				commit.setMessage(message);
 				commit.setParentId(headId);
 				commit.setTreeId(indexTreeId);
@@ -272,7 +272,7 @@
 				dcEntry.setFileMode(FileMode.REGULAR_FILE);
 
 				// insert object
-				dcEntry.setObjectId(inserter.insert(Constants.OBJ_BLOB, content.getBytes("UTF-8")));
+				dcEntry.setObjectId(inserter.insert(org.eclipse.jgit.lib.Constants.OBJ_BLOB, content.getBytes("UTF-8")));
 
 				// add to temporary in-core index
 				dcBuilder.add(dcEntry);
@@ -510,47 +510,71 @@
      * @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) {
-
+                                                 Date minimumDate, int offset, int maxCount,
+                                                 TimeZone timezone) {
         DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
-//		df.setTimeZone(timezone);
+		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));
                 }
-                PushLogEntry digest = dailydigests.get(dateStr);
+                DailyLogEntry digest = dailydigests.get(dateStr);
                 if (commit.getParentCount() == 0) {
+                	linearParent = null;
                 	digest.updateRef(branch, ReceiveCommand.Type.CREATE);
                 } else {
-                	digest.updateRef(branch, ReceiveCommand.Type.UPDATE, commit.getParents()[0].getId().getName(), commit.getName());
+                	linearParent = commit.getParents()[0].getId().getName();
+                	digest.updateRef(branch, ReceiveCommand.Type.UPDATE, linearParent, commit.getName());
                 }
+                
                 RepositoryCommit repoCommit = digest.addCommit(branch, commit);
                 if (repoCommit != null) {
-                    repoCommit.setRefs(allRefs.get(commit.getId()));
-                    if (!ArrayUtils.isEmpty(repoCommit.getRefs())) {
-                        // treat tags as special events in the log
-                        for (RefModel ref : repoCommit.getRefs()) {
+                	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));
+                        			tags.put(dateStr, new DailyLogEntry(repositoryName, tagDate, tagUser));
                                 }
                                 PushLogEntry tagEntry = tags.get(dateStr);
                                 tagEntry.updateRef(ref.getName(), ReceiveCommand.Type.CREATE);
-                                tagEntry.addCommits(Arrays.asList(repoCommit));
+                                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);
                             }
                         }
                     }
@@ -560,6 +584,7 @@
 
         List<DailyLogEntry> list = new ArrayList<DailyLogEntry>(dailydigests.values());
         list.addAll(tags.values());
+        //list.addAll(pulls.values());
         Collections.sort(list);
         return list;
     }
@@ -571,12 +596,14 @@
      * @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) {
+    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);
+        List<DailyLogEntry> pushes = getDailyLog(repositoryName, repository, minimumDate, 0, -1, timezone);
         for (DailyLogEntry push : pushes) {
             for (String ref : push.getChangedRefs()) {
                 if (!refMap.containsKey(ref)) {
diff --git a/src/main/java/com/gitblit/utils/TimeUtils.java b/src/main/java/com/gitblit/utils/TimeUtils.java
index ec8871c..9b5927c 100644
--- a/src/main/java/com/gitblit/utils/TimeUtils.java
+++ b/src/main/java/com/gitblit/utils/TimeUtils.java
@@ -20,6 +20,7 @@
 import java.util.Calendar;

 import java.util.Date;

 import java.util.ResourceBundle;

+import java.util.TimeZone;

 

 /**

  * Utility class of time functions.

@@ -40,12 +41,15 @@
 	

 	private final ResourceBundle translation;

 	

+	private final TimeZone timezone;

+	

 	public TimeUtils() {

-		this(null);

+		this(null, null);

 	}

 	

-	public TimeUtils(ResourceBundle translation) {

+	public TimeUtils(ResourceBundle translation, TimeZone timezone) {

 		this.translation = translation;

+		this.timezone = timezone;

 	}

 

 	/**

@@ -54,8 +58,13 @@
 	 * @param date

 	 * @return true if date is today

 	 */

-	public static boolean isToday(Date date) {

-		return (System.currentTimeMillis() - date.getTime()) < ONEDAY;

+	public static boolean isToday(Date date, TimeZone timezone) {

+		Date now = new Date();

+		SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd");

+		if (timezone != null) {

+			df.setTimeZone(timezone);

+		}

+		return df.format(now).equals(df.format(date));

 	}

 

 	/**

@@ -64,11 +73,14 @@
 	 * @param date

 	 * @return true if date is yesterday

 	 */

-	public static boolean isYesterday(Date date) {

+	public static boolean isYesterday(Date date, TimeZone timezone) {

 		Calendar cal = Calendar.getInstance();

 		cal.setTime(new Date());

 		cal.add(Calendar.DATE, -1);

 		SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd");

+		if (timezone != null) {

+			df.setTimeZone(timezone);

+		}

 		return df.format(cal.getTime()).equals(df.format(date));

 	}

 

@@ -205,7 +217,7 @@
 	 * @return the string representation of the duration OR the css class

 	 */

 	private String timeAgo(Date date, boolean css) {

-		if (isToday(date) || isYesterday(date)) {

+		if (isToday(date, timezone) || isYesterday(date, timezone)) {

 			int mins = minutesAgo(date, true);

 			if (mins >= 120) {

 				if (css) {

diff --git a/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties b/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties
index 2fe2293..a278eff 100644
--- a/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties
+++ b/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties
@@ -479,6 +479,8 @@
 gb.pushedNewBranch = pushed new branch
 gb.createdNewBranch = created new branch
 gb.deletedBranch = deleted branch
+gb.createdNewPullRequest = created pull request
+gb.mergedPullRequest = merged pull request
 gb.rewind = REWIND
 gb.star = star
 gb.unstar = unstar
@@ -486,3 +488,5 @@
 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.findSomeRepositories = find some repositories
diff --git a/src/main/java/com/gitblit/wicket/WicketUtils.java b/src/main/java/com/gitblit/wicket/WicketUtils.java
index 91686b6..0a85dda 100644
--- a/src/main/java/com/gitblit/wicket/WicketUtils.java
+++ b/src/main/java/com/gitblit/wicket/WicketUtils.java
@@ -520,9 +520,9 @@
 			dateString = df.format(date);

 		}

 		String title = null;

-		if (TimeUtils.isToday(date)) {

+		if (TimeUtils.isToday(date, timeZone)) {

 			title = timeUtils.today();

-		} else if (TimeUtils.isYesterday(date)) {

+		} else if (TimeUtils.isYesterday(date, timeZone)) {

 				title = timeUtils.yesterday();

 		} else if (date.getTime() <= System.currentTimeMillis()) {

 			// past

diff --git a/src/main/java/com/gitblit/wicket/pages/BasePage.java b/src/main/java/com/gitblit/wicket/pages/BasePage.java
index c819c78..192f012 100644
--- a/src/main/java/com/gitblit/wicket/pages/BasePage.java
+++ b/src/main/java/com/gitblit/wicket/pages/BasePage.java
@@ -100,7 +100,7 @@
 			} catch (Throwable t) {

 				bundle = ResourceBundle.getBundle("com.gitblit.wicket.GitBlitWebApp");

 			}

-			timeUtils = new TimeUtils(bundle);

+			timeUtils = new TimeUtils(bundle, getTimeZone());

 		}

 		return timeUtils;

 	}

@@ -125,6 +125,9 @@
 

 	protected void setupPage(String repositoryName, String pageName) {

 		String siteName = GitBlit.getString(Keys.web.siteName, Constants.NAME);

+		if (StringUtils.isEmpty(siteName)) {

+			siteName = Constants.NAME;

+		}

 		if (repositoryName != null && repositoryName.trim().length() > 0) {

 			add(new Label("title", repositoryName + " - " + siteName));

 		} else {

diff --git a/src/main/java/com/gitblit/wicket/pages/DashboardPage.html b/src/main/java/com/gitblit/wicket/pages/DashboardPage.html
index d2516f0..16b1cf8 100644
--- a/src/main/java/com/gitblit/wicket/pages/DashboardPage.html
+++ b/src/main/java/com/gitblit/wicket/pages/DashboardPage.html
@@ -19,7 +19,7 @@
 					</tr>

 				</table>

 			</div>

-			<div wicket:id="pushes"></div>

+			<div wicket:id="digests"></div>

 		</div>

 		<div class="span5">

 			<div wicket:id="active">[active]</div>

@@ -44,9 +44,9 @@
 		<div ng-repeat="item in starred | limitTo: 20 | filter:query" style="padding: 3px;border-top: 1px solid #ddd;">

 			<b><span class="repositorySwatch" style="background-color:{{item.c}};"><span ng-show="item.wc">!</span><span ng-show="!item.wc">&nbsp;</span></span></b>

 			<a href="summary/?r={{item.r}}">{{item.p}}<b>{{item.n}}</b></a>

-			<span class="link hidden-tablet hidden-phone" style="color: #bbb;" title="{{item.d}}">{{item.t}}</span>

+			<span class="link hidden-tablet hidden-phone" style="color: #aaa;" title="{{item.d}}">{{item.t}}</span>

 			<span ng-show="item.s" class="pull-right">

-				<span style="padding: 0px 5px;color: #888;font-weight:bold;vertical-align:middle;">{{item.s | number}} <i class="iconic-star"></i></span>

+				<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>

 			</span>

 		</div>

 		

@@ -69,14 +69,14 @@
 			<a href="summary/?r={{item.r}}">{{item.p}}<b>{{item.n}}</b></a>

 			<span class="link hidden-tablet hidden-phone" style="color: #bbb;" title="{{item.d}}">{{item.t}}</span>

 			<span ng-show="item.s" class="pull-right">

-				<span style="padding: 0px 5px;color: #888;font-weight:bold;vertical-align:middle;">{{item.s | number}} <i class="iconic-star"></i></span>

+				<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>

 			</span>

 		</div>		

 	</div>

 </wicket:fragment>

 

 <wicket:fragment wicket:id="activeListFragment">

-	<div ng-controller="activeCtrl" style="border: 1px solid #ddd;border-radius: 4px;">

+	<div ng-controller="activeCtrl" style="border: 1px solid #ddd;border-radius: 4px;margin-bottom: 20px;">

 		<div class="header" style="padding: 5px;border: none;"><i class="icon-user"></i> <wicket:message key="gb.activeRepositories"></wicket:message> ({{active.length}})

 			<div style="padding: 5px 0px 0px;">

 				<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>

@@ -88,7 +88,7 @@
 			<a href="summary/?r={{item.r}}">{{item.p}}<b>{{item.n}}</b></a>

 			<span class="link hidden-tablet hidden-phone" style="color: #bbb;" title="{{item.d}}">{{item.t}}</span>

 			<span ng-show="item.s" class="pull-right">

-				<span style="padding: 0px 5px;color: #888;font-weight:bold;vertical-align:middle;">{{item.s | number}} <i class="iconic-star"></i></span>

+				<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>

 			</span>

 		</div>		

 	</div>

diff --git a/src/main/java/com/gitblit/wicket/pages/DashboardPage.java b/src/main/java/com/gitblit/wicket/pages/DashboardPage.java
index 6a4c565..7e40a54 100644
--- a/src/main/java/com/gitblit/wicket/pages/DashboardPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/DashboardPage.java
@@ -29,8 +29,11 @@
 import java.util.Comparator;

 import java.util.Date;

 import java.util.HashMap;

+import java.util.HashSet;

 import java.util.List;

 import java.util.Map;

+import java.util.Set;

+import java.util.TimeZone;

 

 import org.apache.wicket.Component;

 import org.apache.wicket.PageParameters;

@@ -103,6 +106,9 @@
 		add(repositoriesMessage);

 

 		UserModel user = GitBlitWebSession.get().getUser();

+		if (user == null) {

+			user = UserModel.ANONYMOUS;

+		}

 

 		Comparator<RepositoryModel> lastUpdateSort = new Comparator<RepositoryModel>() {

 			@Override

@@ -111,33 +117,6 @@
 			}

 		};

 		

-		Map<String, RepositoryModel> reposMap = new HashMap<String, RepositoryModel>();

-

-		// owned repositories 

-		List<RepositoryModel> owned = new ArrayList<RepositoryModel>();

-		if (user != null && !UserModel.ANONYMOUS.equals(user)) {

-			for (RepositoryModel model : GitBlit.self().getRepositoryModels(user)) {

-				reposMap.put(model.name, model);

-				if (model.isUsersPersonalRepository(user.username) || model.isOwner(user.username)) {

-					owned.add(model);

-				}

-			}

-		}

-		Collections.sort(owned, lastUpdateSort);

-

-		// starred repositories

-		List<RepositoryModel> starred = new ArrayList<RepositoryModel>();

-		if (user != null && !UserModel.ANONYMOUS.equals(user)) {

-			for (String name : user.getPreferences().getStarredRepositories()) {

-				if (!reposMap.containsKey(name)) {

-					RepositoryModel repo = GitBlit.self().getRepositoryModel(name);

-					reposMap.put(name, repo);

-				}

-				starred.add(reposMap.get(name));

-			}

-		}

-		Collections.sort(starred, lastUpdateSort);

-				

 		// parameters

 		int daysBack = params == null ? 0 : WicketUtils.getDaysBack(params);

 		if (daysBack < 1) {

@@ -146,38 +125,61 @@
 		Calendar c = Calendar.getInstance();

 		c.add(Calendar.DATE, -1*daysBack);

 		Date minimumDate = c.getTime();

+		TimeZone timezone = getTimeZone();

 		

-		// active repositories (displayed for anonymous users)

+		// build repo lists 

+		List<RepositoryModel> starred = new ArrayList<RepositoryModel>();

+		List<RepositoryModel> owned = new ArrayList<RepositoryModel>();

 		List<RepositoryModel> active = new ArrayList<RepositoryModel>();

-		if (user == null || UserModel.ANONYMOUS.equals(user)) {

-			List<RepositoryModel> list = GitBlit.self().getRepositoryModels(UserModel.ANONYMOUS);

-			for (RepositoryModel model : list) {

-				if (model.lastChange.after(minimumDate)) {

-					active.add(model);

-					reposMap.put(model.name, model);

-				}

+

+		for (RepositoryModel model : GitBlit.self().getRepositoryModels(user)) {

+			if (model.isUsersPersonalRepository(user.username) || model.isOwner(user.username)) {

+				owned.add(model);

 			}

-			Collections.sort(active, lastUpdateSort);

+			

+			if (user.getPreferences().isStarredRepository(model.name)) {

+				starred.add(model);

+			}

+			

+			if (model.isShowActivity() && model.lastChange.after(minimumDate)) {

+				active.add(model);

+			}

 		}

 		

-		// show pushlog feed

+		Collections.sort(owned, lastUpdateSort);

+		Collections.sort(starred, lastUpdateSort);

+		Collections.sort(active, lastUpdateSort);

+		

+		Set<RepositoryModel> feedSources = new HashSet<RepositoryModel>();

+		feedSources.addAll(starred);

+		if (feedSources.isEmpty()) {

+			feedSources.addAll(active);

+		}

+		

+		// create daily commit digest feed

 		List<PushLogEntry> pushes = new ArrayList<PushLogEntry>();

-		for (RepositoryModel model : reposMap.values()) {

+		for (RepositoryModel model : feedSources) {

 			Repository repository = GitBlit.self().getRepository(model.name);

-			List<DailyLogEntry> entries = PushLogUtils.getDailyLogByRef(model.name, repository, minimumDate);

+			List<DailyLogEntry> entries = PushLogUtils.getDailyLogByRef(model.name, repository, minimumDate, timezone);

 			pushes.addAll(entries);

 			repository.close();

 		}

 		

 		if (pushes.size() == 0) {

-			if (reposMap.size() == 0) {

-				add(new LinkPanel("pushes", null, "find some repositories", RepositoriesPage.class));

+			// quiet or no starred repositories

+			if (feedSources.size() == 0) {

+				if (UserModel.ANONYMOUS.equals(user)) {

+					add(new Label("digests", getString("gb.noActivity")));	

+				} else {

+					add(new LinkPanel("digests", null, getString("gb.findSomeRepositories"), RepositoriesPage.class));

+				}

 			} else {

-				add(new Label("pushes", "all is quiet"));

+				add(new Label("digests", getString("gb.noActivity")));

 			}

 		} else {

+			// show daily commit digest feed

 			Collections.sort(pushes);

-			add(new PushesPanel("pushes", pushes));

+			add(new PushesPanel("digests", pushes));

 		}

 		

 		// add the nifty charts

@@ -187,11 +189,11 @@
 		}

 		

 		// active repository list

-		if (ArrayUtils.isEmpty(active)) {

-			add(new Label("active").setVisible(false));

-		} else {

+		if (starred.isEmpty()) {

 			Fragment activeView = createNgList("active", "activeListFragment", "activeCtrl", active);

 			add(activeView);

+		} else {

+			add(new Label("active").setVisible(false));

 		}

 		

 		// starred repository list

diff --git a/src/main/java/com/gitblit/wicket/pages/RepositoryPage.java b/src/main/java/com/gitblit/wicket/pages/RepositoryPage.java
index 918127b..80a977f 100644
--- a/src/main/java/com/gitblit/wicket/pages/RepositoryPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/RepositoryPage.java
@@ -286,8 +286,8 @@
 			}

 		}

 		

-		// (un)star link allows a user to star someone else's repository

-		if (user.isAuthenticated && !model.isOwner(user.username) && !model.isUsersPersonalRepository(user.username)) {

+		// (un)star link allows a user to star a repository

+		if (user.isAuthenticated) {

 			PageParameters starParams = DeepCopier.copy(getPageParameters());

 			starParams.put(PARAM_STAR, !user.getPreferences().isStarredRepository(model.name));

 			String toggleStarUrl = getRequestCycle().urlFor(getClass(), starParams).toString();

@@ -301,7 +301,7 @@
 				add(new Label("unstarLink").setVisible(false));

 			}

 		} else {

-			// anonymous user or the repository owner is browsing the repository

+			// anonymous user

 			add(new Label("starLink").setVisible(false));

 			add(new Label("unstarLink").setVisible(false));

 		}

diff --git a/src/main/java/com/gitblit/wicket/panels/BasePanel.java b/src/main/java/com/gitblit/wicket/panels/BasePanel.java
index 9c7cc85..e4aeeb0 100644
--- a/src/main/java/com/gitblit/wicket/panels/BasePanel.java
+++ b/src/main/java/com/gitblit/wicket/panels/BasePanel.java
@@ -53,7 +53,7 @@
 			} catch (Throwable t) {

 				bundle = ResourceBundle.getBundle("com.gitblit.wicket.GitBlitWebApp");

 			}

-			timeUtils = new TimeUtils(bundle);

+			timeUtils = new TimeUtils(bundle, getTimeZone());

 		}

 		return timeUtils;

 	}

diff --git a/src/main/java/com/gitblit/wicket/panels/BranchesPanel.html b/src/main/java/com/gitblit/wicket/panels/BranchesPanel.html
index 58c86a4..e95b283 100644
--- a/src/main/java/com/gitblit/wicket/panels/BranchesPanel.html
+++ b/src/main/java/com/gitblit/wicket/panels/BranchesPanel.html
@@ -8,7 +8,7 @@
 <wicket:panel>

 

 	<!-- header -->

-	<div class="header"><i class="icon-random" style="vertical-align: middle;"></i> <b><span wicket:id="branches">[branches header]</span></b></div>	

+	<div class="header"><i class="icon-random"></i> <b><span wicket:id="branches">[branches header]</span></b></div>	

 	

 	<table class="pretty">

 		<tbody>

diff --git a/src/main/java/com/gitblit/wicket/panels/LogPanel.html b/src/main/java/com/gitblit/wicket/panels/LogPanel.html
index 2b2605a..1abda87 100644
--- a/src/main/java/com/gitblit/wicket/panels/LogPanel.html
+++ b/src/main/java/com/gitblit/wicket/panels/LogPanel.html
@@ -8,7 +8,7 @@
 <wicket:panel>

 

 	<!-- header -->	

-	<div class="header"><i class="icon-refresh" style="vertical-align: middle;"></i> <b><span wicket:id="header">[log header]</span></b></div>

+	<div class="header"><i class="icon-refresh"></i> <b><span wicket:id="header">[log header]</span></b></div>

 	<table class="pretty">

 		<tbody>

        		<tr wicket:id="commit">

diff --git a/src/main/java/com/gitblit/wicket/panels/PushesPanel.html b/src/main/java/com/gitblit/wicket/panels/PushesPanel.html
index 9fb5aed..1ba5ab2 100644
--- a/src/main/java/com/gitblit/wicket/panels/PushesPanel.html
+++ b/src/main/java/com/gitblit/wicket/panels/PushesPanel.html
@@ -9,13 +9,18 @@
 <div wicket:id="push" class="push">

 	<table style="padding: 3px 0px;">

 	<tr>

-		<td class="hidden-phone" style="vertical-align: top;padding-top:10px"><i wicket:id="pushIcon"></i></td>

-		<td style="padding-left: 7px;">

+		<td class="icon hidden-phone"><i wicket:id="pushIcon"></i></td>

+		<td style="padding-left: 7px;vertical-align:middle;">

 			<div>

-				<span 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>

+				<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>

 			</div>

 			<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>

-			<div style="padding: 10px 0px 5px;">

+		</td>

+	</tr>

+	<tr>

+		<td></td>

+		<td style="padding-left: 7px;">

+			<div>

 				<table>

 					<tr wicket:id="commit">

 						<td class="hidden-phone hidden-tablet" style="vertical-align:top;padding-left:7px;"><span wicket:id="commitAuthor"></span></td>

diff --git a/src/main/java/com/gitblit/wicket/panels/PushesPanel.java b/src/main/java/com/gitblit/wicket/panels/PushesPanel.java
index 7bac70e..5c473f7 100644
--- a/src/main/java/com/gitblit/wicket/panels/PushesPanel.java
+++ b/src/main/java/com/gitblit/wicket/panels/PushesPanel.java
@@ -15,9 +15,14 @@
  */

 package com.gitblit.wicket.panels;

 

+import java.text.DateFormat;

 import java.text.MessageFormat;

+import java.text.SimpleDateFormat;

 import java.util.ArrayList;

+import java.util.Calendar;

+import java.util.Date;

 import java.util.List;

+import java.util.TimeZone;

 

 import org.apache.wicket.markup.html.basic.Label;

 import org.apache.wicket.markup.repeater.Item;

@@ -35,6 +40,7 @@
 import com.gitblit.models.RepositoryModel;

 import com.gitblit.utils.PushLogUtils;

 import com.gitblit.utils.StringUtils;

+import com.gitblit.utils.TimeUtils;

 import com.gitblit.wicket.WicketUtils;

 import com.gitblit.wicket.pages.CommitPage;

 import com.gitblit.wicket.pages.ComparePage;

@@ -107,6 +113,12 @@
 	protected void setup(List<PushLogEntry> pushes, final boolean showRepo) {

 		final int hashLen = GitBlit.getInteger(Keys.web.shortCommitIdLength, 6);

 

+		String dateFormat = GitBlit.getString(Keys.web.datestampLongFormat, "EEEE, MMMM d, yyyy");

+		final TimeZone timezone = getTimeZone();

+		final DateFormat df = new SimpleDateFormat(dateFormat);

+		df.setTimeZone(timezone);

+		final Calendar cal = Calendar.getInstance(timezone);

+		

 		ListDataProvider<PushLogEntry> dp = new ListDataProvider<PushLogEntry>(pushes);

 		DataView<PushLogEntry> pushView = new DataView<PushLogEntry>("push", dp) {

 			private static final long serialVersionUID = 1L;

@@ -116,15 +128,41 @@
 				String fullRefName = push.getChangedRefs().get(0);

 				String shortRefName = fullRefName;

 				boolean isTag = false;

-				if (shortRefName.startsWith(org.eclipse.jgit.lib.Constants.R_HEADS)) {

-					shortRefName = shortRefName.substring(org.eclipse.jgit.lib.Constants.R_HEADS.length());

-				} else if (shortRefName.startsWith(org.eclipse.jgit.lib.Constants.R_TAGS)) {

-					shortRefName = shortRefName.substring(org.eclipse.jgit.lib.Constants.R_TAGS.length());

+				boolean isPull = false;

+				if (shortRefName.startsWith(Constants.R_HEADS)) {

+					shortRefName = shortRefName.substring(Constants.R_HEADS.length());

+				} else if (shortRefName.startsWith(Constants.R_TAGS)) {

+					shortRefName = shortRefName.substring(Constants.R_TAGS.length());

 					isTag = true;

+				} else if (shortRefName.startsWith(Constants.R_PULL)) {

+					shortRefName = "#" + shortRefName.substring(Constants.R_PULL.length());

+					if (shortRefName.endsWith("/head")) {

+						// strip pull request head from name 

+						shortRefName = shortRefName.substring(0, shortRefName.length() - "/head".length());

+					}					

+					isPull = true;

 				}

 				boolean isDigest = push instanceof DailyLogEntry;

 				

-				pushItem.add(WicketUtils.createDateLabel("whenPushed", push.date, getTimeZone(), getTimeUtils()));

+				String fuzzydate;

+				TimeUtils tu = getTimeUtils();

+				Date pushDate = push.date;

+				if (TimeUtils.isToday(pushDate, timezone)) {

+					fuzzydate = tu.today();

+				} else if (TimeUtils.isYesterday(pushDate, timezone)) {

+					fuzzydate = tu.yesterday();

+				} else {

+					// calculate a fuzzy time ago date

+                	cal.setTime(pushDate);

+                	cal.set(Calendar.HOUR_OF_DAY, 0);

+                	cal.set(Calendar.MINUTE, 0);

+                	cal.set(Calendar.SECOND, 0);

+                	cal.set(Calendar.MILLISECOND, 0);

+                	pushDate = cal.getTime();

+					fuzzydate = getTimeUtils().timeAgo(pushDate);

+				}

+				pushItem.add(new Label("whenPushed", fuzzydate + ", " + df.format(pushDate)));

+

 				Label pushIcon = new Label("pushIcon");

 				if (showRepo) {

 					// if we are showing the repo, we are showing multiple

@@ -135,6 +173,8 @@
 				}

 				if (isTag) {

 					WicketUtils.setCssClass(pushIcon, "iconic-tag");

+				} else if (isPull) {

+					WicketUtils.setCssClass(pushIcon, "iconic-share");

 				} else if (isDigest) {

 					WicketUtils.setCssClass(pushIcon, "iconic-loop");

 				} else {

@@ -163,6 +203,7 @@
 				switch(push.getChangeType(fullRefName)) {

 				case CREATE:

 					if (isTag) {

+						// new tag

 						if (isDigest) {

 							what = getString("gb.createdNewTag");

 							preposition = "gb.in";

@@ -170,7 +211,12 @@
 							what = getString("gb.pushedNewTag");

 							preposition = "gb.to";

 						}

+					} else if (isPull) {

+						// merged pull request

+						what = getString("gb.mergedPullRequest");

+						preposition = "gb.in";

 					} else {

+						// new branch

 						if (isDigest) {

 							what = getString("gb.createdNewBranch");

 							preposition = "gb.in";

@@ -184,6 +230,8 @@
 					isDelete = true;

 					if (isTag) {

 						what = getString("gb.deletedTag");

+					} if (isPull) {

+						what = getString("gb.deletedTag");

 					} else {

 						what = getString("gb.deletedBranch");

 					}

@@ -217,6 +265,10 @@
 					// link to tag

 					pushItem.add(new LinkPanel("refPushed", null, shortRefName,

 							TagPage.class, WicketUtils.newObjectParameter(push.repository, fullRefName)));

+				} else if (isPull) {

+					// link to pull request

+					pushItem.add(new LinkPanel("refPushed", null, shortRefName,

+							TagPage.class, WicketUtils.newObjectParameter(push.repository, fullRefName)));

 				} else {

 					// link to tree

 					pushItem.add(new LinkPanel("refPushed", null, shortRefName,

diff --git a/src/main/java/com/gitblit/wicket/panels/RefsPanel.java b/src/main/java/com/gitblit/wicket/panels/RefsPanel.java
index dc852e2..7ea3539 100644
--- a/src/main/java/com/gitblit/wicket/panels/RefsPanel.java
+++ b/src/main/java/com/gitblit/wicket/panels/RefsPanel.java
@@ -28,10 +28,10 @@
 import org.apache.wicket.markup.repeater.Item;

 import org.apache.wicket.markup.repeater.data.DataView;

 import org.apache.wicket.markup.repeater.data.ListDataProvider;

-import org.eclipse.jgit.lib.Constants;

 import org.eclipse.jgit.lib.ObjectId;

 import org.eclipse.jgit.revwalk.RevCommit;

 

+import com.gitblit.Constants;

 import com.gitblit.models.RefModel;

 import com.gitblit.wicket.WicketUtils;

 import com.gitblit.wicket.pages.CommitPage;

@@ -42,10 +42,6 @@
 

 	private static final long serialVersionUID = 1L;

 	

-	private static final String R_CHANGES = "refs/changes/";

-	

-	private static final String R_PULL= "refs/pull/";

-

 	public RefsPanel(String id, final String repositoryName, RevCommit c,

 			Map<ObjectId, List<RefModel>> refs) {

 		this(id, repositoryName, refs.get(c.getId()));

@@ -112,17 +108,17 @@
 					// local head

 					linkClass = LogPage.class;

 					cssClass = "headRef";

-				} else if (name.startsWith(R_CHANGES)) {

+				} else if (name.startsWith(Constants.R_CHANGES)) {

 					// Gerrit change ref

-					name = name.substring(R_CHANGES.length());

+					name = name.substring(Constants.R_CHANGES.length());

 					cssClass = "otherRef";

-				} else if (name.startsWith(R_PULL)) {

+				} else if (name.startsWith(Constants.R_PULL)) {

 					// Pull Request ref

-					name = "pull #" + name.substring(R_PULL.length());

+					name = "pull #" + name.substring(Constants.R_PULL.length());

 					if (name.endsWith("/head")) {

 						// strip pull request head from name 

 						name = name.substring(0, name.length() - "/head".length());

-					}

+					}					

 					cssClass = "pullRef";

 				} else if (name.startsWith(Constants.R_REMOTES)) {

 					// remote branch

diff --git a/src/main/java/com/gitblit/wicket/panels/TagsPanel.html b/src/main/java/com/gitblit/wicket/panels/TagsPanel.html
index ba9f15d..7bd6278 100644
--- a/src/main/java/com/gitblit/wicket/panels/TagsPanel.html
+++ b/src/main/java/com/gitblit/wicket/panels/TagsPanel.html
@@ -8,7 +8,7 @@
 <wicket:panel>

 

 	<!-- tags -->

-	<div class="header"><i class="icon-tags" style="vertical-align: middle;"></i> <b><span wicket:id="header">[tags header]</span></b></div>	

+	<div class="header"><i class="icon-tags"></i> <b><span wicket:id="header">[tags header]</span></b></div>	

 	<table class="pretty">

 		<tbody>

     		<tr wicket:id="tag">

diff --git a/src/main/resources/bootstrap/css/iconic.css b/src/main/resources/bootstrap/css/iconic.css
index b4cc544..137606c 100644
--- a/src/main/resources/bootstrap/css/iconic.css
+++ b/src/main/resources/bootstrap/css/iconic.css
@@ -22,15 +22,13 @@
   [class*="iconic-"] {
     font-style: inherit;
     font-weight: normal;
-    vertical-align: bottom;
+    vertical-align: middle;
   }
   [class*="iconic-"]:before {
     display: inline-block;
     width: 1em;
     font-family: IconicFill;
-    font-size: 0.9em;
     text-align: center;
-    vertical-align: middle;
     content: "";
   }
   .iconic-stroke:before {
diff --git a/src/main/resources/gitblit.css b/src/main/resources/gitblit.css
index 526d556..351d43f 100644
--- a/src/main/resources/gitblit.css
+++ b/src/main/resources/gitblit.css
@@ -29,7 +29,12 @@
 	outline: none;

 }

 

-[class^="icon-"], [class*=" icon-"] a i {

+a.btn i {

+	/* override for a links that look like bootstrap buttons */

+	vertical-align: text-bottom;

+}

+

+[class^="icon-"], [class*=" icon-"] i {

 	/* override for a links that look like bootstrap buttons */

 	vertical-align: text-bottom;

 }

@@ -126,11 +131,19 @@
 

 div.push {

     border-bottom: 1px solid #ddd;

-    margin-bottom: 15px;

+	margin-bottom: 5px;

+	padding-bottom: 5px;

 }

+

+div.push .icon {

+    font-size: 42px;

+    line-height: 42px;

+}

+

 div.push i {

-    font-size:3.25em;

-    color:#bbb;

+    font-size: 42px;

+    color: #bbb;

+    vertical-align: middle;

 }

 

 .repositorynavbar {

diff --git a/src/test/java/com/gitblit/tests/TimeUtilsTest.java b/src/test/java/com/gitblit/tests/TimeUtilsTest.java
index f9d5d83..851fb45 100644
--- a/src/test/java/com/gitblit/tests/TimeUtilsTest.java
+++ b/src/test/java/com/gitblit/tests/TimeUtilsTest.java
@@ -43,12 +43,12 @@
 

 	@Test

 	public void testToday() throws Exception {

-		assertTrue(TimeUtils.isToday(new Date()));

+		assertTrue(TimeUtils.isToday(new Date(), null));

 	}

 

 	@Test

 	public void testYesterday() throws Exception {

-		assertTrue(TimeUtils.isYesterday(offset(TimeUtils.ONEDAY)));

+		assertTrue(TimeUtils.isYesterday(offset(TimeUtils.ONEDAY), null));

 	}

 

 	@Test