@@ -17,8 +17,10 @@ git.exportAll = true | |||
# e.g. /libraries/mylibrary.git | |||
git.nestedRepositories = true | |||
# The root clone url | |||
git.cloneUrl = https://localhost/git/ | |||
# Show other URLs on the summary page for accessing your git repositories | |||
# Use spaces to separate urls. {0} is the token for the repository name. | |||
# git.otherUrls = ssh://localhost/git/{0} git://localhost/git/{0} | |||
git.otherUrls = | |||
# | |||
# Authentication Settings | |||
@@ -34,7 +36,7 @@ web.authenticateAdminPages = true | |||
realm.realmFile = users.properties | |||
# How to store passwords. | |||
# Valid values are plain, md5 or crypt (unix style). Default is md5. | |||
# Valid values are plain or md5. Default is md5. | |||
realm.passwordStorage = md5 | |||
# Minimum valid length for a plain text password. |
@@ -9,14 +9,13 @@ import java.util.List; | |||
import javax.servlet.ServletContextEvent; | |||
import javax.servlet.ServletContextListener; | |||
import javax.servlet.http.Cookie; | |||
import org.apache.wicket.protocol.http.WebResponse; | |||
import org.eclipse.jgit.errors.RepositoryNotFoundException; | |||
import org.eclipse.jgit.lib.Repository; | |||
import org.eclipse.jgit.lib.StoredConfig; | |||
import org.eclipse.jgit.transport.resolver.FileResolver; | |||
import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException; | |||
import org.eclipse.jgit.util.FileUtils; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
@@ -61,8 +60,12 @@ public class GitBlit implements ServletContextListener { | |||
return storedSettings.getBoolean(Keys.web.debugMode, false); | |||
} | |||
public String getCloneUrl(String repositoryName) { | |||
return storedSettings.getString(Keys.git.cloneUrl, "https://localhost/git/") + repositoryName; | |||
public List<String> getOtherCloneUrls(String repositoryName) { | |||
List<String> cloneUrls = new ArrayList<String>(); | |||
for (String url : storedSettings.getStrings(Keys.git.otherUrls)) { | |||
cloneUrls.add(MessageFormat.format(url, repositoryName)); | |||
} | |||
return cloneUrls; | |||
} | |||
public void setLoginService(ILoginService loginService) { | |||
@@ -76,49 +79,31 @@ public class GitBlit implements ServletContextListener { | |||
return loginService.authenticate(username, password); | |||
} | |||
public UserModel authenticate(Cookie[] cookies) { | |||
if (loginService == null) { | |||
return null; | |||
} | |||
if (cookies != null && cookies.length > 0) { | |||
for (Cookie cookie : cookies) { | |||
if (cookie.getName().equals(Constants.NAME)) { | |||
String value = cookie.getValue(); | |||
return loginService.authenticate(value.toCharArray()); | |||
} | |||
} | |||
} | |||
return null; | |||
} | |||
public void setCookie(WebResponse response, UserModel user) { | |||
Cookie userCookie = new Cookie(Constants.NAME, user.getCookie()); | |||
userCookie.setMaxAge(Integer.MAX_VALUE); | |||
userCookie.setPath("/"); | |||
response.addCookie(userCookie); | |||
} | |||
public List<String> getAllUsernames() { | |||
List<String> names = loginService.getAllUsernames(); | |||
List<String> names = new ArrayList<String>(loginService.getAllUsernames()); | |||
Collections.sort(names); | |||
return names; | |||
} | |||
public boolean deleteUser(String username) { | |||
return loginService.deleteUser(username); | |||
} | |||
public UserModel getUserModel(String username) { | |||
UserModel user = loginService.getUserModel(username); | |||
return user; | |||
} | |||
public List<String> getRepositoryUsers(RepositoryModel repository) { | |||
return loginService.getUsernamesForRole(repository.name); | |||
} | |||
public boolean setRepositoryUsers(RepositoryModel repository, List<String> repositoryUsers) { | |||
return loginService.setUsernamesForRole(repository.name, repositoryUsers); | |||
} | |||
public void editUserModel(UserModel user, boolean isCreate) throws GitBlitException { | |||
if (!loginService.updateUserModel(user)) { | |||
public void editUserModel(String username, UserModel user, boolean isCreate) throws GitBlitException { | |||
if (!loginService.updateUserModel(username, user)) { | |||
throw new GitBlitException(isCreate ? "Failed to add user!" : "Failed to update user!"); | |||
} | |||
} | |||
@@ -152,7 +137,7 @@ public class GitBlit implements ServletContextListener { | |||
} | |||
return repositories; | |||
} | |||
public RepositoryModel getRepositoryModel(UserModel user, String repositoryName) { | |||
RepositoryModel model = getRepositoryModel(repositoryName); | |||
if (model.accessRestriction.atLeast(AccessRestrictionType.VIEW)) { | |||
@@ -184,7 +169,7 @@ public class GitBlit implements ServletContextListener { | |||
r.close(); | |||
return model; | |||
} | |||
private String getConfig(StoredConfig config, String field, String defaultValue) { | |||
String value = config.getString("gitblit", null, field); | |||
if (StringUtils.isEmpty(value)) { | |||
@@ -192,21 +177,37 @@ public class GitBlit implements ServletContextListener { | |||
} | |||
return value; | |||
} | |||
private boolean getConfig(StoredConfig config, String field, boolean defaultValue) { | |||
return config.getBoolean("gitblit", field, defaultValue); | |||
} | |||
public void editRepositoryModel(RepositoryModel repository, boolean isCreate) throws GitBlitException { | |||
public void editRepositoryModel(String repositoryName, RepositoryModel repository, boolean isCreate) throws GitBlitException { | |||
Repository r = null; | |||
if (isCreate) { | |||
if (new File(repositoriesFolder, repository.name).exists()) { | |||
throw new GitBlitException(MessageFormat.format("Can not create repository {0} because it already exists.", repository.name)); | |||
throw new GitBlitException(MessageFormat.format("Can not create repository ''{0}'' because it already exists.", repository.name)); | |||
} | |||
// create repository | |||
logger.info("create repository " + repository.name); | |||
r = JGitUtils.createRepository(repositoriesFolder, repository.name, true); | |||
} else { | |||
// rename repository | |||
if (!repositoryName.equalsIgnoreCase(repository.name)) { | |||
File folder = new File(repositoriesFolder, repositoryName); | |||
File destFolder = new File(repositoriesFolder, repository.name); | |||
if (destFolder.exists()) { | |||
throw new GitBlitException(MessageFormat.format("Can not rename repository ''{0}'' to ''{1}'' because ''{1}'' already exists.", repositoryName, repository.name)); | |||
} | |||
if (!folder.renameTo(destFolder)) { | |||
throw new GitBlitException(MessageFormat.format("Failed to rename repository ''{0}'' to ''{1}''.", repositoryName, repository.name)); | |||
} | |||
// rename the roles | |||
if (!loginService.renameRole(repositoryName, repository.name)) { | |||
throw new GitBlitException(MessageFormat.format("Failed to rename repository permissions ''{0}'' to ''{1}''.", repositoryName, repository.name)); | |||
} | |||
} | |||
// load repository | |||
logger.info("edit repository " + repository.name); | |||
try { | |||
@@ -235,6 +236,36 @@ public class GitBlit implements ServletContextListener { | |||
r.close(); | |||
} | |||
public boolean deleteRepositoryModel(RepositoryModel model) { | |||
return deleteRepository(model.name); | |||
} | |||
public boolean deleteRepository(String repositoryName) { | |||
try { | |||
File folder = new File(repositoriesFolder, repositoryName); | |||
if (folder.exists() && folder.isDirectory()) { | |||
FileUtils.delete(folder, FileUtils.RECURSIVE); | |||
if (loginService.deleteRole(repositoryName)) { | |||
return true; | |||
} | |||
} | |||
} catch (Throwable t) { | |||
logger.error(MessageFormat.format("Failed to delete repository {0}", repositoryName), t); | |||
} | |||
return false; | |||
} | |||
public boolean renameRepository(RepositoryModel model, String newName) { | |||
File folder = new File(repositoriesFolder, model.name); | |||
if (folder.exists() && folder.isDirectory()) { | |||
File newFolder = new File(repositoriesFolder, newName); | |||
if (folder.renameTo(newFolder)) { | |||
return loginService.renameRole(model.name, newName); | |||
} | |||
} | |||
return false; | |||
} | |||
public void configureContext(IStoredSettings settings) { | |||
logger.info("Using configuration from " + settings.toString()); | |||
this.storedSettings = settings; |
@@ -8,14 +8,16 @@ public interface ILoginService { | |||
UserModel authenticate(String username, char[] password); | |||
UserModel authenticate(char[] cookie); | |||
UserModel getUserModel(String username); | |||
boolean updateUserModel(UserModel model); | |||
boolean updateUserModel(String username, UserModel model); | |||
boolean deleteUserModel(UserModel model); | |||
boolean deleteUser(String username); | |||
List<String> getAllUsernames(); | |||
List<String> getUsernamesForRole(String role); | |||
@@ -25,5 +27,4 @@ public interface ILoginService { | |||
boolean renameRole(String oldRole, String newRole); | |||
boolean deleteRole(String role); | |||
} |
@@ -23,7 +23,6 @@ import org.eclipse.jetty.util.log.Log; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import com.gitblit.utils.StringUtils; | |||
import com.gitblit.wicket.models.UserModel; | |||
public class JettyLoginService extends MappedLoginService implements ILoginService { | |||
@@ -45,7 +44,6 @@ public class JettyLoginService extends MappedLoginService implements ILoginServi | |||
return null; | |||
} | |||
UserModel user = new UserModel(username); | |||
user.setCookie(StringUtils.getSHA1((Constants.NAME + username + new String(password)))); | |||
user.canAdmin(identity.isUserInRole(Constants.ADMIN_ROLE, null)); | |||
// Add repositories | |||
@@ -61,12 +59,6 @@ public class JettyLoginService extends MappedLoginService implements ILoginServi | |||
return user; | |||
} | |||
@Override | |||
public UserModel authenticate(char[] cookie) { | |||
// TODO cookie login | |||
return null; | |||
} | |||
@Override | |||
public UserModel getUserModel(String username) { | |||
UserIdentity identity = _users.get(username); | |||
@@ -107,6 +99,11 @@ public class JettyLoginService extends MappedLoginService implements ILoginServi | |||
@Override | |||
public boolean updateUserModel(UserModel model) { | |||
return updateUserModel(model.getUsername(), model); | |||
} | |||
@Override | |||
public boolean updateUserModel(String username, UserModel model) { | |||
try { | |||
Properties allUsers = readRealmFile(); | |||
ArrayList<String> roles = new ArrayList<String>(model.getRepositories()); | |||
@@ -125,11 +122,13 @@ public class JettyLoginService extends MappedLoginService implements ILoginServi | |||
} | |||
// trim trailing comma | |||
sb.setLength(sb.length() - 1); | |||
allUsers.remove(username); | |||
allUsers.put(model.getUsername(), sb.toString()); | |||
writeRealmFile(allUsers); | |||
// Update login service | |||
removeUser(username); | |||
putUser(model.getUsername(), Credential.getCredential(model.getPassword()), roles.toArray(new String[0])); | |||
return true; | |||
} catch (Throwable t) { | |||
@@ -140,21 +139,26 @@ public class JettyLoginService extends MappedLoginService implements ILoginServi | |||
@Override | |||
public boolean deleteUserModel(UserModel model) { | |||
return deleteUser(model.getUsername()); | |||
} | |||
@Override | |||
public boolean deleteUser(String username) { | |||
try { | |||
// Read realm file | |||
Properties allUsers = readRealmFile(); | |||
allUsers.remove(model.getUsername()); | |||
allUsers.remove(username); | |||
writeRealmFile(allUsers); | |||
// Drop user from map | |||
_users.remove(model.getUsername()); | |||
removeUser(username); | |||
return true; | |||
} catch (Throwable t) { | |||
logger.error(MessageFormat.format("Failed to delete user model {0}!", model.getUsername()), t); | |||
logger.error(MessageFormat.format("Failed to delete user {0}!", username), t); | |||
} | |||
return false; | |||
} | |||
@Override | |||
public List<String> getAllUsernames() { | |||
List<String> list = new ArrayList<String>(); | |||
@@ -366,6 +370,7 @@ public class JettyLoginService extends MappedLoginService implements ILoginServi | |||
// persist changes | |||
writeRealmFile(allUsers); | |||
return true; | |||
} catch (Throwable t) { | |||
logger.error(MessageFormat.format("Failed to delete role {0}!", role), t); | |||
} |
@@ -40,9 +40,13 @@ public class StringUtils { | |||
} | |||
public static String flattenStrings(List<String> values) { | |||
return flattenStrings(values, " "); | |||
} | |||
public static String flattenStrings(List<String> values, String separator) { | |||
StringBuilder sb = new StringBuilder(); | |||
for (String value : values) { | |||
sb.append(value).append(" "); | |||
sb.append(value).append(separator); | |||
} | |||
return sb.toString().trim(); | |||
} |
@@ -1,7 +1,5 @@ | |||
package com.gitblit.wicket; | |||
import javax.servlet.http.Cookie; | |||
import org.apache.wicket.PageParameters; | |||
import org.apache.wicket.markup.html.WebPage; | |||
import org.apache.wicket.markup.html.basic.Label; | |||
@@ -12,8 +10,6 @@ import org.apache.wicket.markup.html.form.TextField; | |||
import org.apache.wicket.markup.html.panel.FeedbackPanel; | |||
import org.apache.wicket.model.IModel; | |||
import org.apache.wicket.model.Model; | |||
import org.apache.wicket.protocol.http.WebRequest; | |||
import org.apache.wicket.protocol.http.WebResponse; | |||
import com.gitblit.Constants; | |||
import com.gitblit.GitBlit; | |||
@@ -28,8 +24,6 @@ public class LoginPage extends WebPage { | |||
public LoginPage(PageParameters params) { | |||
super(params); | |||
tryAutomaticLogin(); | |||
add(new Label("title", GitBlit.self().settings().getString(Keys.web.siteName, Constants.NAME))); | |||
add(new Label("name", Constants.NAME)); | |||
@@ -52,8 +46,6 @@ public class LoginPage extends WebPage { | |||
setRedirect(true); | |||
setResponsePage(getApplication().getHomePage()); | |||
} | |||
tryAutomaticLogin(); | |||
} | |||
@Override | |||
@@ -68,29 +60,12 @@ public class LoginPage extends WebPage { | |||
loginUser(user); | |||
} | |||
} | |||
private void tryAutomaticLogin() { | |||
UserModel user = null; | |||
// Grab cookie from Browser Session | |||
Cookie[] cookies = ((WebRequest) getRequestCycle().getRequest()).getCookies(); | |||
if (cookies != null && cookies.length > 0) { | |||
user = GitBlit.self().authenticate(cookies); | |||
} | |||
// Login the user | |||
loginUser(user); | |||
} | |||
private void loginUser(UserModel user) { | |||
if (user != null) { | |||
// Set the user into the session | |||
GitBlitWebSession.get().setUser(user); | |||
// Set Cookie | |||
WebResponse response = (WebResponse) getRequestCycle().getResponse(); | |||
GitBlit.self().setCookie(response, user); | |||
if (!continueToOriginalDestination()) { | |||
// Redirect to home page | |||
setResponsePage(getApplication().getHomePage()); |
@@ -35,6 +35,7 @@ import com.gitblit.utils.StringUtils; | |||
import com.gitblit.wicket.models.RepositoryModel; | |||
import com.gitblit.wicket.pages.BranchesPage; | |||
import com.gitblit.wicket.pages.DocsPage; | |||
import com.gitblit.wicket.pages.EditRepositoryPage; | |||
import com.gitblit.wicket.pages.LogPage; | |||
import com.gitblit.wicket.pages.SearchPage; | |||
import com.gitblit.wicket.pages.SummaryPage; | |||
@@ -65,6 +66,7 @@ public abstract class RepositoryPage extends BasePage { | |||
put("tags", "gb.tags"); | |||
put("tree", "gb.tree"); | |||
put("tickets", "gb.tickets"); | |||
put("edit", "gb.edit"); | |||
} | |||
}; | |||
@@ -90,16 +92,29 @@ public abstract class RepositoryPage extends BasePage { | |||
// per-repository extra page links | |||
List<String> extraPageLinks = new ArrayList<String>(); | |||
// Conditionally add tickets page | |||
// Conditionally add tickets link | |||
if (model.useTickets && JGitUtils.getTicketsBranch(r) != null) { | |||
extraPageLinks.add("tickets"); | |||
} | |||
// Conditionally add docs page | |||
// Conditionally add docs link | |||
if (model.useDocs) { | |||
extraPageLinks.add("docs"); | |||
} | |||
final boolean showAdmin; | |||
if (GitBlit.self().settings().getBoolean(Keys.web.authenticateAdminPages, true)) { | |||
boolean allowAdmin = GitBlit.self().settings().getBoolean(Keys.web.allowAdministration, false); | |||
showAdmin = allowAdmin && GitBlitWebSession.get().canAdmin(); | |||
} else { | |||
showAdmin = GitBlit.self().settings().getBoolean(Keys.web.allowAdministration, false); | |||
} | |||
// Conditionally add edit link | |||
if (showAdmin || GitBlitWebSession.get().isLoggedIn() && (model.owner != null && model.owner.equalsIgnoreCase(GitBlitWebSession.get().getUser().getUsername()))) { | |||
extraPageLinks.add("edit"); | |||
} | |||
ListDataProvider<String> extrasDp = new ListDataProvider<String>(extraPageLinks); | |||
DataView<String> extrasView = new DataView<String>("extra", extrasDp) { | |||
private static final long serialVersionUID = 1L; | |||
@@ -112,6 +127,9 @@ public abstract class RepositoryPage extends BasePage { | |||
} else if (extra.equals("docs")) { | |||
item.add(new Label("extraSeparator", " | ")); | |||
item.add(new LinkPanel("extraLink", null, getString("gb.docs"), DocsPage.class, WicketUtils.newRepositoryParameter(repositoryName))); | |||
} else if (extra.equals("edit")) { | |||
item.add(new Label("extraSeparator", " | ")); | |||
item.add(new LinkPanel("extraLink", null, getString("gb.edit"), EditRepositoryPage.class, WicketUtils.newRepositoryParameter(repositoryName))); | |||
} | |||
} | |||
}; |
@@ -33,5 +33,10 @@ public class RepositoryModel implements Serializable { | |||
this.owner = owner; | |||
this.lastChange = lastchange; | |||
this.accessRestriction = AccessRestrictionType.NONE; | |||
} | |||
} | |||
@Override | |||
public String toString() { | |||
return name; | |||
} | |||
} |
@@ -10,7 +10,6 @@ public class UserModel implements Serializable { | |||
private String username; | |||
private String password; | |||
private String cookie; | |||
private boolean canAdmin = false; | |||
private List<String> repositories = new ArrayList<String>(); | |||
@@ -42,14 +41,6 @@ public class UserModel implements Serializable { | |||
return canAdmin || repositories.contains(repositoryName); | |||
} | |||
public void setCookie(String cookie) { | |||
this.cookie = cookie; | |||
} | |||
public String getCookie() { | |||
return cookie; | |||
} | |||
public void setRepositories(List<String> repositories) { | |||
this.repositories.clear(); | |||
this.repositories.addAll(repositories); | |||
@@ -63,6 +54,7 @@ public class UserModel implements Serializable { | |||
return repositories; | |||
} | |||
@Override | |||
public String toString() { | |||
return username; | |||
} |
@@ -36,6 +36,8 @@ public class EditRepositoryPage extends BasePage { | |||
private final boolean isCreate; | |||
private boolean isAdmin = false; | |||
public EditRepositoryPage() { | |||
// create constructor | |||
super(); | |||
@@ -67,6 +69,7 @@ public class EditRepositoryPage extends BasePage { | |||
} | |||
} | |||
final String oldName = repositoryModel.name; | |||
final Palette<String> usersPalette = new Palette<String>("users", new ListModel<String>(repositoryUsers), new CollectionModel<String>(GitBlit.self().getAllUsernames()), new ChoiceRenderer<String>("", ""), 10, false); | |||
CompoundPropertyModel<RepositoryModel> model = new CompoundPropertyModel<RepositoryModel>(repositoryModel); | |||
Form<RepositoryModel> form = new Form<RepositoryModel>("editForm", model) { | |||
@@ -94,7 +97,7 @@ public class EditRepositoryPage extends BasePage { | |||
ok |= c == vc; | |||
} | |||
if (!ok) { | |||
error(MessageFormat.format("Illegal character '{0}' in repository name!", c)); | |||
error(MessageFormat.format("Illegal character ''{0}'' in repository name!", c)); | |||
return; | |||
} | |||
} | |||
@@ -107,7 +110,7 @@ public class EditRepositoryPage extends BasePage { | |||
} | |||
// save the repository | |||
GitBlit.self().editRepositoryModel(repositoryModel, isCreate); | |||
GitBlit.self().editRepositoryModel(oldName, repositoryModel, isCreate); | |||
// save the repository access list | |||
if (repositoryModel.accessRestriction.exceeds(AccessRestrictionType.NONE)) { | |||
@@ -117,7 +120,7 @@ public class EditRepositoryPage extends BasePage { | |||
repositoryUsers.add(users.next()); | |||
} | |||
// ensure the owner is added to the user list | |||
if (!repositoryUsers.contains(repositoryModel.owner)) { | |||
if (repositoryModel.owner != null && !repositoryUsers.contains(repositoryModel.owner)) { | |||
repositoryUsers.add(repositoryModel.owner); | |||
} | |||
GitBlit.self().setRepositoryUsers(repositoryModel, repositoryUsers); | |||
@@ -132,7 +135,7 @@ public class EditRepositoryPage extends BasePage { | |||
}; | |||
// field names reflective match RepositoryModel fields | |||
form.add(new TextField<String>("name").setEnabled(isCreate)); | |||
form.add(new TextField<String>("name").setEnabled(isCreate || isAdmin)); | |||
form.add(new TextField<String>("description")); | |||
form.add(new DropDownChoice<String>("owner", GitBlit.self().getAllUsernames()).setEnabled(GitBlitWebSession.get().canAdmin())); | |||
form.add(new DropDownChoice<AccessRestrictionType>("accessRestriction", Arrays.asList(AccessRestrictionType.values()), new AccessRestrictionRenderer())); | |||
@@ -175,6 +178,7 @@ public class EditRepositoryPage extends BasePage { | |||
// Edit Repository | |||
if (user.canAdmin()) { | |||
// Admins can edit everything | |||
isAdmin = true; | |||
return; | |||
} else { | |||
if (!model.owner.equalsIgnoreCase(user.getUsername())) { |
@@ -67,6 +67,7 @@ public class EditUserPage extends BasePage { | |||
repos.add(repo); | |||
} | |||
} | |||
final String oldName = userModel.getUsername(); | |||
final Palette<String> repositories = new Palette<String>("repositories", new ListModel<String>(userModel.getRepositories()), new CollectionModel<String>(repos), new ChoiceRenderer<String>("", ""), 10, false); | |||
Form<UserModel> form = new Form<UserModel>("editForm", model) { | |||
@@ -87,7 +88,7 @@ public class EditUserPage extends BasePage { | |||
if (isCreate) { | |||
UserModel model = GitBlit.self().getUserModel(username); | |||
if (model != null) { | |||
error(MessageFormat.format("Username {0} is unavailable.", username)); | |||
error(MessageFormat.format("Username ''{0}'' is unavailable.", username)); | |||
return; | |||
} | |||
} | |||
@@ -108,14 +109,11 @@ public class EditUserPage extends BasePage { | |||
return; | |||
} | |||
// Optionally encrypt/obfuscate the password. | |||
// Optionally store the password MD5 digest. | |||
String type = GitBlit.self().settings().getString(Keys.realm.passwordStorage, "md5"); | |||
if (type.equalsIgnoreCase("md5")) { | |||
// store MD5 checksum of password | |||
// store MD5 digest of password | |||
userModel.setPassword(MD5.digest(userModel.getPassword())); | |||
} else if (type.equalsIgnoreCase("crypt")) { | |||
// simple unix encryption | |||
userModel.setPassword(Crypt.crypt(userModel.getUsername(), userModel.getPassword())); | |||
} | |||
} | |||
@@ -126,7 +124,7 @@ public class EditUserPage extends BasePage { | |||
} | |||
userModel.setRepositories(repos); | |||
try { | |||
GitBlit.self().editUserModel(userModel, isCreate); | |||
GitBlit.self().editUserModel(oldName, userModel, isCreate); | |||
} catch (GitBlitException e) { | |||
error(e.getMessage()); | |||
return; | |||
@@ -134,7 +132,7 @@ public class EditUserPage extends BasePage { | |||
setRedirect(false); | |||
if (isCreate) { | |||
// create another user | |||
info(MessageFormat.format("New user {0} successfully created.", userModel.getUsername())); | |||
info(MessageFormat.format("New user ''{0}'' successfully created.", userModel.getUsername())); | |||
setResponsePage(EditUserPage.class); | |||
} else { | |||
// back to home | |||
@@ -144,7 +142,7 @@ public class EditUserPage extends BasePage { | |||
}; | |||
// field names reflective match UserModel fields | |||
form.add(new TextField<String>("username").setEnabled(isCreate)); | |||
form.add(new TextField<String>("username")); | |||
PasswordTextField passwordField = new PasswordTextField("password"); | |||
passwordField.setResetPassword(false); | |||
form.add(passwordField); |
@@ -46,7 +46,6 @@ public class RepositoriesPage extends BasePage { | |||
String cachedMessage = GitBlitWebSession.get().clearErrorMessage(); | |||
if (!StringUtils.isEmpty(cachedMessage)) { | |||
error(cachedMessage); | |||
System.out.println("displayed message"); | |||
} | |||
// Load the markdown welcome message |
@@ -20,7 +20,7 @@ | |||
<tr><th><wicket:message key="gb.owner">[owner]</wicket:message></th><td><span wicket:id="repositoryOwner">[repository owner]</span></td></tr> | |||
<tr><th><wicket:message key="gb.lastChange">[last change]</wicket:message></th><td><span wicket:id="repositoryLastChange">[repository last change]</span></td></tr> | |||
<tr><th><wicket:message key="gb.stats">[stats]</wicket:message></th><td><span wicket:id="repositoryStats">[repository stats]</span></td></tr> | |||
<tr><th><wicket:message key="gb.url">[URL]</wicket:message></th><td><img style="vertical-align: top; padding-right:5px;" wicket:id="accessRestrictionIcon" /><span wicket:id="repositoryCloneUrl">[repository clone url]</span></td></tr> | |||
<tr><th valign="top"><wicket:message key="gb.url">[URL]</wicket:message></th><td><img style="vertical-align: top; padding-right:5px;" wicket:id="accessRestrictionIcon" /><span wicket:id="repositoryCloneUrl">[repository clone url]</span></td></tr> | |||
</table> | |||
</div> | |||
</div> |
@@ -3,10 +3,14 @@ package com.gitblit.wicket.pages; | |||
import java.awt.Color; | |||
import java.awt.Dimension; | |||
import java.text.MessageFormat; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import javax.servlet.http.HttpServletRequest; | |||
import org.apache.wicket.PageParameters; | |||
import org.apache.wicket.markup.html.basic.Label; | |||
import org.apache.wicket.protocol.http.WebRequest; | |||
import org.eclipse.jgit.lib.Repository; | |||
import org.wicketstuff.googlecharts.AbstractChartData; | |||
import org.wicketstuff.googlecharts.Chart; | |||
@@ -19,10 +23,12 @@ import org.wicketstuff.googlecharts.LineStyle; | |||
import org.wicketstuff.googlecharts.MarkerType; | |||
import org.wicketstuff.googlecharts.ShapeMarker; | |||
import com.gitblit.Constants; | |||
import com.gitblit.Constants.AccessRestrictionType; | |||
import com.gitblit.GitBlit; | |||
import com.gitblit.Keys; | |||
import com.gitblit.utils.JGitUtils; | |||
import com.gitblit.utils.StringUtils; | |||
import com.gitblit.utils.TimeUtils; | |||
import com.gitblit.wicket.RepositoryPage; | |||
import com.gitblit.wicket.WicketUtils; | |||
@@ -67,25 +73,45 @@ public class SummaryPage extends RepositoryPage { | |||
} else { | |||
add(new Label("repositoryStats", MessageFormat.format("{0} commits and {1} tags in {2}", metricsTotal.count, metricsTotal.tag, TimeUtils.duration(metricsTotal.duration)))); | |||
} | |||
AccessRestrictionType accessRestriction = getRepositoryModel().accessRestriction; | |||
switch (accessRestriction) { | |||
case NONE: | |||
add(WicketUtils.newClearPixel("accessRestrictionIcon").setVisible(false)); | |||
break; | |||
case PUSH: | |||
add(WicketUtils.newImage("accessRestrictionIcon", "lock_go_16x16.png", getAccessRestrictions().get(accessRestriction))); | |||
break; | |||
case CLONE: | |||
add(WicketUtils.newImage("accessRestrictionIcon", "lock_pull_16x16.png", getAccessRestrictions().get(accessRestriction))); | |||
break; | |||
case VIEW: | |||
add(WicketUtils.newImage("accessRestrictionIcon", "shield_16x16.png", getAccessRestrictions().get(accessRestriction))); | |||
break; | |||
default: | |||
List<String> repositoryUrls = new ArrayList<String>(); | |||
if (GitBlit.self().settings().getBoolean(Keys.git.enableGitServlet, true)) { | |||
AccessRestrictionType accessRestriction = getRepositoryModel().accessRestriction; | |||
switch (accessRestriction) { | |||
case NONE: | |||
add(WicketUtils.newClearPixel("accessRestrictionIcon").setVisible(false)); | |||
break; | |||
case PUSH: | |||
add(WicketUtils.newImage("accessRestrictionIcon", "lock_go_16x16.png", getAccessRestrictions().get(accessRestriction))); | |||
break; | |||
case CLONE: | |||
add(WicketUtils.newImage("accessRestrictionIcon", "lock_pull_16x16.png", getAccessRestrictions().get(accessRestriction))); | |||
break; | |||
case VIEW: | |||
add(WicketUtils.newImage("accessRestrictionIcon", "shield_16x16.png", getAccessRestrictions().get(accessRestriction))); | |||
break; | |||
default: | |||
add(WicketUtils.newClearPixel("accessRestrictionIcon").setVisible(false)); | |||
} | |||
HttpServletRequest req = ((WebRequest) getRequestCycle().getRequest()).getHttpServletRequest(); | |||
StringBuilder sb = new StringBuilder(); | |||
sb.append(req.getScheme()); | |||
sb.append("://"); | |||
sb.append(req.getServerName()); | |||
if ((req.getScheme().equals("http") && req.getServerPort() != 80) || (req.getScheme().equals("https") && req.getServerPort() != 443)) { | |||
sb.append(":" + req.getServerPort()); | |||
} | |||
sb.append(Constants.GIT_SERVLET_PATH); | |||
sb.append(repositoryName); | |||
repositoryUrls.add(sb.toString()); | |||
} else { | |||
add(WicketUtils.newClearPixel("accessRestrictionIcon").setVisible(false)); | |||
} | |||
add(new Label("repositoryCloneUrl", GitBlit.self().getCloneUrl(repositoryName))); | |||
repositoryUrls.addAll(GitBlit.self().getOtherCloneUrls(repositoryName)); | |||
add(new Label("repositoryCloneUrl", StringUtils.flattenStrings(repositoryUrls, "<br/>")).setEscapeModelStrings(false)); | |||
add(new LogPanel("commitsPanel", repositoryName, null, r, numberCommits, 0)); | |||
add(new TagsPanel("tagsPanel", repositoryName, r, numberRefs)); | |||
@@ -113,9 +139,9 @@ public class SummaryPage extends RepositoryPage { | |||
commitAxis.setLabels(new String[] { "", String.valueOf((int) maxValue(metrics)) }); | |||
provider.addAxis(commitAxis); | |||
provider.setLineStyles(new LineStyle[] {new LineStyle(2, 4, 0), new LineStyle(0, 4, 1)}); | |||
provider.setLineStyles(new LineStyle[] { new LineStyle(2, 4, 0), new LineStyle(0, 4, 1) }); | |||
provider.addShapeMarker(new ShapeMarker(MarkerType.CIRCLE, Color.BLUE, 1, -1, 5)); | |||
add(new Chart("commitsChart", provider)); | |||
} else { | |||
add(WicketUtils.newBlankImage("commitsChart")); |
@@ -2,8 +2,10 @@ package com.gitblit.wicket.panels; | |||
import java.util.TimeZone; | |||
import org.apache.wicket.AttributeModifier; | |||
import org.apache.wicket.Component; | |||
import org.apache.wicket.markup.html.panel.Panel; | |||
import org.apache.wicket.model.Model; | |||
import com.gitblit.GitBlit; | |||
import com.gitblit.Keys; | |||
@@ -30,4 +32,22 @@ public abstract class BasePanel extends Panel { | |||
WicketUtils.setHtmlTooltip(component, getString("gb.searchForCommitter") + " " + value); | |||
} | |||
} | |||
public class JavascriptEventConfirmation extends AttributeModifier { | |||
private static final long serialVersionUID = 1L; | |||
public JavascriptEventConfirmation(String event, String msg) { | |||
super(event, true, new Model<String>(msg)); | |||
} | |||
protected String newValue(final String currentValue, final String replacementValue) { | |||
String prefix = "var conf = confirm('" + replacementValue + "'); " + "if (!conf) return false; "; | |||
String result = prefix; | |||
if (currentValue != null) { | |||
result = prefix + currentValue; | |||
} | |||
return result; | |||
} | |||
} | |||
} |
@@ -31,7 +31,7 @@ | |||
</wicket:fragment> | |||
<wicket:fragment wicket:id="repositoryAdminLinks"> | |||
<span class="link"><a wicket:id="editRepository"><wicket:message key="gb.edit">[edit]</wicket:message></a> | <a wicket:id="renameRepository"><wicket:message key="gb.rename">[rename]</wicket:message></a> | <a wicket:id="deleteRepository"><wicket:message key="gb.delete">[delete]</wicket:message></a></span> | |||
<span class="link"><a wicket:id="editRepository"><wicket:message key="gb.edit">[edit]</wicket:message></a> | <a wicket:id="deleteRepository"><wicket:message key="gb.delete">[delete]</wicket:message></a></span> | |||
</wicket:fragment> | |||
<wicket:fragment wicket:id="repositoryOwnerLinks"> |
@@ -1,5 +1,6 @@ | |||
package com.gitblit.wicket.panels; | |||
import java.text.MessageFormat; | |||
import java.util.ArrayList; | |||
import java.util.Collections; | |||
import java.util.Comparator; | |||
@@ -15,6 +16,7 @@ import org.apache.wicket.extensions.markup.html.repeater.util.SortParam; | |||
import org.apache.wicket.extensions.markup.html.repeater.util.SortableDataProvider; | |||
import org.apache.wicket.markup.html.basic.Label; | |||
import org.apache.wicket.markup.html.link.BookmarkablePageLink; | |||
import org.apache.wicket.markup.html.link.Link; | |||
import org.apache.wicket.markup.html.panel.Fragment; | |||
import org.apache.wicket.markup.repeater.Item; | |||
import org.apache.wicket.markup.repeater.data.DataView; | |||
@@ -36,22 +38,21 @@ import com.gitblit.wicket.models.UserModel; | |||
import com.gitblit.wicket.pages.EditRepositoryPage; | |||
import com.gitblit.wicket.pages.SummaryPage; | |||
public class RepositoriesPanel extends BasePanel { | |||
private static final long serialVersionUID = 1L; | |||
public RepositoriesPanel(String wicketId, final boolean showAdmin, final Map<AccessRestrictionType, String> accessRestrictionTranslations) { | |||
super(wicketId); | |||
final UserModel user = GitBlitWebSession.get().getUser(); | |||
List<RepositoryModel> models = GitBlit.self().getRepositoryModels(user); | |||
IDataProvider<RepositoryModel> dp; | |||
final IDataProvider<RepositoryModel> dp; | |||
Fragment adminLinks = new Fragment("adminPanel", "adminLinks", this); | |||
adminLinks.add(new BookmarkablePageLink<Void>("newRepository", EditRepositoryPage.class)); | |||
add(adminLinks.setVisible(showAdmin)); | |||
if (GitBlit.self().settings().getString(Keys.web.repositoryListType, "flat").equalsIgnoreCase("grouped")) { | |||
Map<String, List<RepositoryModel>> groups = new HashMap<String, List<RepositoryModel>>(); | |||
for (RepositoryModel model : models) { | |||
@@ -69,24 +70,30 @@ public class RepositoriesPanel extends BasePanel { | |||
List<RepositoryModel> groupedModels = new ArrayList<RepositoryModel>(); | |||
for (String root : roots) { | |||
List<RepositoryModel> subModels = groups.get(root); | |||
groupedModels.add(new GroupRepositoryModel(root + " (" + subModels.size() + ")")); | |||
groupedModels.add(new GroupRepositoryModel(root, subModels.size())); | |||
groupedModels.addAll(subModels); | |||
} | |||
dp = new ListDataProvider<RepositoryModel>(groupedModels); | |||
dp = new RepositoriesProvider(groupedModels); | |||
} else { | |||
dp = new DataProvider(models); | |||
dp = new SortableRepositoriesProvider(models); | |||
} | |||
DataView<RepositoryModel> dataView = new DataView<RepositoryModel>("row", dp) { | |||
private static final long serialVersionUID = 1L; | |||
int counter = 0; | |||
@Override | |||
protected void onBeforeRender() { | |||
super.onBeforeRender(); | |||
counter = 0; | |||
} | |||
public void populateItem(final Item<RepositoryModel> item) { | |||
final RepositoryModel entry = item.getModelObject(); | |||
if (entry instanceof GroupRepositoryModel) { | |||
Fragment row = new Fragment("rowContent", "groupRepositoryRow", this); | |||
item.add(row); | |||
row.add(new Label("groupName", entry.name)); | |||
row.add(new Label("groupName", entry.toString())); | |||
WicketUtils.setCssClass(item, "group"); | |||
return; | |||
} | |||
@@ -144,12 +151,30 @@ public class RepositoriesPanel extends BasePanel { | |||
row.add(lastChangeLabel); | |||
WicketUtils.setCssClass(lastChangeLabel, TimeUtils.timeAgoCss(entry.lastChange)); | |||
boolean showOwner = user != null && user.getUsername().equalsIgnoreCase(entry.owner); | |||
boolean showOwner = user != null && user.getUsername().equalsIgnoreCase(entry.owner); | |||
if (showAdmin) { | |||
Fragment repositoryLinks = new Fragment("repositoryLinks", "repositoryAdminLinks", this); | |||
repositoryLinks.add(new BookmarkablePageLink<Void>("editRepository", EditRepositoryPage.class, WicketUtils.newRepositoryParameter(entry.name))); | |||
repositoryLinks.add(new BookmarkablePageLink<Void>("renameRepository", EditRepositoryPage.class, WicketUtils.newRepositoryParameter(entry.name)).setEnabled(false)); | |||
repositoryLinks.add(new BookmarkablePageLink<Void>("deleteRepository", EditRepositoryPage.class, WicketUtils.newRepositoryParameter(entry.name)).setEnabled(false)); | |||
Link<Void> deleteLink = new Link<Void>("deleteRepository") { | |||
private static final long serialVersionUID = 1L; | |||
@Override | |||
public void onClick() { | |||
if (GitBlit.self().deleteRepositoryModel(entry)) { | |||
info(MessageFormat.format("Repository ''{0}'' deleted.", entry)); | |||
if (dp instanceof SortableRepositoriesProvider) { | |||
((SortableRepositoriesProvider) dp).remove(entry); | |||
} else { | |||
((RepositoriesProvider) dp).remove(entry); | |||
} | |||
} else { | |||
error(MessageFormat.format("Failed to delete repository ''{0}''!", entry)); | |||
} | |||
} | |||
}; | |||
deleteLink.add(new JavascriptEventConfirmation("onclick", MessageFormat.format("Delete repository \"{0}\"?", entry))); | |||
repositoryLinks.add(deleteLink); | |||
row.add(repositoryLinks); | |||
} else if (showOwner) { | |||
Fragment repositoryLinks = new Fragment("repositoryLinks", "repositoryOwnerLinks", this); | |||
@@ -179,16 +204,24 @@ public class RepositoriesPanel extends BasePanel { | |||
add(fragment); | |||
} | |||
} | |||
private class GroupRepositoryModel extends RepositoryModel { | |||
private static final long serialVersionUID = 1L; | |||
GroupRepositoryModel(String name) { | |||
int count = 0; | |||
GroupRepositoryModel(String name, int count) { | |||
super(name, "", "", new Date(0)); | |||
this.count = count; | |||
} | |||
@Override | |||
public String toString() { | |||
return name + " (" + count + ")"; | |||
} | |||
} | |||
protected enum SortBy { | |||
repository, description, owner, date; | |||
} | |||
@@ -204,15 +237,71 @@ public class RepositoriesPanel extends BasePanel { | |||
}; | |||
} | |||
private class DataProvider extends SortableDataProvider<RepositoryModel> { | |||
private class RepositoriesProvider extends ListDataProvider<RepositoryModel> { | |||
private static final long serialVersionUID = 1L; | |||
public RepositoriesProvider(List<RepositoryModel> list) { | |||
super(list); | |||
} | |||
@Override | |||
public List<RepositoryModel> getData() { | |||
return super.getData(); | |||
} | |||
public void remove(RepositoryModel model) { | |||
int index = getData().indexOf(model); | |||
RepositoryModel groupModel = null; | |||
if (index == (getData().size() - 1)) { | |||
// last element | |||
if (index > 0) { | |||
// previous element is group header, then this is last | |||
// repository in group. remove group too. | |||
if (getData().get(index - 1) instanceof GroupRepositoryModel) { | |||
groupModel = getData().get(index - 1); | |||
} | |||
} | |||
} else if (index < (getData().size() - 1)) { | |||
// not last element. check next element for group match. | |||
if (getData().get(index - 1) instanceof GroupRepositoryModel && getData().get(index + 1) instanceof GroupRepositoryModel) { | |||
// repository is sandwiched by group headers so this | |||
// repository is the only element in the group. remove | |||
// group. | |||
groupModel = getData().get(index - 1); | |||
} | |||
} | |||
if (groupModel == null) { | |||
// Find the group and decrement the count | |||
for (int i = index; i >= 0; i--) { | |||
if (getData().get(i) instanceof GroupRepositoryModel) { | |||
((GroupRepositoryModel) getData().get(i)).count--; | |||
break; | |||
} | |||
} | |||
} else { | |||
// Remove the group header | |||
getData().remove(groupModel); | |||
} | |||
getData().remove(model); | |||
} | |||
} | |||
private class SortableRepositoriesProvider extends SortableDataProvider<RepositoryModel> { | |||
private static final long serialVersionUID = 1L; | |||
private List<RepositoryModel> list = null; | |||
protected DataProvider(List<RepositoryModel> list) { | |||
protected SortableRepositoriesProvider(List<RepositoryModel> list) { | |||
this.list = list; | |||
setSort(SortBy.date.name(), false); | |||
} | |||
public void remove(RepositoryModel model) { | |||
list.remove(model); | |||
} | |||
@Override | |||
public int size() { | |||
if (list == null) |
@@ -1,6 +1,10 @@ | |||
package com.gitblit.wicket.panels; | |||
import java.text.MessageFormat; | |||
import java.util.List; | |||
import org.apache.wicket.markup.html.link.BookmarkablePageLink; | |||
import org.apache.wicket.markup.html.link.Link; | |||
import org.apache.wicket.markup.html.panel.Fragment; | |||
import org.apache.wicket.markup.repeater.Item; | |||
import org.apache.wicket.markup.repeater.data.DataView; | |||
@@ -10,7 +14,6 @@ import com.gitblit.GitBlit; | |||
import com.gitblit.wicket.LinkPanel; | |||
import com.gitblit.wicket.WicketUtils; | |||
import com.gitblit.wicket.pages.EditUserPage; | |||
import com.gitblit.wicket.pages.RepositoriesPage; | |||
public class UsersPanel extends BasePanel { | |||
@@ -22,10 +25,17 @@ public class UsersPanel extends BasePanel { | |||
Fragment adminLinks = new Fragment("adminPanel", "adminLinks", this); | |||
adminLinks.add(new BookmarkablePageLink<Void>("newUser", EditUserPage.class)); | |||
add(adminLinks.setVisible(showAdmin)); | |||
DataView<String> usersView = new DataView<String>("userRow", new ListDataProvider<String>(GitBlit.self().getAllUsernames())) { | |||
final List<String> usernames = GitBlit.self().getAllUsernames(); | |||
DataView<String> usersView = new DataView<String>("userRow", new ListDataProvider<String>(usernames)) { | |||
private static final long serialVersionUID = 1L; | |||
private int counter = 0; | |||
@Override | |||
protected void onBeforeRender() { | |||
super.onBeforeRender(); | |||
counter = 0; | |||
} | |||
public void populateItem(final Item<String> item) { | |||
final String entry = item.getModelObject(); | |||
@@ -34,7 +44,22 @@ public class UsersPanel extends BasePanel { | |||
item.add(editLink); | |||
Fragment userLinks = new Fragment("userLinks", "userAdminLinks", this); | |||
userLinks.add(new BookmarkablePageLink<Void>("editUser", EditUserPage.class, WicketUtils.newUsernameParameter(entry))); | |||
userLinks.add(new BookmarkablePageLink<Void>("deleteUser", RepositoriesPage.class, WicketUtils.newUsernameParameter(entry)).setEnabled(false)); | |||
Link<Void> deleteLink = new Link<Void>("deleteUser") { | |||
private static final long serialVersionUID = 1L; | |||
@Override | |||
public void onClick() { | |||
if (GitBlit.self().deleteUser(entry)) { | |||
usernames.remove(entry); | |||
info(MessageFormat.format("User ''{0}'' deleted.", entry)); | |||
} else { | |||
error(MessageFormat.format("Failed to delete user ''{0}''!", entry)); | |||
} | |||
} | |||
}; | |||
deleteLink.add(new JavascriptEventConfirmation("onclick", MessageFormat.format("Delete user \"{0}\"?", entry))); | |||
userLinks.add(deleteLink); | |||
item.add(userLinks); | |||
WicketUtils.setAlternatingBackground(item, counter); |