From 8f0f665b9ee4e2cd21e9e0d5d7cfc69b1d19b86f Mon Sep 17 00:00:00 2001 From: James Moger <james.moger@gitblit.com> Date: Mon, 05 May 2014 12:46:02 -0400 Subject: [PATCH] Merged #23 "Enhance the plugin infrastructure to allow deeper plugin integration" --- src/main/java/com/gitblit/wicket/pages/RootPage.java | 1312 ++++++++++++++++++++++++++++++++--------------------------- 1 files changed, 708 insertions(+), 604 deletions(-) diff --git a/src/main/java/com/gitblit/wicket/pages/RootPage.java b/src/main/java/com/gitblit/wicket/pages/RootPage.java index c59c189..a2f3a49 100644 --- a/src/main/java/com/gitblit/wicket/pages/RootPage.java +++ b/src/main/java/com/gitblit/wicket/pages/RootPage.java @@ -1,604 +1,708 @@ -/* - * 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); - boolean allowLucene = app().settings().getBoolean(Keys.web.allowLuceneIndexing, 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 (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)); - } - - boolean showRegistrations = app().federation().canFederate() - && app().settings().getBoolean(Keys.web.showFederationRegistrations, false); - - // navigation links - List<PageRegistration> pages = new ArrayList<PageRegistration>(); - if (!authenticateView || (authenticateView && isLoggedIn)) { - pages.add(new PageRegistration(isLoggedIn ? "gb.myDashboard" : "gb.dashboard", MyDashboardPage.class, - getRootPageParameters())); - if (isLoggedIn && app().tickets().isReady()) { - pages.add(new PageRegistration("gb.myTickets", MyTicketsPage.class)); - } - pages.add(new PageRegistration("gb.repositories", RepositoriesPage.class, - getRootPageParameters())); - pages.add(new PageRegistration("gb.activity", ActivityPage.class, getRootPageParameters())); - if (allowLucene) { - 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 && 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 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.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.WebResponse; + +import com.gitblit.Constants; +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.SessionlessForm; +import com.gitblit.wicket.WicketUtils; +import com.gitblit.wicket.panels.GravatarImage; +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 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 (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)) { + 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.activity", ActivityPage.class, getRootPageParameters())); + if (allowLucene) { + navLinks.add(new PageNavLink("gb.search", LuceneSearchPage.class)); + } + + if (!authenticateView || (authenticateView && isLoggedIn)) { + addDropDownMenus(navLinks); + } + + UserModel user = UserModel.ANONYMOUS; + if (isLoggedIn) { + user = GitBlitWebSession.get().getUser(); + } + + // 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) { + // 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<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(); + + 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); + } + + @Override + protected void onInitialize() { + super.onInitialize(); + + 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())); + } + + List<MenuItem> standardItems = new ArrayList<MenuItem>(); + standardItems.add(new MenuDivider()); + if (user.canAdmin() || user.canCreate()) { + standardItems.add(new PageLinkMenuItem("gb.newRepository", EditRepositoryPage.class)); + } + 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