From f76fee63ed9cb3a30d3c0c092d860b1cb93a481b Mon Sep 17 00:00:00 2001 From: Gerard Smyth <gerard.smyth@gmail.com> Date: Thu, 08 May 2014 13:09:30 -0400 Subject: [PATCH] Updated the SyndicationServlet to provide an additional option to return details of the tags in the repository instead of the commits. This uses a new 'ot' request parameter to indicate the object type of the content to return, which can be ither TAG or COMMIT. If this is not provided, then COMMIT is assumed to maintain backwards compatability. If tags are returned, then the paging parameters, 'l' and 'pg' are still supported, but searching options are currently ignored. --- src/main/java/com/gitblit/wicket/pages/RootPage.java | 1307 ++++++++++++++++++++++++++++++++--------------------------- 1 files changed, 708 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..a2f3a49 100644 --- a/src/main/java/com/gitblit/wicket/pages/RootPage.java +++ b/src/main/java/com/gitblit/wicket/pages/RootPage.java @@ -1,599 +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); - - 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 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