@@ -194,6 +194,15 @@ web.syndicationEntries = 25 | |||
# SINCE 0.5.2 | |||
web.showRepositorySizes = true | |||
# List of custom regex expressions that can be displayed in the Filters menu | |||
# of the Repositories and Activity pages. Keep them very simple because you | |||
# are likely to run into encoding issues if they are too complex. | |||
# | |||
# Use !!! to separate the filters | |||
# | |||
# SINCE 0.8.0 | |||
web.customFilters = | |||
# Show federation registrations (without token) and the current pull status | |||
# to non-administrator users. | |||
# |
@@ -26,16 +26,12 @@ The original `users.properties` file and it's corresponding implementation are * | |||
**New:** *web.activityDuration = 14* | |||
**New:** *web.timeFormat = HH:mm* | |||
**New:** *web.datestampLongFormat = EEEE, MMMM d, yyyy* | |||
- added: filters menu to repositories page and activity page. You can filter by federation set, team, and simple custom regular expressions (issue 27) | |||
**New:** *web.customFilters=* | |||
- fixed: several a bugs in FileUserService related to cleaning up old repository permissions on a rename or delete | |||
- added: optional flash-based 1-step *copy to clipboard* of the primary repository url | |||
- added: javascript-based 3-step (click, ctrl+c, enter) *copy to clipboard* of the primary repository url | |||
**New:** *web.allowFlashCopyToClipboard = true* | |||
- added: Name, regex, set, and team filtering of the repositories list and the activity page via url parameters (issue 27) | |||
Here are some url examples: | |||
localhost/gb?r=myrepo.git *(specific repository)* | |||
localhost/gb?x=my *(regex matching. encoding may be an issue. YMMV)* | |||
localhost/gb?set=animal,mineral *(animal and mineral federation sets)* | |||
localhost/qb/activity?team=qa,qa2 *(qa and qa2 teams)* | |||
- improved: empty repositories now link to a new *empty repository* page which gives some direction to the user for the next step in using Gitblit. This page displays the primary push/clone url of the repository and gives sample syntax for the git command-line client. (issue 31) | |||
- improved: unit testing framework has been migrated to JUnit4 syntax and the test suite has been redesigned to run all unit tests, including rpc, federation, and git push/clone tests | |||
@@ -1479,7 +1479,7 @@ public class GitBlit implements ServletContextListener { | |||
} | |||
return scripts; | |||
} | |||
public List<String> getInheritedPreReceiveScripts(RepositoryModel repository) { | |||
Set<String> globals = new HashSet<String>(); | |||
for (String script : getStrings(Keys.groovy.preReceiveScripts)) { |
@@ -200,4 +200,5 @@ gb.mailingLists = mailing lists | |||
gb.preReceiveScripts = pre-receive scripts | |||
gb.postReceiveScripts = post-receive scripts | |||
gb.hookScripts = hook scripts | |||
gb.accessPermissions = access permissions | |||
gb.accessPermissions = access permissions | |||
gb.filters = filters |
@@ -16,10 +16,13 @@ | |||
package com.gitblit.wicket; | |||
import java.io.Serializable; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import org.apache.wicket.PageParameters; | |||
import org.apache.wicket.markup.html.WebPage; | |||
import com.gitblit.wicket.pages.BasePage; | |||
import com.gitblit.utils.StringUtils; | |||
/** | |||
* Represents a page link registration for the topbar. | |||
@@ -31,17 +34,111 @@ public class PageRegistration implements Serializable { | |||
private static final long serialVersionUID = 1L; | |||
public final String translationKey; | |||
public final Class<? extends BasePage> pageClass; | |||
public final Class<? extends WebPage> pageClass; | |||
public final PageParameters params; | |||
public PageRegistration(String translationKey, Class<? extends BasePage> pageClass) { | |||
public PageRegistration(String translationKey, Class<? extends WebPage> pageClass) { | |||
this(translationKey, pageClass, null); | |||
} | |||
public PageRegistration(String translationKey, Class<? extends BasePage> pageClass, | |||
public PageRegistration(String translationKey, Class<? extends WebPage> pageClass, | |||
PageParameters params) { | |||
this.translationKey = translationKey; | |||
this.pageClass = pageClass; | |||
this.params = params; | |||
} | |||
/** | |||
* Represents a DropDownMenu for the topbar | |||
* | |||
* @author James Moger | |||
* | |||
*/ | |||
public static class DropDownMenuRegistration extends PageRegistration { | |||
private static final long serialVersionUID = 1L; | |||
public final List<DropDownMenuItem> menuItems; | |||
public DropDownMenuRegistration(String translationKey, Class<? extends WebPage> pageClass) { | |||
super(translationKey, pageClass); | |||
menuItems = new ArrayList<DropDownMenuItem>(); | |||
} | |||
} | |||
/** | |||
* A MenuItem for the DropDownMenu. | |||
* | |||
* @author James Moger | |||
* | |||
*/ | |||
public static class DropDownMenuItem implements Serializable { | |||
private static final long serialVersionUID = 1L; | |||
final String displayText; | |||
final String parameter; | |||
final String value; | |||
/** | |||
* Divider constructor. | |||
*/ | |||
public DropDownMenuItem() { | |||
displayText = null; | |||
parameter = null; | |||
value = null; | |||
} | |||
/** | |||
* Standard Menu Item constructor. | |||
* | |||
* @param displayText | |||
* @param parameter | |||
* @param value | |||
*/ | |||
public DropDownMenuItem(String displayText, String parameter, String value) { | |||
this.displayText = displayText; | |||
this.parameter = parameter; | |||
this.value = value; | |||
} | |||
public String formatParameter() { | |||
if (StringUtils.isEmpty(parameter) || StringUtils.isEmpty(value)) { | |||
return ""; | |||
} | |||
return parameter + "=" + value; | |||
} | |||
public boolean isDivider() { | |||
return displayText == null && value == null && parameter == null; | |||
} | |||
@Override | |||
public int hashCode() { | |||
if (isDivider()) { | |||
// divider menu item | |||
return super.hashCode(); | |||
} | |||
if (StringUtils.isEmpty(displayText)) { | |||
return value.hashCode() + parameter.hashCode(); | |||
} | |||
return displayText.hashCode(); | |||
} | |||
@Override | |||
public boolean equals(Object o) { | |||
if (o instanceof DropDownMenuItem) { | |||
return hashCode() == o.hashCode(); | |||
} | |||
return false; | |||
} | |||
@Override | |||
public String toString() { | |||
if (StringUtils.isEmpty(displayText)) { | |||
return formatParameter(); | |||
} | |||
return displayText; | |||
} | |||
} | |||
} |
@@ -24,14 +24,18 @@ import java.util.List; | |||
import java.util.Map; | |||
import java.util.Set; | |||
import org.apache.wicket.Application; | |||
import org.apache.wicket.PageParameters; | |||
import org.apache.wicket.behavior.HeaderContributor; | |||
import org.apache.wicket.markup.html.basic.Label; | |||
import com.gitblit.GitBlit; | |||
import com.gitblit.models.Activity; | |||
import com.gitblit.models.Metric; | |||
import com.gitblit.models.RepositoryModel; | |||
import com.gitblit.utils.ActivityUtils; | |||
import com.gitblit.wicket.PageRegistration; | |||
import com.gitblit.wicket.PageRegistration.DropDownMenuRegistration; | |||
import com.gitblit.wicket.WicketUtils; | |||
import com.gitblit.wicket.charting.GoogleChart; | |||
import com.gitblit.wicket.charting.GoogleCharts; | |||
@@ -90,6 +94,13 @@ public class ActivityPage extends RootPage { | |||
add(new ActivityPanel("activityPanel", recentActivity)); | |||
} | |||
} | |||
@Override | |||
protected void addDropDownMenus(List<PageRegistration> pages) { | |||
DropDownMenuRegistration menu = new DropDownMenuRegistration("gb.filters", ActivityPage.class); | |||
menu.menuItems.addAll(getFilterMenuItems()); | |||
pages.add(menu); | |||
} | |||
/** | |||
* Creates the daily activity line chart, the active repositories pie chart, | |||
@@ -167,4 +178,21 @@ public class ActivityPage extends RootPage { | |||
return charts; | |||
} | |||
@Override | |||
protected void onBeforeRender() { | |||
if (GitBlit.isDebugMode()) { | |||
// strip Wicket tags in debug mode for jQuery DOM traversal | |||
Application.get().getMarkupSettings().setStripWicketTags(true); | |||
} | |||
super.onBeforeRender(); | |||
} | |||
@Override | |||
protected void onAfterRender() { | |||
if (GitBlit.isDebugMode()) { | |||
// restore Wicket debug tags | |||
Application.get().getMarkupSettings().setStripWicketTags(false); | |||
} | |||
super.onAfterRender(); | |||
} | |||
} |
@@ -22,6 +22,7 @@ import java.io.InputStreamReader; | |||
import java.text.MessageFormat; | |||
import java.util.List; | |||
import org.apache.wicket.Application; | |||
import org.apache.wicket.Component; | |||
import org.apache.wicket.PageParameters; | |||
import org.apache.wicket.markup.html.basic.Label; | |||
@@ -33,6 +34,8 @@ import com.gitblit.models.RepositoryModel; | |||
import com.gitblit.utils.MarkdownUtils; | |||
import com.gitblit.utils.StringUtils; | |||
import com.gitblit.wicket.GitBlitWebSession; | |||
import com.gitblit.wicket.PageRegistration; | |||
import com.gitblit.wicket.PageRegistration.DropDownMenuRegistration; | |||
import com.gitblit.wicket.WicketUtils; | |||
import com.gitblit.wicket.panels.RepositoriesPanel; | |||
@@ -80,6 +83,13 @@ public class RepositoriesPage extends RootPage { | |||
add(repositoriesPanel); | |||
} | |||
@Override | |||
protected void addDropDownMenus(List<PageRegistration> pages) { | |||
DropDownMenuRegistration menu = new DropDownMenuRegistration("gb.filters", RepositoriesPage.class); | |||
menu.menuItems.addAll(getFilterMenuItems()); | |||
pages.add(menu); | |||
} | |||
private String readMarkdown(String messageSource, String resource) { | |||
String message = ""; | |||
if (messageSource.equalsIgnoreCase("gitblit")) { | |||
@@ -119,4 +129,21 @@ public class RepositoriesPage extends RootPage { | |||
} | |||
return message; | |||
} | |||
@Override | |||
protected void onBeforeRender() { | |||
if (GitBlit.isDebugMode()) { | |||
// strip Wicket tags in debug mode for jQuery DOM traversal | |||
Application.get().getMarkupSettings().setStripWicketTags(true); | |||
} | |||
super.onBeforeRender(); | |||
} | |||
@Override | |||
protected void onAfterRender() { | |||
if (GitBlit.isDebugMode()) { | |||
// restore Wicket debug tags | |||
Application.get().getMarkupSettings().setStripWicketTags(false); | |||
} | |||
super.onAfterRender(); | |||
} | |||
} |
@@ -17,7 +17,9 @@ package com.gitblit.wicket.pages; | |||
import java.text.MessageFormat; | |||
import java.util.ArrayList; | |||
import java.util.LinkedHashSet; | |||
import java.util.List; | |||
import java.util.Set; | |||
import java.util.regex.Pattern; | |||
import org.apache.wicket.PageParameters; | |||
@@ -37,6 +39,7 @@ import com.gitblit.models.UserModel; | |||
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.WicketUtils; | |||
import com.gitblit.wicket.panels.NavigationPanel; | |||
@@ -64,14 +67,17 @@ public abstract class RootPage extends BasePage { | |||
@Override | |||
protected void setupPage(String repositoryName, String pageName) { | |||
if (GitBlit.getBoolean(Keys.web.authenticateAdminPages, true)) { | |||
boolean allowAdmin = GitBlit.getBoolean(Keys.web.allowAdministration, false); | |||
boolean authenticateView = GitBlit.getBoolean(Keys.web.authenticateViewPages, false); | |||
boolean authenticateAdmin = GitBlit.getBoolean(Keys.web.authenticateAdminPages, true); | |||
boolean allowAdmin = GitBlit.getBoolean(Keys.web.allowAdministration, true); | |||
if (authenticateAdmin) { | |||
showAdmin = allowAdmin && GitBlitWebSession.get().canAdmin(); | |||
// authentication requires state and session | |||
setStatelessHint(false); | |||
} else { | |||
showAdmin = GitBlit.getBoolean(Keys.web.allowAdministration, false); | |||
if (GitBlit.getBoolean(Keys.web.authenticateViewPages, false)) { | |||
showAdmin = allowAdmin; | |||
if (authenticateView) { | |||
// authentication requires state and session | |||
setStatelessHint(false); | |||
} else { | |||
@@ -92,6 +98,11 @@ public abstract class RootPage extends BasePage { | |||
if (showAdmin || showRegistrations) { | |||
pages.add(new PageRegistration("gb.federation", FederationPage.class)); | |||
} | |||
if (!authenticateView || (authenticateView && GitBlitWebSession.get().isLoggedIn())) { | |||
addDropDownMenus(pages); | |||
} | |||
NavigationPanel navPanel = new NavigationPanel("navPanel", getClass(), pages); | |||
add(navPanel); | |||
@@ -125,8 +136,8 @@ public abstract class RootPage extends BasePage { | |||
WicketUtils.setInputPlaceholder(pwField, getString("gb.password")); | |||
loginForm.add(pwField); | |||
add(loginForm); | |||
if (GitBlit.getBoolean(Keys.web.authenticateViewPages, true) | |||
|| GitBlit.getBoolean(Keys.web.authenticateAdminPages, true)) { | |||
if (authenticateView || authenticateAdmin) { | |||
loginForm.setVisible(!GitBlitWebSession.get().isLoggedIn()); | |||
} else { | |||
loginForm.setVisible(false); | |||
@@ -167,6 +178,53 @@ public abstract class RootPage extends BasePage { | |||
} | |||
} | |||
protected void addDropDownMenus(List<PageRegistration> pages) { | |||
} | |||
protected List<DropDownMenuItem> getFilterMenuItems() { | |||
final UserModel user = GitBlitWebSession.get().getUser(); | |||
Set<DropDownMenuItem> filters = new LinkedHashSet<DropDownMenuItem>(); | |||
// accessible repositories by federation set | |||
for (RepositoryModel repository : GitBlit.self().getRepositoryModels(user)) { | |||
for (String set : repository.federationSets) { | |||
filters.add(new DropDownMenuItem(set, "set", set)); | |||
} | |||
} | |||
if (filters.size() > 0) { | |||
// divider | |||
filters.add(new DropDownMenuItem()); | |||
} | |||
// user's team memberships | |||
if (user != null && user.teams.size() > 0) { | |||
for (TeamModel team : user.teams) { | |||
filters.add(new DropDownMenuItem(team.name, "team", team.name)); | |||
} | |||
// divider | |||
filters.add(new DropDownMenuItem()); | |||
} | |||
// custom filters | |||
String customFilters = GitBlit.getString(Keys.web.customFilters, null); | |||
if (!StringUtils.isEmpty(customFilters)) { | |||
List<String> expressions = StringUtils.getStringsFromValue(customFilters, "!!!"); | |||
for (String expression : expressions) { | |||
filters.add(new DropDownMenuItem(null, "x", expression)); | |||
} | |||
} | |||
if (filters.size() > 0) { | |||
// if we have any filters, add the divider | |||
filters.add(new DropDownMenuItem()); | |||
// add All Repositories | |||
filters.add(new DropDownMenuItem("All Repositories", null, null)); | |||
} | |||
return new ArrayList<DropDownMenuItem>(filters); | |||
} | |||
protected List<RepositoryModel> getRepositories(PageParameters params) { | |||
final UserModel user = GitBlitWebSession.get().getUser(); | |||
if (params == null) { | |||
@@ -220,7 +278,7 @@ public abstract class RootPage extends BasePage { | |||
} else if (!StringUtils.isEmpty(team)) { | |||
// filter the repositories by the specified teams | |||
List<String> teams = StringUtils.getStringsFromValue(team, ","); | |||
// need TeamModels first | |||
List<TeamModel> teamModels = new ArrayList<TeamModel>(); | |||
for (String name : teams) { | |||
@@ -229,7 +287,7 @@ public abstract class RootPage extends BasePage { | |||
teamModels.add(model); | |||
} | |||
} | |||
// brute-force our way through finding the matching models | |||
List<RepositoryModel> matchingModels = new ArrayList<RepositoryModel>(); | |||
for (RepositoryModel repositoryModel : models) { |
@@ -0,0 +1,35 @@ | |||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | |||
<html xmlns="http://www.w3.org/1999/xhtml" | |||
xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd" | |||
xml:lang="en" | |||
lang="en"> | |||
<wicket:head> | |||
<script src="jquery.min.js" type="text/javascript" ></script> | |||
<script type="text/javascript"> | |||
function clearMenu() { | |||
// hide the menu | |||
$('a.menu, .dropdown-toggle').parent('li').removeClass('open') | |||
} | |||
$(document).ready(function() { | |||
// hide menu when clicking anywhere else in page | |||
$('html').bind('click', clearMenu) | |||
$('a.menu, .dropdown-toggle').click( | |||
function (e) { | |||
var $li = $(this).parent('li').toggleClass('open'); | |||
return false; | |||
} | |||
); | |||
}); | |||
</script> | |||
</wicket:head> | |||
<wicket:panel> | |||
<a class="menu" href=""><span wicket:id="label">label</span></a> | |||
<ul class="menu-dropdown"> | |||
<li wicket:id="menuItems"><span wicket:id="menuItem">[MenuItem]</span></li> | |||
</ul> | |||
</wicket:panel> | |||
</html> |
@@ -0,0 +1,56 @@ | |||
/* | |||
* 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.panels; | |||
import org.apache.wicket.PageParameters; | |||
import org.apache.wicket.markup.html.basic.Label; | |||
import org.apache.wicket.markup.html.panel.Panel; | |||
import org.apache.wicket.markup.repeater.Item; | |||
import org.apache.wicket.markup.repeater.data.DataView; | |||
import org.apache.wicket.markup.repeater.data.ListDataProvider; | |||
import com.gitblit.wicket.PageRegistration.DropDownMenuItem; | |||
import com.gitblit.wicket.PageRegistration.DropDownMenuRegistration; | |||
import com.gitblit.wicket.WicketUtils; | |||
public class DropDownMenu extends Panel { | |||
private static final long serialVersionUID = 1L; | |||
public DropDownMenu(String id, String label, final DropDownMenuRegistration menu) { | |||
super(id); | |||
add(new Label("label", label).setRenderBodyOnly(true)); | |||
ListDataProvider<DropDownMenuItem> items = new ListDataProvider<DropDownMenuItem>( | |||
menu.menuItems); | |||
DataView<DropDownMenuItem> view = new DataView<DropDownMenuItem>("menuItems", items) { | |||
private static final long serialVersionUID = 1L; | |||
public void populateItem(final Item<DropDownMenuItem> item) { | |||
DropDownMenuItem entry = item.getModelObject(); | |||
if (entry.isDivider()) { | |||
item.add(new Label("menuItem").setRenderBodyOnly(true)); | |||
WicketUtils.setCssClass(item, "divider"); | |||
} else { | |||
item.add(new LinkPanel("menuItem", null, entry.toString(), menu.pageClass, | |||
new PageParameters(entry.formatParameter())).setRenderBodyOnly(true)); | |||
} | |||
} | |||
}; | |||
add(view); | |||
setRenderBodyOnly(true); | |||
} | |||
} |
@@ -24,6 +24,7 @@ import org.apache.wicket.markup.repeater.data.DataView; | |||
import org.apache.wicket.markup.repeater.data.ListDataProvider; | |||
import com.gitblit.wicket.PageRegistration; | |||
import com.gitblit.wicket.PageRegistration.DropDownMenuRegistration; | |||
import com.gitblit.wicket.WicketUtils; | |||
import com.gitblit.wicket.pages.BasePage; | |||
@@ -31,20 +32,32 @@ public class NavigationPanel extends Panel { | |||
private static final long serialVersionUID = 1L; | |||
public NavigationPanel(String id, final Class<? extends BasePage> pageClass, List<PageRegistration> registeredPages) { | |||
public NavigationPanel(String id, final Class<? extends BasePage> pageClass, | |||
List<PageRegistration> registeredPages) { | |||
super(id); | |||
ListDataProvider<PageRegistration> refsDp = new ListDataProvider<PageRegistration>(registeredPages); | |||
ListDataProvider<PageRegistration> refsDp = new ListDataProvider<PageRegistration>( | |||
registeredPages); | |||
DataView<PageRegistration> refsView = new DataView<PageRegistration>("navLink", refsDp) { | |||
private static final long serialVersionUID = 1L; | |||
public void populateItem(final Item<PageRegistration> item) { | |||
PageRegistration entry = item.getModelObject(); | |||
Component c = new LinkPanel("link", null, getString(entry.translationKey), entry.pageClass, entry.params); | |||
if (entry.pageClass.equals(pageClass)) { | |||
WicketUtils.setCssClass(item, "active"); | |||
if (entry instanceof DropDownMenuRegistration) { | |||
// drop down menu | |||
DropDownMenuRegistration reg = (DropDownMenuRegistration) entry; | |||
Component c = new DropDownMenu("link", getString(entry.translationKey), reg); | |||
item.add(c); | |||
WicketUtils.setCssClass(item, "menu"); | |||
} else { | |||
// standard page link | |||
Component c = new LinkPanel("link", null, getString(entry.translationKey), | |||
entry.pageClass, entry.params); | |||
if (entry.pageClass.equals(pageClass)) { | |||
WicketUtils.setCssClass(item, "active"); | |||
} | |||
item.add(c); | |||
} | |||
item.add(c); | |||
} | |||
}; | |||
add(refsView); |