From a502d96a860456ec5e8c96761db70f7cabb74751 Mon Sep 17 00:00:00 2001
From: Paul Martin <paul@paulsputer.com>
Date: Sat, 30 Apr 2016 04:19:14 -0400
Subject: [PATCH] Merge pull request #1073 from gitblit/1062-DocEditorUpdates

---
 src/main/java/com/gitblit/wicket/pages/RootPage.java | 1326 +++++++++++++++++++++++++++++++--------------------------
 1 files changed, 727 insertions(+), 599 deletions(-)

diff --git a/src/main/java/com/gitblit/wicket/pages/RootPage.java b/src/main/java/com/gitblit/wicket/pages/RootPage.java
index 5ccc3a4..12779ca 100644
--- a/src/main/java/com/gitblit/wicket/pages/RootPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/RootPage.java
@@ -1,599 +1,727 @@
-/*
- * Copyright 2011 gitblit.com.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.gitblit.wicket.pages;
-
-import java.text.MessageFormat;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Calendar;
-import java.util.Collections;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeSet;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.regex.Pattern;
-
-import org.apache.wicket.MarkupContainer;
-import org.apache.wicket.PageParameters;
-import org.apache.wicket.behavior.HeaderContributor;
-import org.apache.wicket.markup.html.IHeaderContributor;
-import org.apache.wicket.markup.html.IHeaderResponse;
-import org.apache.wicket.markup.html.basic.Label;
-import org.apache.wicket.markup.html.form.PasswordTextField;
-import org.apache.wicket.markup.html.form.TextField;
-import org.apache.wicket.markup.html.link.BookmarkablePageLink;
-import org.apache.wicket.markup.html.panel.Fragment;
-import org.apache.wicket.model.IModel;
-import org.apache.wicket.model.Model;
-import org.apache.wicket.protocol.http.WebResponse;
-
-import com.gitblit.Constants;
-import com.gitblit.Keys;
-import com.gitblit.models.RepositoryModel;
-import com.gitblit.models.TeamModel;
-import com.gitblit.models.UserModel;
-import com.gitblit.utils.ModelUtils;
-import com.gitblit.utils.StringUtils;
-import com.gitblit.wicket.GitBlitWebSession;
-import com.gitblit.wicket.PageRegistration;
-import com.gitblit.wicket.PageRegistration.DropDownMenuItem;
-import com.gitblit.wicket.PageRegistration.DropDownToggleItem;
-import com.gitblit.wicket.SessionlessForm;
-import com.gitblit.wicket.WicketUtils;
-import com.gitblit.wicket.panels.GravatarImage;
-import com.gitblit.wicket.panels.NavigationPanel;
-
-/**
- * Root page is a topbar, navigable page like Repositories, Users, or
- * Federation.
- *
- * @author James Moger
- *
- */
-public abstract class RootPage extends BasePage {
-
-	boolean showAdmin;
-
-	IModel<String> username = new Model<String>("");
-	IModel<String> password = new Model<String>("");
-	List<RepositoryModel> repositoryModels = new ArrayList<RepositoryModel>();
-
-	public RootPage() {
-		super();
-	}
-
-	public RootPage(PageParameters params) {
-		super(params);
-	}
-
-	@Override
-	protected void setupPage(String repositoryName, String pageName) {
-
-		// CSS header overrides
-		add(new HeaderContributor(new IHeaderContributor() {
-			private static final long serialVersionUID = 1L;
-
-			@Override
-			public void renderHead(IHeaderResponse response) {
-				StringBuilder buffer = new StringBuilder();
-				buffer.append("<style type=\"text/css\">\n");
-				buffer.append(".navbar-inner {\n");
-				final String headerBackground = app().settings().getString(Keys.web.headerBackgroundColor, null);
-				if (!StringUtils.isEmpty(headerBackground)) {
-					buffer.append(MessageFormat.format("background-color: {0};\n", headerBackground));
-				}
-				final String headerBorder = app().settings().getString(Keys.web.headerBorderColor, null);
-				if (!StringUtils.isEmpty(headerBorder)) {
-					buffer.append(MessageFormat.format("border-bottom: 1px solid {0} !important;\n", headerBorder));
-				}
-				buffer.append("}\n");
-				final String headerBorderFocus = app().settings().getString(Keys.web.headerBorderFocusColor, null);
-				if (!StringUtils.isEmpty(headerBorderFocus)) {
-					buffer.append(".navbar ul li:focus, .navbar .active {\n");
-					buffer.append(MessageFormat.format("border-bottom: 4px solid {0};\n", headerBorderFocus));
-					buffer.append("}\n");
-				}
-				final String headerForeground = app().settings().getString(Keys.web.headerForegroundColor, null);
-				if (!StringUtils.isEmpty(headerForeground)) {
-					buffer.append(".navbar ul.nav li a {\n");
-					buffer.append(MessageFormat.format("color: {0};\n", headerForeground));
-					buffer.append("}\n");
-					buffer.append(".navbar ul.nav .active a {\n");
-					buffer.append(MessageFormat.format("color: {0};\n", headerForeground));
-					buffer.append("}\n");
-				}
-				final String headerHover = app().settings().getString(Keys.web.headerHoverColor, null);
-				if (!StringUtils.isEmpty(headerHover)) {
-					buffer.append(".navbar ul.nav li a:hover {\n");
-					buffer.append(MessageFormat.format("color: {0} !important;\n", headerHover));
-					buffer.append("}\n");
-				}
-				buffer.append("</style>\n");
-				response.renderString(buffer.toString());
-				}
-			}));
-
-		boolean authenticateView = app().settings().getBoolean(Keys.web.authenticateViewPages, false);
-		boolean authenticateAdmin = app().settings().getBoolean(Keys.web.authenticateAdminPages, true);
-		boolean allowAdmin = app().settings().getBoolean(Keys.web.allowAdministration, true);
-
-		if (authenticateAdmin) {
-			showAdmin = allowAdmin && GitBlitWebSession.get().canAdmin();
-			// authentication requires state and session
-			setStatelessHint(false);
-		} else {
-			showAdmin = allowAdmin;
-			if (authenticateView) {
-				// authentication requires state and session
-				setStatelessHint(false);
-			} else {
-				// no authentication required, no state and no session required
-				setStatelessHint(true);
-			}
-		}
-
-		if (authenticateView || authenticateAdmin) {
-			if (GitBlitWebSession.get().isLoggedIn()) {
-				UserMenu userFragment = new UserMenu("userPanel", "userMenuFragment", RootPage.this);
-				add(userFragment);
-			} else {
-				LoginForm loginForm = new LoginForm("userPanel", "loginFormFragment", RootPage.this);
-				add(loginForm);
-			}
-		} else {
-			add(new Label("userPanel").setVisible(false));
-		}
-
-		boolean showRegistrations = app().federation().canFederate()
-				&& app().settings().getBoolean(Keys.web.showFederationRegistrations, false);
-
-		// navigation links
-		List<PageRegistration> pages = new ArrayList<PageRegistration>();
-		if (!authenticateView || (authenticateView && GitBlitWebSession.get().isLoggedIn())) {
-			pages.add(new PageRegistration(GitBlitWebSession.get().isLoggedIn() ? "gb.myDashboard" : "gb.dashboard", MyDashboardPage.class,
-					getRootPageParameters()));
-			pages.add(new PageRegistration("gb.repositories", RepositoriesPage.class,
-					getRootPageParameters()));
-			pages.add(new PageRegistration("gb.activity", ActivityPage.class, getRootPageParameters()));
-			if (app().settings().getBoolean(Keys.web.allowLuceneIndexing, true)) {
-				pages.add(new PageRegistration("gb.search", LuceneSearchPage.class));
-			}
-			if (showAdmin) {
-				pages.add(new PageRegistration("gb.users", UsersPage.class));
-			}
-			if (showAdmin || showRegistrations) {
-				pages.add(new PageRegistration("gb.federation", FederationPage.class));
-			}
-
-			if (!authenticateView || (authenticateView && GitBlitWebSession.get().isLoggedIn())) {
-				addDropDownMenus(pages);
-			}
-		}
-
-		NavigationPanel navPanel = new NavigationPanel("navPanel", getRootNavPageClass(), pages);
-		add(navPanel);
-
-		// display an error message cached from a redirect
-		String cachedMessage = GitBlitWebSession.get().clearErrorMessage();
-		if (!StringUtils.isEmpty(cachedMessage)) {
-			error(cachedMessage);
-		} else if (showAdmin) {
-			int pendingProposals = app().federation().getPendingFederationProposals().size();
-			if (pendingProposals == 1) {
-				info(getString("gb.OneProposalToReview"));
-			} else if (pendingProposals > 1) {
-				info(MessageFormat.format(getString("gb.nFederationProposalsToReview"),
-						pendingProposals));
-			}
-		}
-
-		super.setupPage(repositoryName, pageName);
-	}
-
-	protected Class<? extends BasePage> getRootNavPageClass() {
-		return getClass();
-	}
-
-	private PageParameters getRootPageParameters() {
-		if (reusePageParameters()) {
-			PageParameters pp = getPageParameters();
-			if (pp != null) {
-				PageParameters params = new PageParameters(pp);
-				// remove named project parameter
-				params.remove("p");
-
-				// remove named repository parameter
-				params.remove("r");
-
-				// remove named user parameter
-				params.remove("user");
-
-				// remove days back parameter if it is the default value
-				if (params.containsKey("db")
-						&& params.getInt("db") == app().settings().getInteger(Keys.web.activityDuration, 7)) {
-					params.remove("db");
-				}
-				return params;
-			}
-		}
-		return null;
-	}
-
-	protected boolean reusePageParameters() {
-		return false;
-	}
-
-	private void loginUser(UserModel user) {
-		if (user != null) {
-			// Set the user into the session
-			GitBlitWebSession session = GitBlitWebSession.get();
-			// issue 62: fix session fixation vulnerability
-			session.replaceSession();
-			session.setUser(user);
-
-			// Set Cookie
-			if (app().settings().getBoolean(Keys.web.allowCookieAuthentication, false)) {
-				WebResponse response = (WebResponse) getRequestCycle().getResponse();
-				app().authentication().setCookie(response.getHttpServletResponse(), user);
-			}
-
-			if (!session.continueRequest()) {
-				PageParameters params = getPageParameters();
-				if (params == null) {
-					// redirect to this page
-					setResponsePage(getClass());
-				} else {
-					// Strip username and password and redirect to this page
-					params.remove("username");
-					params.remove("password");
-					setResponsePage(getClass(), params);
-				}
-			}
-		}
-	}
-
-	protected List<RepositoryModel> getRepositoryModels() {
-		if (repositoryModels.isEmpty()) {
-			final UserModel user = GitBlitWebSession.get().getUser();
-			List<RepositoryModel> repositories = app().repositories().getRepositoryModels(user);
-			repositoryModels.addAll(repositories);
-			Collections.sort(repositoryModels);
-		}
-		return repositoryModels;
-	}
-
-	protected void addDropDownMenus(List<PageRegistration> pages) {
-
-	}
-
-	protected List<DropDownMenuItem> getRepositoryFilterItems(PageParameters params) {
-		final UserModel user = GitBlitWebSession.get().getUser();
-		Set<DropDownMenuItem> filters = new LinkedHashSet<DropDownMenuItem>();
-		List<RepositoryModel> repositories = getRepositoryModels();
-
-		// accessible repositories by federation set
-		Map<String, AtomicInteger> setMap = new HashMap<String, AtomicInteger>();
-		for (RepositoryModel repository : repositories) {
-			for (String set : repository.federationSets) {
-				String key = set.toLowerCase();
-				if (setMap.containsKey(key)) {
-					setMap.get(key).incrementAndGet();
-				} else {
-					setMap.put(key, new AtomicInteger(1));
-				}
-			}
-		}
-		if (setMap.size() > 0) {
-			List<String> sets = new ArrayList<String>(setMap.keySet());
-			Collections.sort(sets);
-			for (String set : sets) {
-				filters.add(new DropDownToggleItem(MessageFormat.format("{0} ({1})", set,
-						setMap.get(set).get()), "set", set, params));
-			}
-			// divider
-			filters.add(new DropDownMenuItem());
-		}
-
-		// user's team memberships
-		if (user != null && user.teams.size() > 0) {
-			List<TeamModel> teams = new ArrayList<TeamModel>(user.teams);
-			Collections.sort(teams);
-			for (TeamModel team : teams) {
-				filters.add(new DropDownToggleItem(MessageFormat.format("{0} ({1})", team.name,
-						team.repositories.size()), "team", team.name, params));
-			}
-			// divider
-			filters.add(new DropDownMenuItem());
-		}
-
-		// custom filters
-		String customFilters = app().settings().getString(Keys.web.customFilters, null);
-		if (!StringUtils.isEmpty(customFilters)) {
-			boolean addedExpression = false;
-			List<String> expressions = StringUtils.getStringsFromValue(customFilters, "!!!");
-			for (String expression : expressions) {
-				if (!StringUtils.isEmpty(expression)) {
-					addedExpression = true;
-					filters.add(new DropDownToggleItem(null, "x", expression, params));
-				}
-			}
-			// if we added any custom expressions, add a divider
-			if (addedExpression) {
-				filters.add(new DropDownMenuItem());
-			}
-		}
-		return new ArrayList<DropDownMenuItem>(filters);
-	}
-
-	protected List<DropDownMenuItem> getTimeFilterItems(PageParameters params) {
-		// days back choices - additive parameters
-		int daysBack = app().settings().getInteger(Keys.web.activityDuration, 7);
-		int maxDaysBack = app().settings().getInteger(Keys.web.activityDurationMaximum, 30);
-		if (daysBack < 1) {
-			daysBack = 7;
-		}
-		if (daysBack > maxDaysBack) {
-			daysBack = maxDaysBack;
-		}
-		PageParameters clonedParams;
-		if (params == null) {
-			clonedParams = new PageParameters();
-		} else {
-			clonedParams = new PageParameters(params);
-		}
-
-		if (!clonedParams.containsKey("db")) {
-			clonedParams.put("db",  daysBack);
-		}
-
-		List<DropDownMenuItem> items = new ArrayList<DropDownMenuItem>();
-		Set<Integer> choicesSet = new TreeSet<Integer>(app().settings().getIntegers(Keys.web.activityDurationChoices));
-		if (choicesSet.isEmpty()) {
-			 choicesSet.addAll(Arrays.asList(1, 3, 7, 14, 21, 28));
-		}
-		List<Integer> choices = new ArrayList<Integer>(choicesSet);
-		Collections.sort(choices);
-		String lastDaysPattern = getString("gb.lastNDays");
-		for (Integer db : choices) {
-			if (db == 1) {
-				items.add(new DropDownMenuItem(getString("gb.time.today"), "db", db.toString(), clonedParams));
-			} else {
-				String txt = MessageFormat.format(lastDaysPattern, db);
-				items.add(new DropDownMenuItem(txt, "db", db.toString(), clonedParams));
-			}
-		}
-		items.add(new DropDownMenuItem());
-		return items;
-	}
-
-	protected List<RepositoryModel> getRepositories(PageParameters params) {
-		if (params == null) {
-			return getRepositoryModels();
-		}
-
-		boolean hasParameter = false;
-		String projectName = WicketUtils.getProjectName(params);
-		String userName = WicketUtils.getUsername(params);
-		if (StringUtils.isEmpty(projectName)) {
-			if (!StringUtils.isEmpty(userName)) {
-				projectName = ModelUtils.getPersonalPath(userName);
-			}
-		}
-		String repositoryName = WicketUtils.getRepositoryName(params);
-		String set = WicketUtils.getSet(params);
-		String regex = WicketUtils.getRegEx(params);
-		String team = WicketUtils.getTeam(params);
-		int daysBack = params.getInt("db", 0);
-		int maxDaysBack = app().settings().getInteger(Keys.web.activityDurationMaximum, 30);
-
-		List<RepositoryModel> availableModels = getRepositoryModels();
-		Set<RepositoryModel> models = new HashSet<RepositoryModel>();
-
-		if (!StringUtils.isEmpty(repositoryName)) {
-			// try named repository
-			hasParameter = true;
-			for (RepositoryModel model : availableModels) {
-				if (model.name.equalsIgnoreCase(repositoryName)) {
-					models.add(model);
-					break;
-				}
-			}
-		}
-
-		if (!StringUtils.isEmpty(projectName)) {
-			// try named project
-			hasParameter = true;
-			if (projectName.equalsIgnoreCase(app().settings().getString(Keys.web.repositoryRootGroupName, "main"))) {
-				// root project/group
-				for (RepositoryModel model : availableModels) {
-					if (model.name.indexOf('/') == -1) {
-						models.add(model);
-					}
-				}
-			} else {
-				// named project/group
-				String group = projectName.toLowerCase() + "/";
-				for (RepositoryModel model : availableModels) {
-					if (model.name.toLowerCase().startsWith(group)) {
-						models.add(model);
-					}
-				}
-			}
-		}
-
-		if (!StringUtils.isEmpty(regex)) {
-			// filter the repositories by the regex
-			hasParameter = true;
-			Pattern pattern = Pattern.compile(regex);
-			for (RepositoryModel model : availableModels) {
-				if (pattern.matcher(model.name).find()) {
-					models.add(model);
-				}
-			}
-		}
-
-		if (!StringUtils.isEmpty(set)) {
-			// filter the repositories by the specified sets
-			hasParameter = true;
-			List<String> sets = StringUtils.getStringsFromValue(set, ",");
-			for (RepositoryModel model : availableModels) {
-				for (String curr : sets) {
-					if (model.federationSets.contains(curr)) {
-						models.add(model);
-					}
-				}
-			}
-		}
-
-		if (!StringUtils.isEmpty(team)) {
-			// filter the repositories by the specified teams
-			hasParameter = true;
-			List<String> teams = StringUtils.getStringsFromValue(team, ",");
-
-			// need TeamModels first
-			List<TeamModel> teamModels = new ArrayList<TeamModel>();
-			for (String name : teams) {
-				TeamModel teamModel = app().users().getTeamModel(name);
-				if (teamModel != null) {
-					teamModels.add(teamModel);
-				}
-			}
-
-			// brute-force our way through finding the matching models
-			for (RepositoryModel repositoryModel : availableModels) {
-				for (TeamModel teamModel : teamModels) {
-					if (teamModel.hasRepositoryPermission(repositoryModel.name)) {
-						models.add(repositoryModel);
-					}
-				}
-			}
-		}
-
-		if (!hasParameter) {
-			models.addAll(availableModels);
-		}
-
-		// time-filter the list
-		if (daysBack > 0) {
-			if (maxDaysBack > 0 && daysBack > maxDaysBack) {
-				daysBack = maxDaysBack;
-			}
-			Calendar cal = Calendar.getInstance();
-			cal.set(Calendar.HOUR_OF_DAY, 0);
-			cal.set(Calendar.MINUTE, 0);
-			cal.set(Calendar.SECOND, 0);
-			cal.set(Calendar.MILLISECOND, 0);
-			cal.add(Calendar.DATE, -1 * daysBack);
-			Date threshold = cal.getTime();
-			Set<RepositoryModel> timeFiltered = new HashSet<RepositoryModel>();
-			for (RepositoryModel model : models) {
-				if (model.lastChange.after(threshold)) {
-					timeFiltered.add(model);
-				}
-			}
-			models = timeFiltered;
-		}
-
-		List<RepositoryModel> list = new ArrayList<RepositoryModel>(models);
-		Collections.sort(list);
-		return list;
-	}
-
-	/**
-	 * Inline login form.
-	 */
-	private class LoginForm extends Fragment {
-		private static final long serialVersionUID = 1L;
-
-		public LoginForm(String id, String markupId, MarkupContainer markupProvider) {
-			super(id, markupId, markupProvider);
-			setRenderBodyOnly(true);
-
-			SessionlessForm<Void> loginForm = new SessionlessForm<Void>("loginForm", RootPage.this.getClass(), getPageParameters()) {
-
-				private static final long serialVersionUID = 1L;
-
-				@Override
-				public void onSubmit() {
-					String username = RootPage.this.username.getObject();
-					char[] password = RootPage.this.password.getObject().toCharArray();
-
-					UserModel user = app().authentication().authenticate(username, password);
-					if (user == null) {
-						error(getString("gb.invalidUsernameOrPassword"));
-					} else if (user.username.equals(Constants.FEDERATION_USER)) {
-						// disallow the federation user from logging in via the
-						// web ui
-						error(getString("gb.invalidUsernameOrPassword"));
-						user = null;
-					} else {
-						loginUser(user);
-					}
-				}
-			};
-			TextField<String> unameField = new TextField<String>("username", username);
-			WicketUtils.setInputPlaceholder(unameField, markupProvider.getString("gb.username"));
-			loginForm.add(unameField);
-			PasswordTextField pwField = new PasswordTextField("password", password);
-			WicketUtils.setInputPlaceholder(pwField, markupProvider.getString("gb.password"));
-			loginForm.add(pwField);
-			add(loginForm);
-		}
-	}
-
-	/**
-	 * Menu for the authenticated user.
-	 */
-	class UserMenu extends Fragment {
-
-		private static final long serialVersionUID = 1L;
-
-		public UserMenu(String id, String markupId, MarkupContainer markupProvider) {
-			super(id, markupId, markupProvider);
-			setRenderBodyOnly(true);
-
-			GitBlitWebSession session = GitBlitWebSession.get();
-			UserModel user = session.getUser();
-			boolean editCredentials = app().authentication().supportsCredentialChanges(user);
-			boolean standardLogin = session.authenticationType.isStandard();
-
-			if (app().settings().getBoolean(Keys.web.allowGravatar, true)) {
-				add(new GravatarImage("username", user, "navbarGravatar", 20, false));
-			} else {
-				add(new Label("username", user.getDisplayName()));
-			}
-
-			add(new Label("displayName", user.getDisplayName()));
-
-			add(new BookmarkablePageLink<Void>("newRepository",
-					EditRepositoryPage.class).setVisible(user.canAdmin() || user.canCreate()));
-
-			add(new BookmarkablePageLink<Void>("myProfile",
-					UserPage.class, WicketUtils.newUsernameParameter(user.username)));
-
-			add(new BookmarkablePageLink<Void>("changePassword",
-					ChangePasswordPage.class).setVisible(editCredentials));
-
-			add(new BookmarkablePageLink<Void>("logout",
-					LogoutPage.class).setVisible(standardLogin));
-		}
-	}
-}
+/*
+ * Copyright 2011 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.wicket.pages;
+
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.regex.Pattern;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.wicket.MarkupContainer;
+import org.apache.wicket.PageParameters;
+import org.apache.wicket.RequestCycle;
+import org.apache.wicket.behavior.HeaderContributor;
+import org.apache.wicket.markup.html.IHeaderContributor;
+import org.apache.wicket.markup.html.IHeaderResponse;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.form.TextField;
+import org.apache.wicket.markup.html.link.BookmarkablePageLink;
+import org.apache.wicket.markup.html.panel.Fragment;
+import org.apache.wicket.markup.repeater.Item;
+import org.apache.wicket.markup.repeater.data.DataView;
+import org.apache.wicket.markup.repeater.data.ListDataProvider;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.protocol.http.WebRequest;
+import org.apache.wicket.protocol.http.WebResponse;
+
+import com.gitblit.Constants;
+import com.gitblit.Constants.AuthenticationType;
+import com.gitblit.Keys;
+import com.gitblit.extensions.NavLinkExtension;
+import com.gitblit.extensions.UserMenuExtension;
+import com.gitblit.models.Menu.ExternalLinkMenuItem;
+import com.gitblit.models.Menu.MenuDivider;
+import com.gitblit.models.Menu.MenuItem;
+import com.gitblit.models.Menu.PageLinkMenuItem;
+import com.gitblit.models.Menu.ParameterMenuItem;
+import com.gitblit.models.Menu.ToggleMenuItem;
+import com.gitblit.models.NavLink;
+import com.gitblit.models.NavLink.PageNavLink;
+import com.gitblit.models.RepositoryModel;
+import com.gitblit.models.TeamModel;
+import com.gitblit.models.UserModel;
+import com.gitblit.utils.ModelUtils;
+import com.gitblit.utils.StringUtils;
+import com.gitblit.wicket.GitBlitWebSession;
+import com.gitblit.wicket.NonTrimmedPasswordTextField;
+import com.gitblit.wicket.SessionlessForm;
+import com.gitblit.wicket.WicketUtils;
+import com.gitblit.wicket.panels.AvatarImage;
+import com.gitblit.wicket.panels.LinkPanel;
+import com.gitblit.wicket.panels.NavigationPanel;
+
+/**
+ * Root page is a topbar, navigable page like Repositories, Users, or
+ * Federation.
+ *
+ * @author James Moger
+ *
+ */
+public abstract class RootPage extends BasePage {
+
+	boolean showAdmin;
+
+	IModel<String> username = new Model<String>("");
+	IModel<String> password = new Model<String>("");
+	List<RepositoryModel> repositoryModels = new ArrayList<RepositoryModel>();
+
+	public RootPage() {
+		super();
+	}
+
+	public RootPage(PageParameters params) {
+		super(params);
+	}
+
+	@Override
+	protected void setupPage(String repositoryName, String pageName) {
+
+		// CSS header overrides
+		add(new HeaderContributor(new IHeaderContributor() {
+			private static final long serialVersionUID = 1L;
+
+			@Override
+			public void renderHead(IHeaderResponse response) {
+				StringBuilder buffer = new StringBuilder();
+				buffer.append("<style type=\"text/css\">\n");
+				buffer.append(".navbar-inner {\n");
+				final String headerBackground = app().settings().getString(Keys.web.headerBackgroundColor, null);
+				if (!StringUtils.isEmpty(headerBackground)) {
+					buffer.append(MessageFormat.format("background-color: {0};\n", headerBackground));
+				}
+				final String headerBorder = app().settings().getString(Keys.web.headerBorderColor, null);
+				if (!StringUtils.isEmpty(headerBorder)) {
+					buffer.append(MessageFormat.format("border-bottom: 1px solid {0} !important;\n", headerBorder));
+				}
+				buffer.append("}\n");
+				final String headerBorderFocus = app().settings().getString(Keys.web.headerBorderFocusColor, null);
+				if (!StringUtils.isEmpty(headerBorderFocus)) {
+					buffer.append(".navbar ul li:focus, .navbar .active {\n");
+					buffer.append(MessageFormat.format("border-bottom: 4px solid {0};\n", headerBorderFocus));
+					buffer.append("}\n");
+				}
+				final String headerForeground = app().settings().getString(Keys.web.headerForegroundColor, null);
+				if (!StringUtils.isEmpty(headerForeground)) {
+					buffer.append(".navbar ul.nav li a {\n");
+					buffer.append(MessageFormat.format("color: {0};\n", headerForeground));
+					buffer.append("}\n");
+					buffer.append(".navbar ul.nav .active a {\n");
+					buffer.append(MessageFormat.format("color: {0};\n", headerForeground));
+					buffer.append("}\n");
+				}
+				final String headerHover = app().settings().getString(Keys.web.headerHoverColor, null);
+				if (!StringUtils.isEmpty(headerHover)) {
+					buffer.append(".navbar ul.nav li a:hover {\n");
+					buffer.append(MessageFormat.format("color: {0} !important;\n", headerHover));
+					buffer.append("}\n");
+				}
+				buffer.append("</style>\n");
+				response.renderString(buffer.toString());
+				}
+			}));
+
+		boolean authenticateView = app().settings().getBoolean(Keys.web.authenticateViewPages, false);
+		boolean authenticateAdmin = app().settings().getBoolean(Keys.web.authenticateAdminPages, true);
+		boolean allowAdmin = app().settings().getBoolean(Keys.web.allowAdministration, true);
+		boolean allowLucene = app().settings().getBoolean(Keys.web.allowLuceneIndexing, true);
+		boolean displayUserPanel = app().settings().getBoolean(Keys.web.displayUserPanel, true);
+		boolean isLoggedIn = GitBlitWebSession.get().isLoggedIn();
+
+		if (authenticateAdmin) {
+			showAdmin = allowAdmin && GitBlitWebSession.get().canAdmin();
+			// authentication requires state and session
+			setStatelessHint(false);
+		} else {
+			showAdmin = allowAdmin;
+			if (authenticateView) {
+				// authentication requires state and session
+				setStatelessHint(false);
+			} else {
+				// no authentication required, no state and no session required
+				setStatelessHint(true);
+			}
+		}
+
+		if (displayUserPanel && (authenticateView || authenticateAdmin)) {
+			if (isLoggedIn) {
+				UserMenu userFragment = new UserMenu("userPanel", "userMenuFragment", RootPage.this);
+				add(userFragment);
+			} else {
+				LoginForm loginForm = new LoginForm("userPanel", "loginFormFragment", RootPage.this);
+				add(loginForm);
+			}
+		} else {
+			add(new Label("userPanel").setVisible(false));
+		}
+
+		// navigation links
+		List<NavLink> navLinks = new ArrayList<NavLink>();
+		if (!authenticateView || (authenticateView && isLoggedIn)) {
+			UserModel user = UserModel.ANONYMOUS;
+			if (isLoggedIn) {
+				user = GitBlitWebSession.get().getUser();
+			}
+
+			navLinks.add(new PageNavLink(isLoggedIn ? "gb.myDashboard" : "gb.dashboard", MyDashboardPage.class,
+					getRootPageParameters()));
+			if (isLoggedIn && app().tickets().isReady()) {
+				navLinks.add(new PageNavLink("gb.myTickets", MyTicketsPage.class));
+			}
+			navLinks.add(new PageNavLink("gb.repositories", RepositoriesPage.class,
+					getRootPageParameters()));
+			
+			navLinks.add(new PageNavLink("gb.filestore", FilestorePage.class, getRootPageParameters()));
+				
+			navLinks.add(new PageNavLink("gb.activity", ActivityPage.class, getRootPageParameters()));
+			if (allowLucene) {
+				navLinks.add(new PageNavLink("gb.search", LuceneSearchPage.class));
+			}
+
+			if (!authenticateView || (authenticateView && isLoggedIn)) {
+				addDropDownMenus(navLinks);
+			}
+
+			// add nav link extensions
+			List<NavLinkExtension> extensions = app().plugins().getExtensions(NavLinkExtension.class);
+			for (NavLinkExtension ext : extensions) {
+				navLinks.addAll(ext.getNavLinks(user));
+			}
+		}
+
+		NavigationPanel navPanel = new NavigationPanel("navPanel", getRootNavPageClass(), navLinks);
+		add(navPanel);
+
+		// display an error message cached from a redirect
+		String cachedMessage = GitBlitWebSession.get().clearErrorMessage();
+		if (!StringUtils.isEmpty(cachedMessage)) {
+			error(cachedMessage);
+		} else if (showAdmin) {
+			int pendingProposals = app().federation().getPendingFederationProposals().size();
+			if (pendingProposals == 1) {
+				info(getString("gb.OneProposalToReview"));
+			} else if (pendingProposals > 1) {
+				info(MessageFormat.format(getString("gb.nFederationProposalsToReview"),
+						pendingProposals));
+			}
+		}
+
+		super.setupPage(repositoryName, pageName);
+	}
+
+	protected Class<? extends BasePage> getRootNavPageClass() {
+		return getClass();
+	}
+
+	private PageParameters getRootPageParameters() {
+		if (reusePageParameters()) {
+			PageParameters pp = getPageParameters();
+			if (pp != null) {
+				PageParameters params = new PageParameters(pp);
+				// remove named project parameter
+				params.remove("p");
+
+				// remove named repository parameter
+				params.remove("r");
+
+				// remove named user parameter
+				params.remove("user");
+
+				// remove days back parameter if it is the default value
+				if (params.containsKey("db")
+						&& params.getInt("db") == app().settings().getInteger(Keys.web.activityDuration, 7)) {
+					params.remove("db");
+				}
+				return params;
+			}
+		}
+		return null;
+	}
+
+	protected boolean reusePageParameters() {
+		return false;
+	}
+
+	private void loginUser(UserModel user) {
+		if (user != null) {
+			HttpServletRequest request = ((WebRequest) getRequest()).getHttpServletRequest();
+			HttpServletResponse response = ((WebResponse) getResponse()).getHttpServletResponse();
+
+			// Set the user into the session
+			GitBlitWebSession session = GitBlitWebSession.get();
+
+			// issue 62: fix session fixation vulnerability
+			session.replaceSession();
+			session.setUser(user);
+
+			request = ((WebRequest) getRequest()).getHttpServletRequest();
+			response = ((WebResponse) getResponse()).getHttpServletResponse();
+			request.getSession().setAttribute(Constants.ATTRIB_AUTHTYPE, AuthenticationType.CREDENTIALS);
+
+			// Set Cookie
+			app().authentication().setCookie(request, response, user);
+
+			if (!session.continueRequest()) {
+				PageParameters params = getPageParameters();
+				if (params == null) {
+					// redirect to this page
+					redirectTo(getClass());
+				} else {
+					// Strip username and password and redirect to this page
+					params.remove("username");
+					params.remove("password");
+					redirectTo(getClass(), params);
+				}
+			}
+		}
+	}
+
+	protected List<RepositoryModel> getRepositoryModels() {
+		if (repositoryModels.isEmpty()) {
+			final UserModel user = GitBlitWebSession.get().getUser();
+			List<RepositoryModel> repositories = app().repositories().getRepositoryModels(user);
+			repositoryModels.addAll(repositories);
+			Collections.sort(repositoryModels);
+		}
+		return repositoryModels;
+	}
+
+	protected void addDropDownMenus(List<NavLink> navLinks) {
+
+	}
+
+	protected List<com.gitblit.models.Menu.MenuItem> getRepositoryFilterItems(PageParameters params) {
+		final UserModel user = GitBlitWebSession.get().getUser();
+		Set<MenuItem> filters = new LinkedHashSet<MenuItem>();
+		List<RepositoryModel> repositories = getRepositoryModels();
+
+		// accessible repositories by federation set
+		Map<String, AtomicInteger> setMap = new HashMap<String, AtomicInteger>();
+		for (RepositoryModel repository : repositories) {
+			for (String set : repository.federationSets) {
+				String key = set.toLowerCase();
+				if (setMap.containsKey(key)) {
+					setMap.get(key).incrementAndGet();
+				} else {
+					setMap.put(key, new AtomicInteger(1));
+				}
+			}
+		}
+		if (setMap.size() > 0) {
+			List<String> sets = new ArrayList<String>(setMap.keySet());
+			Collections.sort(sets);
+			for (String set : sets) {
+				filters.add(new ToggleMenuItem(MessageFormat.format("{0} ({1})", set,
+						setMap.get(set).get()), "set", set, params));
+			}
+			// divider
+			filters.add(new MenuDivider());
+		}
+
+		// user's team memberships
+		if (user != null && user.teams.size() > 0) {
+			List<TeamModel> teams = new ArrayList<TeamModel>(user.teams);
+			Collections.sort(teams);
+			for (TeamModel team : teams) {
+				filters.add(new ToggleMenuItem(MessageFormat.format("{0} ({1})", team.name,
+						team.repositories.size()), "team", team.name, params));
+			}
+			// divider
+			filters.add(new MenuDivider());
+		}
+
+		// custom filters
+		String customFilters = app().settings().getString(Keys.web.customFilters, null);
+		if (!StringUtils.isEmpty(customFilters)) {
+			boolean addedExpression = false;
+			List<String> expressions = StringUtils.getStringsFromValue(customFilters, "!!!");
+			for (String expression : expressions) {
+				if (!StringUtils.isEmpty(expression)) {
+					addedExpression = true;
+					filters.add(new ToggleMenuItem(null, "x", expression, params));
+				}
+			}
+			// if we added any custom expressions, add a divider
+			if (addedExpression) {
+				filters.add(new MenuDivider());
+			}
+		}
+		return new ArrayList<MenuItem>(filters);
+	}
+
+	protected List<MenuItem> getTimeFilterItems(PageParameters params) {
+		// days back choices - additive parameters
+		int daysBack = app().settings().getInteger(Keys.web.activityDuration, 7);
+		int maxDaysBack = app().settings().getInteger(Keys.web.activityDurationMaximum, 30);
+		if (daysBack < 1) {
+			daysBack = 7;
+		}
+		if (daysBack > maxDaysBack) {
+			daysBack = maxDaysBack;
+		}
+		PageParameters clonedParams;
+		if (params == null) {
+			clonedParams = new PageParameters();
+		} else {
+			clonedParams = new PageParameters(params);
+		}
+
+		if (!clonedParams.containsKey("db")) {
+			clonedParams.put("db",  daysBack);
+		}
+
+		List<MenuItem> items = new ArrayList<MenuItem>();
+		Set<Integer> choicesSet = new TreeSet<Integer>(app().settings().getIntegers(Keys.web.activityDurationChoices));
+		if (choicesSet.isEmpty()) {
+			 choicesSet.addAll(Arrays.asList(1, 3, 7, 14, 21, 28));
+		}
+		List<Integer> choices = new ArrayList<Integer>(choicesSet);
+		Collections.sort(choices);
+		String lastDaysPattern = getString("gb.lastNDays");
+		for (Integer db : choices) {
+			if (db == 1) {
+				items.add(new ParameterMenuItem(getString("gb.time.today"), "db", db.toString(), clonedParams));
+			} else {
+				String txt = MessageFormat.format(lastDaysPattern, db);
+				items.add(new ParameterMenuItem(txt, "db", db.toString(), clonedParams));
+			}
+		}
+		items.add(new MenuDivider());
+		return items;
+	}
+
+	protected List<RepositoryModel> getRepositories(PageParameters params) {
+		if (params == null) {
+			return getRepositoryModels();
+		}
+
+		boolean hasParameter = false;
+		String projectName = WicketUtils.getProjectName(params);
+		String userName = WicketUtils.getUsername(params);
+		if (StringUtils.isEmpty(projectName)) {
+			if (!StringUtils.isEmpty(userName)) {
+				projectName = ModelUtils.getPersonalPath(userName);
+			}
+		}
+		String repositoryName = WicketUtils.getRepositoryName(params);
+		String set = WicketUtils.getSet(params);
+		String regex = WicketUtils.getRegEx(params);
+		String team = WicketUtils.getTeam(params);
+		int daysBack = params.getInt("db", 0);
+		int maxDaysBack = app().settings().getInteger(Keys.web.activityDurationMaximum, 30);
+
+		List<RepositoryModel> availableModels = getRepositoryModels();
+		Set<RepositoryModel> models = new HashSet<RepositoryModel>();
+
+		if (!StringUtils.isEmpty(repositoryName)) {
+			// try named repository
+			hasParameter = true;
+			for (RepositoryModel model : availableModels) {
+				if (model.name.equalsIgnoreCase(repositoryName)) {
+					models.add(model);
+					break;
+				}
+			}
+		}
+
+		if (!StringUtils.isEmpty(projectName)) {
+			// try named project
+			hasParameter = true;
+			if (projectName.equalsIgnoreCase(app().settings().getString(Keys.web.repositoryRootGroupName, "main"))) {
+				// root project/group
+				for (RepositoryModel model : availableModels) {
+					if (model.name.indexOf('/') == -1) {
+						models.add(model);
+					}
+				}
+			} else {
+				// named project/group
+				String group = projectName.toLowerCase() + "/";
+				for (RepositoryModel model : availableModels) {
+					if (model.name.toLowerCase().startsWith(group)) {
+						models.add(model);
+					}
+				}
+			}
+		}
+
+		if (!StringUtils.isEmpty(regex)) {
+			// filter the repositories by the regex
+			hasParameter = true;
+			Pattern pattern = Pattern.compile(regex);
+			for (RepositoryModel model : availableModels) {
+				if (pattern.matcher(model.name).find()) {
+					models.add(model);
+				}
+			}
+		}
+
+		if (!StringUtils.isEmpty(set)) {
+			// filter the repositories by the specified sets
+			hasParameter = true;
+			List<String> sets = StringUtils.getStringsFromValue(set, ",");
+			for (RepositoryModel model : availableModels) {
+				for (String curr : sets) {
+					if (model.federationSets.contains(curr)) {
+						models.add(model);
+					}
+				}
+			}
+		}
+
+		if (!StringUtils.isEmpty(team)) {
+			// filter the repositories by the specified teams
+			hasParameter = true;
+			List<String> teams = StringUtils.getStringsFromValue(team, ",");
+
+			// need TeamModels first
+			List<TeamModel> teamModels = new ArrayList<TeamModel>();
+			for (String name : teams) {
+				TeamModel teamModel = app().users().getTeamModel(name);
+				if (teamModel != null) {
+					teamModels.add(teamModel);
+				}
+			}
+
+			// brute-force our way through finding the matching models
+			for (RepositoryModel repositoryModel : availableModels) {
+				for (TeamModel teamModel : teamModels) {
+					if (teamModel.hasRepositoryPermission(repositoryModel.name)) {
+						models.add(repositoryModel);
+					}
+				}
+			}
+		}
+
+		if (!hasParameter) {
+			models.addAll(availableModels);
+		}
+
+		// time-filter the list
+		if (daysBack > 0) {
+			if (maxDaysBack > 0 && daysBack > maxDaysBack) {
+				daysBack = maxDaysBack;
+			}
+			Calendar cal = Calendar.getInstance();
+			cal.set(Calendar.HOUR_OF_DAY, 0);
+			cal.set(Calendar.MINUTE, 0);
+			cal.set(Calendar.SECOND, 0);
+			cal.set(Calendar.MILLISECOND, 0);
+			cal.add(Calendar.DATE, -1 * daysBack);
+			Date threshold = cal.getTime();
+			Set<RepositoryModel> timeFiltered = new HashSet<RepositoryModel>();
+			for (RepositoryModel model : models) {
+				if (model.lastChange.after(threshold)) {
+					timeFiltered.add(model);
+				}
+			}
+			models = timeFiltered;
+		}
+
+		List<RepositoryModel> list = new ArrayList<RepositoryModel>(models);
+		Collections.sort(list);
+		return list;
+	}
+
+	/**
+	 * Inline login form.
+	 */
+	private class LoginForm extends Fragment {
+		private static final long serialVersionUID = 1L;
+
+		public LoginForm(String id, String markupId, MarkupContainer markupProvider) {
+			super(id, markupId, markupProvider);
+			setRenderBodyOnly(true);
+
+			SessionlessForm<Void> loginForm = new SessionlessForm<Void>("loginForm", RootPage.this.getClass(), getPageParameters()) {
+
+				private static final long serialVersionUID = 1L;
+
+				@Override
+				public void onSubmit() {
+					String username = RootPage.this.username.getObject();
+					char[] password = RootPage.this.password.getObject().toCharArray();
+
+					HttpServletRequest request = ((WebRequest)RequestCycle.get().getRequest()).getHttpServletRequest();
+
+					UserModel user = app().authentication().authenticate(username, password, request.getRemoteAddr());
+					if (user == null) {
+						error(getString("gb.invalidUsernameOrPassword"));
+					} else if (user.username.equals(Constants.FEDERATION_USER)) {
+						// disallow the federation user from logging in via the
+						// web ui
+						error(getString("gb.invalidUsernameOrPassword"));
+						user = null;
+					} else {
+						loginUser(user);
+					}
+				}
+			};
+			TextField<String> unameField = new TextField<String>("username", username);
+			WicketUtils.setInputPlaceholder(unameField, markupProvider.getString("gb.username"));
+			loginForm.add(unameField);
+			NonTrimmedPasswordTextField pwField = new NonTrimmedPasswordTextField("password", password);
+			WicketUtils.setInputPlaceholder(pwField, markupProvider.getString("gb.password"));
+			loginForm.add(pwField);
+			add(loginForm);
+		}
+	}
+
+	/**
+	 * Menu for the authenticated user.
+	 */
+	class UserMenu extends Fragment {
+
+		private static final long serialVersionUID = 1L;
+
+		public UserMenu(String id, String markupId, MarkupContainer markupProvider) {
+			super(id, markupId, markupProvider);
+			setRenderBodyOnly(true);
+		}
+
+		@Override
+		protected void onInitialize() {
+			super.onInitialize();
+
+			GitBlitWebSession session = GitBlitWebSession.get();
+			UserModel user = session.getUser();
+			boolean editCredentials = app().authentication().supportsCredentialChanges(user);
+			HttpServletRequest request = ((WebRequest) getRequest()).getHttpServletRequest();
+			AuthenticationType authenticationType = (AuthenticationType) request.getAttribute(Constants.ATTRIB_AUTHTYPE);
+			boolean standardLogin = (null != authenticationType) ? authenticationType.isStandard() : true;
+
+			if (app().settings().getBoolean(Keys.web.allowGravatar, true)) {
+				add(new AvatarImage("username", user, "navbarGravatar", 20, false));
+			} else {
+				add(new Label("username", user.getDisplayName()));
+			}
+
+			List<MenuItem> standardItems = new ArrayList<MenuItem>();
+			standardItems.add(new MenuDivider());
+			if (user.canAdmin() || user.canCreate()) {
+				standardItems.add(new PageLinkMenuItem("gb.newRepository", app().getNewRepositoryPage()));
+			}
+			standardItems.add(new PageLinkMenuItem("gb.myProfile", UserPage.class,
+					WicketUtils.newUsernameParameter(user.username)));
+			if (editCredentials) {
+				standardItems.add(new PageLinkMenuItem("gb.changePassword", ChangePasswordPage.class));
+			}
+			standardItems.add(new MenuDivider());
+			add(newSubmenu("standardMenu", user.getDisplayName(), standardItems));
+
+			if (showAdmin) {
+				// admin menu
+				List<MenuItem> adminItems = new ArrayList<MenuItem>();
+				adminItems.add(new MenuDivider());
+				adminItems.add(new PageLinkMenuItem("gb.users", UsersPage.class));
+				adminItems.add(new PageLinkMenuItem("gb.teams", TeamsPage.class));
+
+				boolean showRegistrations = app().federation().canFederate()
+						&& app().settings().getBoolean(Keys.web.showFederationRegistrations, false);
+				if (showRegistrations) {
+					adminItems.add(new PageLinkMenuItem("gb.federation", FederationPage.class));
+				}
+				adminItems.add(new MenuDivider());
+
+				add(newSubmenu("adminMenu", getString("gb.administration"), adminItems));
+			} else {
+				add(new Label("adminMenu").setVisible(false));
+			}
+
+			// plugin extension items
+			List<MenuItem> extensionItems = new ArrayList<MenuItem>();
+			List<UserMenuExtension> extensions = app().plugins().getExtensions(UserMenuExtension.class);
+			for (UserMenuExtension ext : extensions) {
+				List<MenuItem> items = ext.getMenuItems(user);
+				extensionItems.addAll(items);
+			}
+
+			if (extensionItems.isEmpty()) {
+				// no extension items
+				add(new Label("extensionsMenu").setVisible(false));
+			} else {
+				// found extension items
+				extensionItems.add(0, new MenuDivider());
+				add(newSubmenu("extensionsMenu", getString("gb.extensions"), extensionItems));
+				extensionItems.add(new MenuDivider());
+			}
+
+			add(new BookmarkablePageLink<Void>("logout",
+					LogoutPage.class).setVisible(standardLogin));
+		}
+
+		/**
+		 * Creates a submenu.  This is not actually submenu because we're using
+		 * an older Twitter Bootstrap which is pre-submenu.
+		 *
+		 * @param wicketId
+		 * @param submenuTitle
+		 * @param menuItems
+		 * @return a submenu fragment
+		 */
+		private Fragment newSubmenu(String wicketId, String submenuTitle, List<MenuItem> menuItems) {
+			Fragment submenu = new Fragment(wicketId, "submenuFragment", this);
+			submenu.add(new Label("submenuTitle", submenuTitle).setRenderBodyOnly(true));
+			ListDataProvider<MenuItem> menuItemsDp = new ListDataProvider<MenuItem>(menuItems);
+			DataView<MenuItem> submenuItems = new DataView<MenuItem>("submenuItem", menuItemsDp) {
+				private static final long serialVersionUID = 1L;
+
+				@Override
+				public void populateItem(final Item<MenuItem> menuItem) {
+					final MenuItem item = menuItem.getModelObject();
+					String name = item.toString();
+					try {
+						// try to lookup translation
+						name = getString(name);
+					} catch (Exception e) {
+					}
+					if (item instanceof PageLinkMenuItem) {
+						// link to another Wicket page
+						PageLinkMenuItem pageLink = (PageLinkMenuItem) item;
+						menuItem.add(new LinkPanel("submenuLink", null, null, name, pageLink.getPageClass(),
+								pageLink.getPageParameters(), false).setRenderBodyOnly(true));
+					} else if (item instanceof ExternalLinkMenuItem) {
+						// link to a specified href
+						ExternalLinkMenuItem extLink = (ExternalLinkMenuItem) item;
+						menuItem.add(new LinkPanel("submenuLink", null, name, extLink.getHref(),
+								extLink.openInNewWindow()).setRenderBodyOnly(true));
+					} else if (item instanceof MenuDivider) {
+						// divider
+						menuItem.add(new Label("submenuLink").setRenderBodyOnly(true));
+						WicketUtils.setCssClass(menuItem, "divider");
+					}
+				}
+			};
+			submenu.add(submenuItems);
+			submenu.setRenderBodyOnly(true);
+			return submenu;
+		}
+	}
+}

--
Gitblit v1.9.1