diff options
Diffstat (limited to 'src/main/java/com/gitblit')
132 files changed, 4453 insertions, 2475 deletions
diff --git a/src/main/java/com/gitblit/ConfigUserService.java b/src/main/java/com/gitblit/ConfigUserService.java index d7d6c14f..200ec8a6 100644 --- a/src/main/java/com/gitblit/ConfigUserService.java +++ b/src/main/java/com/gitblit/ConfigUserService.java @@ -37,6 +37,7 @@ import org.slf4j.LoggerFactory; import com.gitblit.Constants.AccessPermission;
import com.gitblit.Constants.AccountType;
+import com.gitblit.Constants.Role;
import com.gitblit.Constants.Transport;
import com.gitblit.manager.IRuntimeManager;
import com.gitblit.models.TeamModel;
@@ -734,22 +735,22 @@ public class ConfigUserService implements IUserService { // user roles
List<String> roles = new ArrayList<String>();
if (model.canAdmin) {
- roles.add(Constants.ADMIN_ROLE);
+ roles.add(Role.ADMIN.getRole());
}
if (model.canFork) {
- roles.add(Constants.FORK_ROLE);
+ roles.add(Role.FORK.getRole());
}
if (model.canCreate) {
- roles.add(Constants.CREATE_ROLE);
+ roles.add(Role.CREATE.getRole());
}
if (model.excludeFromFederation) {
- roles.add(Constants.NOT_FEDERATED_ROLE);
+ roles.add(Role.NOT_FEDERATED.getRole());
}
if (roles.size() == 0) {
// we do this to ensure that user record with no password
// is written. otherwise, StoredConfig optimizes that account
// away. :(
- roles.add(Constants.NO_ROLE);
+ roles.add(Role.NONE.getRole());
}
config.setStringList(USER, model.username, ROLE, roles);
@@ -778,18 +779,18 @@ public class ConfigUserService implements IUserService { // team roles
List<String> roles = new ArrayList<String>();
if (model.canAdmin) {
- roles.add(Constants.ADMIN_ROLE);
+ roles.add(Role.ADMIN.getRole());
}
if (model.canFork) {
- roles.add(Constants.FORK_ROLE);
+ roles.add(Role.FORK.getRole());
}
if (model.canCreate) {
- roles.add(Constants.CREATE_ROLE);
+ roles.add(Role.CREATE.getRole());
}
if (roles.size() == 0) {
// we do this to ensure that team record is written.
// Otherwise, StoredConfig might optimizes that record away.
- roles.add(Constants.NO_ROLE);
+ roles.add(Role.NONE.getRole());
}
config.setStringList(TEAM, model.name, ROLE, roles);
if (model.accountType != null) {
@@ -911,10 +912,10 @@ public class ConfigUserService implements IUserService { // user roles
Set<String> roles = new HashSet<String>(Arrays.asList(config.getStringList(
USER, username, ROLE)));
- user.canAdmin = roles.contains(Constants.ADMIN_ROLE);
- user.canFork = roles.contains(Constants.FORK_ROLE);
- user.canCreate = roles.contains(Constants.CREATE_ROLE);
- user.excludeFromFederation = roles.contains(Constants.NOT_FEDERATED_ROLE);
+ user.canAdmin = roles.contains(Role.ADMIN.getRole());
+ user.canFork = roles.contains(Role.FORK.getRole());
+ user.canCreate = roles.contains(Role.CREATE.getRole());
+ user.excludeFromFederation = roles.contains(Role.NOT_FEDERATED.getRole());
// repository memberships
if (!user.canAdmin) {
@@ -947,9 +948,9 @@ public class ConfigUserService implements IUserService { TeamModel team = new TeamModel(teamname);
Set<String> roles = new HashSet<String>(Arrays.asList(config.getStringList(
TEAM, teamname, ROLE)));
- team.canAdmin = roles.contains(Constants.ADMIN_ROLE);
- team.canFork = roles.contains(Constants.FORK_ROLE);
- team.canCreate = roles.contains(Constants.CREATE_ROLE);
+ team.canAdmin = roles.contains(Role.ADMIN.getRole());
+ team.canFork = roles.contains(Role.FORK.getRole());
+ team.canCreate = roles.contains(Role.CREATE.getRole());
team.accountType = AccountType.fromString(config.getString(TEAM, teamname, ACCOUNTTYPE));
if (!team.canAdmin) {
diff --git a/src/main/java/com/gitblit/Constants.java b/src/main/java/com/gitblit/Constants.java index fa8af25f..8b05f895 100644 --- a/src/main/java/com/gitblit/Constants.java +++ b/src/main/java/com/gitblit/Constants.java @@ -36,14 +36,19 @@ public class Constants { public static final String FULL_NAME = "Gitblit - a pure Java Git solution";
+ @Deprecated
public static final String ADMIN_ROLE = "#admin";
+ @Deprecated
public static final String FORK_ROLE = "#fork";
+ @Deprecated
public static final String CREATE_ROLE = "#create";
+ @Deprecated
public static final String NOT_FEDERATED_ROLE = "#notfederated";
+ @Deprecated
public static final String NO_ROLE = "#none";
public static final String EXTERNAL_ACCOUNT = "#externalAccount";
@@ -70,6 +75,8 @@ public class Constants { public static final String RAW_PATH = "/raw/";
+ public static final String PT_PATH = "/pt";
+
public static final String BRANCH_GRAPH_PATH = "/graph/";
public static final String BORDER = "*****************************************************************";
@@ -148,6 +155,17 @@ public class Constants { return getManifestValue("build-date", "PENDING");
}
+ public static String getASCIIArt() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(" _____ _ _ _ _ _ _").append('\n');
+ sb.append(" | __ \\(_)| | | | | |(_)| |").append('\n');
+ sb.append(" | | \\/ _ | |_ | |__ | | _ | |_").append('\n');
+ sb.append(" | | __ | || __|| '_ \\ | || || __|").append(" ").append("http://gitblit.com").append('\n');
+ sb.append(" | |_\\ \\| || |_ | |_) || || || |_").append(" ").append("@gitblit").append('\n');
+ sb.append(" \\____/|_| \\__||_.__/ |_||_| \\__|").append(" ").append(Constants.getVersion()).append('\n');
+ return sb.toString();
+ }
+
private static String getManifestValue(String attrib, String defaultValue) {
Class<?> clazz = Constants.class;
String className = clazz.getSimpleName() + ".class";
@@ -167,6 +185,19 @@ public class Constants { return defaultValue;
}
+ public static enum Role {
+ NONE, ADMIN, CREATE, FORK, NOT_FEDERATED;
+
+ public String getRole() {
+ return "#" + name().replace("_", "").toLowerCase();
+ }
+
+ @Override
+ public String toString() {
+ return getRole();
+ }
+ }
+
/**
* Enumeration representing the four access restriction levels.
*/
diff --git a/src/main/java/com/gitblit/DaggerModule.java b/src/main/java/com/gitblit/DaggerModule.java deleted file mode 100644 index dd7e1b2b..00000000 --- a/src/main/java/com/gitblit/DaggerModule.java +++ /dev/null @@ -1,222 +0,0 @@ -/* - * Copyright 2013 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; - -import javax.inject.Singleton; - -import com.gitblit.manager.AuthenticationManager; -import com.gitblit.manager.FederationManager; -import com.gitblit.manager.IAuthenticationManager; -import com.gitblit.manager.IFederationManager; -import com.gitblit.manager.IGitblit; -import com.gitblit.manager.INotificationManager; -import com.gitblit.manager.IPluginManager; -import com.gitblit.manager.IProjectManager; -import com.gitblit.manager.IRepositoryManager; -import com.gitblit.manager.IRuntimeManager; -import com.gitblit.manager.IUserManager; -import com.gitblit.manager.NotificationManager; -import com.gitblit.manager.PluginManager; -import com.gitblit.manager.ProjectManager; -import com.gitblit.manager.RepositoryManager; -import com.gitblit.manager.RuntimeManager; -import com.gitblit.manager.UserManager; -import com.gitblit.transport.ssh.FileKeyManager; -import com.gitblit.transport.ssh.IPublicKeyManager; -import com.gitblit.transport.ssh.MemoryKeyManager; -import com.gitblit.transport.ssh.NullKeyManager; -import com.gitblit.utils.JSoupXssFilter; -import com.gitblit.utils.StringUtils; -import com.gitblit.utils.XssFilter; -import com.gitblit.wicket.GitBlitWebApp; - -import dagger.Module; -import dagger.Provides; - -/** - * DaggerModule references all injectable objects. - * - * @author James Moger - * - */ -@Module( - library = true, - injects = { - IStoredSettings.class, - XssFilter.class, - - // core managers - IRuntimeManager.class, - IPluginManager.class, - INotificationManager.class, - IUserManager.class, - IAuthenticationManager.class, - IPublicKeyManager.class, - IRepositoryManager.class, - IProjectManager.class, - IFederationManager.class, - - // the monolithic manager - IGitblit.class, - - // the Gitblit Wicket app - GitBlitWebApp.class - } -) -public class DaggerModule { - - @Provides @Singleton IStoredSettings provideSettings() { - return new FileSettings(); - } - - @Provides @Singleton XssFilter provideXssFilter() { - return new JSoupXssFilter(); - } - - @Provides @Singleton IRuntimeManager provideRuntimeManager(IStoredSettings settings, XssFilter xssFilter) { - return new RuntimeManager(settings, xssFilter); - } - - @Provides @Singleton IPluginManager providePluginManager(IRuntimeManager runtimeManager) { - return new PluginManager(runtimeManager); - } - - @Provides @Singleton INotificationManager provideNotificationManager(IStoredSettings settings) { - return new NotificationManager(settings); - } - - @Provides @Singleton IUserManager provideUserManager( - IRuntimeManager runtimeManager, - IPluginManager pluginManager) { - - return new UserManager(runtimeManager, pluginManager); - } - - @Provides @Singleton IAuthenticationManager provideAuthenticationManager( - IRuntimeManager runtimeManager, - IUserManager userManager) { - - return new AuthenticationManager( - runtimeManager, - userManager); - } - - @Provides @Singleton IPublicKeyManager providePublicKeyManager( - IStoredSettings settings, - IRuntimeManager runtimeManager) { - - String clazz = settings.getString(Keys.git.sshKeysManager, FileKeyManager.class.getName()); - if (StringUtils.isEmpty(clazz)) { - clazz = FileKeyManager.class.getName(); - } - if (FileKeyManager.class.getName().equals(clazz)) { - return new FileKeyManager(runtimeManager); - } else if (NullKeyManager.class.getName().equals(clazz)) { - return new NullKeyManager(); - } else if (MemoryKeyManager.class.getName().equals(clazz)) { - return new MemoryKeyManager(); - } else { - try { - Class<?> mgrClass = Class.forName(clazz); - return (IPublicKeyManager) mgrClass.newInstance(); - } catch (Exception e) { - - } - return null; - } - } - - @Provides @Singleton IRepositoryManager provideRepositoryManager( - IRuntimeManager runtimeManager, - IPluginManager pluginManager, - IUserManager userManager) { - - return new RepositoryManager( - runtimeManager, - pluginManager, - userManager); - } - - @Provides @Singleton IProjectManager provideProjectManager( - IRuntimeManager runtimeManager, - IUserManager userManager, - IRepositoryManager repositoryManager) { - - return new ProjectManager( - runtimeManager, - userManager, - repositoryManager); - } - - @Provides @Singleton IFederationManager provideFederationManager( - IRuntimeManager runtimeManager, - INotificationManager notificationManager, - IRepositoryManager repositoryManager) { - - return new FederationManager( - runtimeManager, - notificationManager, - repositoryManager); - } - - @Provides @Singleton IGitblit provideGitblit( - IRuntimeManager runtimeManager, - IPluginManager pluginManager, - INotificationManager notificationManager, - IUserManager userManager, - IAuthenticationManager authenticationManager, - IPublicKeyManager publicKeyManager, - IRepositoryManager repositoryManager, - IProjectManager projectManager, - IFederationManager federationManager) { - - return new GitBlit( - runtimeManager, - pluginManager, - notificationManager, - userManager, - authenticationManager, - publicKeyManager, - repositoryManager, - projectManager, - federationManager); - } - - @Provides @Singleton GitBlitWebApp provideWebApplication( - IRuntimeManager runtimeManager, - IPluginManager pluginManager, - INotificationManager notificationManager, - IUserManager userManager, - IAuthenticationManager authenticationManager, - IPublicKeyManager publicKeyManager, - IRepositoryManager repositoryManager, - IProjectManager projectManager, - IFederationManager federationManager, - IGitblit gitblit) { - - return new GitBlitWebApp( - runtimeManager, - pluginManager, - notificationManager, - userManager, - authenticationManager, - publicKeyManager, - repositoryManager, - projectManager, - federationManager, - gitblit); - } -}
\ No newline at end of file diff --git a/src/main/java/com/gitblit/FederationClient.java b/src/main/java/com/gitblit/FederationClient.java index 079355ef..487080e5 100644 --- a/src/main/java/com/gitblit/FederationClient.java +++ b/src/main/java/com/gitblit/FederationClient.java @@ -1,192 +1,192 @@ -/*
- * 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;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
-import org.kohsuke.args4j.CmdLineException;
-import org.kohsuke.args4j.CmdLineParser;
-import org.kohsuke.args4j.Option;
-
-import com.gitblit.manager.FederationManager;
-import com.gitblit.manager.GitblitManager;
-import com.gitblit.manager.IGitblit;
-import com.gitblit.manager.INotificationManager;
-import com.gitblit.manager.RepositoryManager;
-import com.gitblit.manager.RuntimeManager;
-import com.gitblit.manager.UserManager;
-import com.gitblit.models.FederationModel;
-import com.gitblit.models.Mailing;
-import com.gitblit.service.FederationPullService;
-import com.gitblit.utils.FederationUtils;
-import com.gitblit.utils.StringUtils;
-import com.gitblit.utils.XssFilter;
-import com.gitblit.utils.XssFilter.AllowXssFilter;
-
-/**
- * Command-line client to pull federated Gitblit repositories.
- *
- * @author James Moger
- *
- */
-public class FederationClient {
-
- public static void main(String[] args) {
- Params params = new Params();
- CmdLineParser parser = new CmdLineParser(params);
- try {
- parser.parseArgument(args);
- } catch (CmdLineException t) {
- usage(parser, t);
- }
-
- System.out.println("Gitblit Federation Client v" + Constants.getVersion() + " (" + Constants.getBuildDate() + ")");
-
- // command-line specified base folder
- File baseFolder = new File(System.getProperty("user.dir"));
- if (!StringUtils.isEmpty(params.baseFolder)) {
- baseFolder = new File(params.baseFolder);
- }
-
- File regFile = com.gitblit.utils.FileUtils.resolveParameter(Constants.baseFolder$, baseFolder, params.registrationsFile);
- FileSettings settings = new FileSettings(regFile.getAbsolutePath());
- List<FederationModel> registrations = new ArrayList<FederationModel>();
- if (StringUtils.isEmpty(params.url)) {
- registrations.addAll(FederationUtils.getFederationRegistrations(settings));
- } else {
- if (StringUtils.isEmpty(params.token)) {
- System.out.println("Must specify --token parameter!");
- System.exit(0);
- }
- FederationModel model = new FederationModel("Gitblit");
- model.url = params.url;
- model.token = params.token;
- model.mirror = params.mirror;
- model.bare = params.bare;
- model.folder = "";
- registrations.add(model);
- }
- if (registrations.size() == 0) {
- System.out.println("No Federation Registrations! Nothing to do.");
- System.exit(0);
- }
-
- // command-line specified repositories folder
- if (!StringUtils.isEmpty(params.repositoriesFolder)) {
- settings.overrideSetting(Keys.git.repositoriesFolder, new File(
- params.repositoriesFolder).getAbsolutePath());
- }
-
- // configure the Gitblit singleton for minimal, non-server operation
- XssFilter xssFilter = new AllowXssFilter();
- RuntimeManager runtime = new RuntimeManager(settings, xssFilter, baseFolder).start();
- NoopNotificationManager notifications = new NoopNotificationManager().start();
- UserManager users = new UserManager(runtime, null).start();
- RepositoryManager repositories = new RepositoryManager(runtime, null, users).start();
- FederationManager federation = new FederationManager(runtime, notifications, repositories).start();
- IGitblit gitblit = new GitblitManager(runtime, null, notifications, users, null, null, repositories, null, federation);
-
- FederationPullService puller = new FederationPullService(gitblit, federation.getFederationRegistrations()) {
- @Override
- public void reschedule(FederationModel registration) {
- // NOOP
- }
- };
- puller.run();
-
- System.out.println("Finished.");
- System.exit(0);
- }
-
- private static void usage(CmdLineParser parser, CmdLineException t) {
- System.out.println(Constants.getGitBlitVersion());
- System.out.println();
- if (t != null) {
- System.out.println(t.getMessage());
- System.out.println();
- }
-
- if (parser != null) {
- parser.printUsage(System.out);
- }
- System.exit(0);
- }
-
- /**
- * Parameters class for FederationClient.
- */
- private static class Params {
-
- @Option(name = "--registrations", usage = "Gitblit Federation Registrations File", metaVar = "FILE")
- public String registrationsFile = "${baseFolder}/federation.properties";
-
- @Option(name = "--url", usage = "URL of Gitblit instance to mirror from", metaVar = "URL")
- public String url;
-
- @Option(name = "--mirror", usage = "Mirror repositories")
- public boolean mirror;
-
- @Option(name = "--bare", usage = "Create bare repositories")
- public boolean bare;
-
- @Option(name = "--token", usage = "Federation Token", metaVar = "TOKEN")
- public String token;
-
- @Option(name = "--baseFolder", usage = "Base folder for received data", metaVar = "PATH")
- public String baseFolder;
-
- @Option(name = "--repositoriesFolder", usage = "Destination folder for cloned repositories", metaVar = "PATH")
- public String repositoriesFolder;
-
- }
-
- private static class NoopNotificationManager implements INotificationManager {
-
- @Override
- public NoopNotificationManager start() {
- return this;
- }
-
- @Override
- public NoopNotificationManager stop() {
- return this;
- }
-
- @Override
- public boolean isSendingMail() {
- return false;
- }
-
- @Override
- public void sendMailToAdministrators(String subject, String message) {
- }
-
- @Override
- public void sendMail(String subject, String message, Collection<String> toAddresses) {
- }
-
- @Override
- public void sendHtmlMail(String subject, String message, Collection<String> toAddresses) {
- }
-
- @Override
- public void send(Mailing mailing) {
- }
- }
-}
+/* + * 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; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.kohsuke.args4j.CmdLineException; +import org.kohsuke.args4j.CmdLineParser; +import org.kohsuke.args4j.Option; + +import com.gitblit.manager.FederationManager; +import com.gitblit.manager.GitblitManager; +import com.gitblit.manager.IGitblit; +import com.gitblit.manager.INotificationManager; +import com.gitblit.manager.RepositoryManager; +import com.gitblit.manager.RuntimeManager; +import com.gitblit.manager.UserManager; +import com.gitblit.models.FederationModel; +import com.gitblit.models.Mailing; +import com.gitblit.service.FederationPullService; +import com.gitblit.utils.FederationUtils; +import com.gitblit.utils.StringUtils; +import com.gitblit.utils.XssFilter; +import com.gitblit.utils.XssFilter.AllowXssFilter; + +/** + * Command-line client to pull federated Gitblit repositories. + * + * @author James Moger + * + */ +public class FederationClient { + + public static void main(String[] args) { + Params params = new Params(); + CmdLineParser parser = new CmdLineParser(params); + try { + parser.parseArgument(args); + } catch (CmdLineException t) { + usage(parser, t); + } + + System.out.println("Gitblit Federation Client v" + Constants.getVersion() + " (" + Constants.getBuildDate() + ")"); + + // command-line specified base folder + File baseFolder = new File(System.getProperty("user.dir")); + if (!StringUtils.isEmpty(params.baseFolder)) { + baseFolder = new File(params.baseFolder); + } + + File regFile = com.gitblit.utils.FileUtils.resolveParameter(Constants.baseFolder$, baseFolder, params.registrationsFile); + FileSettings settings = new FileSettings(regFile.getAbsolutePath()); + List<FederationModel> registrations = new ArrayList<FederationModel>(); + if (StringUtils.isEmpty(params.url)) { + registrations.addAll(FederationUtils.getFederationRegistrations(settings)); + } else { + if (StringUtils.isEmpty(params.token)) { + System.out.println("Must specify --token parameter!"); + System.exit(0); + } + FederationModel model = new FederationModel("Gitblit"); + model.url = params.url; + model.token = params.token; + model.mirror = params.mirror; + model.bare = params.bare; + model.folder = ""; + registrations.add(model); + } + if (registrations.size() == 0) { + System.out.println("No Federation Registrations! Nothing to do."); + System.exit(0); + } + + // command-line specified repositories folder + if (!StringUtils.isEmpty(params.repositoriesFolder)) { + settings.overrideSetting(Keys.git.repositoriesFolder, new File( + params.repositoriesFolder).getAbsolutePath()); + } + + // configure the Gitblit singleton for minimal, non-server operation + XssFilter xssFilter = new AllowXssFilter(); + RuntimeManager runtime = new RuntimeManager(settings, xssFilter, baseFolder).start(); + NoopNotificationManager notifications = new NoopNotificationManager().start(); + UserManager users = new UserManager(runtime, null).start(); + RepositoryManager repositories = new RepositoryManager(runtime, null, users).start(); + FederationManager federation = new FederationManager(runtime, notifications, repositories).start(); + IGitblit gitblit = new GitblitManager(null, null, runtime, null, notifications, users, null, repositories, null, federation); + + FederationPullService puller = new FederationPullService(gitblit, federation.getFederationRegistrations()) { + @Override + public void reschedule(FederationModel registration) { + // NOOP + } + }; + puller.run(); + + System.out.println("Finished."); + System.exit(0); + } + + private static void usage(CmdLineParser parser, CmdLineException t) { + System.out.println(Constants.getGitBlitVersion()); + System.out.println(); + if (t != null) { + System.out.println(t.getMessage()); + System.out.println(); + } + + if (parser != null) { + parser.printUsage(System.out); + } + System.exit(0); + } + + /** + * Parameters class for FederationClient. + */ + private static class Params { + + @Option(name = "--registrations", usage = "Gitblit Federation Registrations File", metaVar = "FILE") + public String registrationsFile = "${baseFolder}/federation.properties"; + + @Option(name = "--url", usage = "URL of Gitblit instance to mirror from", metaVar = "URL") + public String url; + + @Option(name = "--mirror", usage = "Mirror repositories") + public boolean mirror; + + @Option(name = "--bare", usage = "Create bare repositories") + public boolean bare; + + @Option(name = "--token", usage = "Federation Token", metaVar = "TOKEN") + public String token; + + @Option(name = "--baseFolder", usage = "Base folder for received data", metaVar = "PATH") + public String baseFolder; + + @Option(name = "--repositoriesFolder", usage = "Destination folder for cloned repositories", metaVar = "PATH") + public String repositoriesFolder; + + } + + private static class NoopNotificationManager implements INotificationManager { + + @Override + public NoopNotificationManager start() { + return this; + } + + @Override + public NoopNotificationManager stop() { + return this; + } + + @Override + public boolean isSendingMail() { + return false; + } + + @Override + public void sendMailToAdministrators(String subject, String message) { + } + + @Override + public void sendMail(String subject, String message, Collection<String> toAddresses) { + } + + @Override + public void sendHtmlMail(String subject, String message, Collection<String> toAddresses) { + } + + @Override + public void send(Mailing mailing) { + } + } +} diff --git a/src/main/java/com/gitblit/FileSettings.java b/src/main/java/com/gitblit/FileSettings.java index 21a20435..3caf966b 100644 --- a/src/main/java/com/gitblit/FileSettings.java +++ b/src/main/java/com/gitblit/FileSettings.java @@ -18,10 +18,13 @@ package com.gitblit; import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.List;
import java.util.Map;
import java.util.Properties;
import com.gitblit.utils.FileUtils;
+import com.gitblit.utils.StringUtils;
/**
* Dynamically loads and reloads a properties file by keeping track of the last
@@ -77,10 +80,14 @@ public class FileSettings extends IStoredSettings { if (propertiesFile != null && propertiesFile.exists() && (forceReload || (propertiesFile.lastModified() > lastModified))) {
FileInputStream is = null;
try {
+ logger.debug("loading {}", propertiesFile);
Properties props = new Properties();
is = new FileInputStream(propertiesFile);
props.load(is);
+ // ticket-110
+ props = readIncludes(props);
+
// load properties after we have successfully read file
properties.clear();
properties.putAll(props);
@@ -103,12 +110,68 @@ public class FileSettings extends IStoredSettings { return properties;
}
+ /**
+ * Recursively read "include" properties files.
+ *
+ * @param properties
+ * @return
+ * @throws IOException
+ */
+ private Properties readIncludes(Properties properties) throws IOException {
+
+ Properties baseProperties = new Properties();
+
+ String include = (String) properties.remove("include");
+ if (!StringUtils.isEmpty(include)) {
+
+ // allow for multiples
+ List<String> names = StringUtils.getStringsFromValue(include, ",");
+ for (String name : names) {
+
+ if (StringUtils.isEmpty(name)) {
+ continue;
+ }
+
+ // try co-located
+ File file = new File(propertiesFile.getParentFile(), name.trim());
+ if (!file.exists()) {
+ // try absolute path
+ file = new File(name.trim());
+ }
+
+ if (!file.exists()) {
+ logger.warn("failed to locate {}", file);
+ continue;
+ }
+
+ // load properties
+ logger.debug("loading {}", file);
+ try (FileInputStream iis = new FileInputStream(file)) {
+ baseProperties.load(iis);
+ }
+
+ // read nested includes
+ baseProperties = readIncludes(baseProperties);
+
+ }
+
+ }
+
+ // includes are "default" properties, they must be set first and the
+ // props which specified the "includes" must override
+ Properties merged = new Properties();
+ merged.putAll(baseProperties);
+ merged.putAll(properties);
+
+ return merged;
+ }
+
@Override
public boolean saveSettings() {
String content = FileUtils.readContent(propertiesFile, "\n");
for (String key : removals) {
String regex = "(?m)^(" + regExEscape(key) + "\\s*+=\\s*+)"
- + "(?:[^\r\n\\\\]++|\\\\(?:\r?\n|\r|.))*+$";
+ + "(?:[^\r\n\\\\]++|\\\\(?:\r?\n|\r|.))*+$";
content = content.replaceAll(regex, "");
}
removals.clear();
@@ -128,7 +191,7 @@ public class FileSettings extends IStoredSettings { String content = FileUtils.readContent(propertiesFile, "\n");
for (Map.Entry<String, String> setting:settings.entrySet()) {
String regex = "(?m)^(" + regExEscape(setting.getKey()) + "\\s*+=\\s*+)"
- + "(?:[^\r\n\\\\]++|\\\\(?:\r?\n|\r|.))*+$";
+ + "(?:[^\r\n\\\\]++|\\\\(?:\r?\n|\r|.))*+$";
String oldContent = content;
content = content.replaceAll(regex, setting.getKey() + " = " + setting.getValue());
if (content.equals(oldContent)) {
diff --git a/src/main/java/com/gitblit/GitBlit.java b/src/main/java/com/gitblit/GitBlit.java index f9d9be9f..68a91bb5 100644 --- a/src/main/java/com/gitblit/GitBlit.java +++ b/src/main/java/com/gitblit/GitBlit.java @@ -15,471 +15,55 @@ */ package com.gitblit; -import java.text.MessageFormat; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Set; - -import javax.inject.Singleton; -import javax.servlet.http.HttpServletRequest; - -import com.gitblit.Constants.AccessPermission; -import com.gitblit.Constants.Transport; import com.gitblit.manager.GitblitManager; import com.gitblit.manager.IAuthenticationManager; import com.gitblit.manager.IFederationManager; -import com.gitblit.manager.IGitblit; import com.gitblit.manager.INotificationManager; import com.gitblit.manager.IPluginManager; import com.gitblit.manager.IProjectManager; import com.gitblit.manager.IRepositoryManager; import com.gitblit.manager.IRuntimeManager; import com.gitblit.manager.IUserManager; -import com.gitblit.manager.ServicesManager; -import com.gitblit.models.RepositoryModel; -import com.gitblit.models.RepositoryUrl; -import com.gitblit.models.UserModel; -import com.gitblit.tickets.BranchTicketService; -import com.gitblit.tickets.FileTicketService; import com.gitblit.tickets.ITicketService; -import com.gitblit.tickets.NullTicketService; -import com.gitblit.tickets.RedisTicketService; import com.gitblit.transport.ssh.IPublicKeyManager; -import com.gitblit.utils.StringUtils; - -import dagger.Module; -import dagger.ObjectGraph; -import dagger.Provides; +import com.google.inject.Inject; +import com.google.inject.Provider; +import com.google.inject.Singleton; /** - * GitBlit is the aggregate manager for the Gitblit webapp. It provides all - * management functions and also manages some long-running services. + * GitBlit is the aggregate manager for the Gitblit webapp. The parent class provides all + * functionality. This class exists to not break existing Groovy push hooks. * * @author James Moger * */ +@Singleton +@Deprecated public class GitBlit extends GitblitManager { - private final ObjectGraph injector; - - private final ServicesManager servicesManager; - - private ITicketService ticketService; - + @Inject public GitBlit( + Provider<IPublicKeyManager> publicKeyManagerProvider, + Provider<ITicketService> ticketServiceProvider, IRuntimeManager runtimeManager, IPluginManager pluginManager, INotificationManager notificationManager, IUserManager userManager, IAuthenticationManager authenticationManager, - IPublicKeyManager publicKeyManager, IRepositoryManager repositoryManager, IProjectManager projectManager, IFederationManager federationManager) { - super(runtimeManager, + super( + publicKeyManagerProvider, + ticketServiceProvider, + runtimeManager, pluginManager, notificationManager, userManager, authenticationManager, - publicKeyManager, repositoryManager, projectManager, federationManager); - - this.injector = ObjectGraph.create(getModules()); - - this.servicesManager = new ServicesManager(this); - } - - @Override - public GitBlit start() { - super.start(); - logger.info("Starting services manager..."); - servicesManager.start(); - configureTicketService(); - return this; - } - - @Override - public GitBlit stop() { - super.stop(); - servicesManager.stop(); - ticketService.stop(); - return this; - } - - @Override - public boolean isServingRepositories() { - return servicesManager.isServingRepositories(); - } - - @Override - public boolean isServingHTTP() { - return servicesManager.isServingHTTP(); - } - - @Override - public boolean isServingGIT() { - return servicesManager.isServingGIT(); - } - - @Override - public boolean isServingSSH() { - return servicesManager.isServingSSH(); - } - - protected Object [] getModules() { - return new Object [] { new GitBlitModule()}; - } - - protected boolean acceptPush(Transport byTransport) { - if (byTransport == null) { - logger.info("Unknown transport, push rejected!"); - return false; - } - - Set<Transport> transports = new HashSet<Transport>(); - for (String value : getSettings().getStrings(Keys.git.acceptedPushTransports)) { - Transport transport = Transport.fromString(value); - if (transport == null) { - logger.info(String.format("Ignoring unknown registered transport %s", value)); - continue; - } - - transports.add(transport); - } - - if (transports.isEmpty()) { - // no transports are explicitly specified, all are acceptable - return true; - } - - // verify that the transport is permitted - return transports.contains(byTransport); - } - - /** - * Returns a list of repository URLs and the user access permission. - * - * @param request - * @param user - * @param repository - * @return a list of repository urls - */ - @Override - public List<RepositoryUrl> getRepositoryUrls(HttpServletRequest request, UserModel user, RepositoryModel repository) { - if (user == null) { - user = UserModel.ANONYMOUS; - } - String username = StringUtils.encodeUsername(UserModel.ANONYMOUS.equals(user) ? "" : user.username); - - List<RepositoryUrl> list = new ArrayList<RepositoryUrl>(); - - // http/https url - if (settings.getBoolean(Keys.git.enableGitServlet, true)) { - AccessPermission permission = user.getRepositoryPermission(repository).permission; - if (permission.exceeds(AccessPermission.NONE)) { - Transport transport = Transport.fromString(request.getScheme()); - if (permission.atLeast(AccessPermission.PUSH) && !acceptPush(transport)) { - // downgrade the repo permission for this transport - // because it is not an acceptable PUSH transport - permission = AccessPermission.CLONE; - } - list.add(new RepositoryUrl(getRepositoryUrl(request, username, repository), permission)); - } - } - - // ssh daemon url - String sshDaemonUrl = servicesManager.getSshDaemonUrl(request, user, repository); - if (!StringUtils.isEmpty(sshDaemonUrl)) { - AccessPermission permission = user.getRepositoryPermission(repository).permission; - if (permission.exceeds(AccessPermission.NONE)) { - if (permission.atLeast(AccessPermission.PUSH) && !acceptPush(Transport.SSH)) { - // downgrade the repo permission for this transport - // because it is not an acceptable PUSH transport - permission = AccessPermission.CLONE; - } - - list.add(new RepositoryUrl(sshDaemonUrl, permission)); - } - } - - // git daemon url - String gitDaemonUrl = servicesManager.getGitDaemonUrl(request, user, repository); - if (!StringUtils.isEmpty(gitDaemonUrl)) { - AccessPermission permission = servicesManager.getGitDaemonAccessPermission(user, repository); - if (permission.exceeds(AccessPermission.NONE)) { - if (permission.atLeast(AccessPermission.PUSH) && !acceptPush(Transport.GIT)) { - // downgrade the repo permission for this transport - // because it is not an acceptable PUSH transport - permission = AccessPermission.CLONE; - } - list.add(new RepositoryUrl(gitDaemonUrl, permission)); - } - } - - // add all other urls - // {0} = repository - // {1} = username - for (String url : settings.getStrings(Keys.web.otherUrls)) { - if (url.contains("{1}")) { - // external url requires username, only add url IF we have one - if (!StringUtils.isEmpty(username)) { - list.add(new RepositoryUrl(MessageFormat.format(url, repository.name, username), null)); - } - } else { - // external url does not require username - list.add(new RepositoryUrl(MessageFormat.format(url, repository.name), null)); - } - } - - // sort transports by highest permission and then by transport security - Collections.sort(list, new Comparator<RepositoryUrl>() { - - @Override - public int compare(RepositoryUrl o1, RepositoryUrl o2) { - if (!o1.isExternal() && o2.isExternal()) { - // prefer Gitblit over external - return -1; - } else if (o1.isExternal() && !o2.isExternal()) { - // prefer Gitblit over external - return 1; - } else if (o1.isExternal() && o2.isExternal()) { - // sort by Transport ordinal - return o1.transport.compareTo(o2.transport); - } else if (o1.permission.exceeds(o2.permission)) { - // prefer highest permission - return -1; - } else if (o2.permission.exceeds(o1.permission)) { - // prefer highest permission - return 1; - } - - // prefer more secure transports - return o1.transport.compareTo(o2.transport); - } - }); - - // consider the user's transport preference - RepositoryUrl preferredUrl = null; - Transport preferredTransport = user.getPreferences().getTransport(); - if (preferredTransport != null) { - Iterator<RepositoryUrl> itr = list.iterator(); - while (itr.hasNext()) { - RepositoryUrl url = itr.next(); - if (url.transport.equals(preferredTransport)) { - itr.remove(); - preferredUrl = url; - break; - } - } - } - if (preferredUrl != null) { - list.add(0, preferredUrl); - } - - return list; - } - - /** - * Detect renames and reindex as appropriate. - */ - @Override - public void updateRepositoryModel(String repositoryName, RepositoryModel repository, - boolean isCreate) throws GitBlitException { - RepositoryModel oldModel = null; - boolean isRename = !isCreate && !repositoryName.equalsIgnoreCase(repository.name); - if (isRename) { - oldModel = repositoryManager.getRepositoryModel(repositoryName); - } - - super.updateRepositoryModel(repositoryName, repository, isCreate); - - if (isRename && ticketService != null) { - ticketService.rename(oldModel, repository); - } - } - - /** - * Delete the user and all associated public ssh keys. - */ - @Override - public boolean deleteUser(String username) { - UserModel user = userManager.getUserModel(username); - return deleteUserModel(user); - } - - @Override - public boolean deleteUserModel(UserModel model) { - boolean success = userManager.deleteUserModel(model); - if (success) { - getPublicKeyManager().removeAllKeys(model.username); - } - return success; - } - - /** - * Delete the repository and all associated tickets. - */ - @Override - public boolean deleteRepository(String repositoryName) { - RepositoryModel repository = repositoryManager.getRepositoryModel(repositoryName); - return deleteRepositoryModel(repository); - } - - @Override - public boolean deleteRepositoryModel(RepositoryModel model) { - boolean success = repositoryManager.deleteRepositoryModel(model); - if (success && ticketService != null) { - ticketService.deleteAll(model); - } - return success; - } - - /** - * Returns the configured ticket service. - * - * @return a ticket service - */ - @Override - public ITicketService getTicketService() { - return ticketService; - } - - protected void configureTicketService() { - String clazz = settings.getString(Keys.tickets.service, NullTicketService.class.getName()); - if (StringUtils.isEmpty(clazz)) { - clazz = NullTicketService.class.getName(); - } - try { - Class<? extends ITicketService> serviceClass = (Class<? extends ITicketService>) Class.forName(clazz); - ticketService = injector.get(serviceClass).start(); - if (ticketService instanceof NullTicketService) { - logger.warn("No ticket service configured."); - } else if (ticketService.isReady()) { - logger.info("{} is ready.", ticketService); - } else { - logger.warn("{} is disabled.", ticketService); - } - } catch (Exception e) { - logger.error("failed to create ticket service " + clazz, e); - ticketService = injector.get(NullTicketService.class).start(); - } - } - - /** - * A nested Dagger graph is used for constructor dependency injection of - * complex classes. - * - * @author James Moger - * - */ - @Module( - library = true, - injects = { - IStoredSettings.class, - - // core managers - IRuntimeManager.class, - IPluginManager.class, - INotificationManager.class, - IUserManager.class, - IAuthenticationManager.class, - IRepositoryManager.class, - IProjectManager.class, - IFederationManager.class, - - // the monolithic manager - IGitblit.class, - - // ticket services - NullTicketService.class, - FileTicketService.class, - BranchTicketService.class, - RedisTicketService.class - } - ) - class GitBlitModule { - - @Provides @Singleton IStoredSettings provideSettings() { - return settings; - } - - @Provides @Singleton IRuntimeManager provideRuntimeManager() { - return runtimeManager; - } - - @Provides @Singleton IPluginManager providePluginManager() { - return pluginManager; - } - - @Provides @Singleton INotificationManager provideNotificationManager() { - return notificationManager; - } - - @Provides @Singleton IUserManager provideUserManager() { - return userManager; - } - - @Provides @Singleton IAuthenticationManager provideAuthenticationManager() { - return authenticationManager; - } - - @Provides @Singleton IRepositoryManager provideRepositoryManager() { - return repositoryManager; - } - - @Provides @Singleton IProjectManager provideProjectManager() { - return projectManager; - } - - @Provides @Singleton IFederationManager provideFederationManager() { - return federationManager; - } - - @Provides @Singleton IGitblit provideGitblit() { - return GitBlit.this; - } - - @Provides @Singleton NullTicketService provideNullTicketService() { - return new NullTicketService( - runtimeManager, - pluginManager, - notificationManager, - userManager, - repositoryManager); - } - - @Provides @Singleton FileTicketService provideFileTicketService() { - return new FileTicketService( - runtimeManager, - pluginManager, - notificationManager, - userManager, - repositoryManager); - } - - @Provides @Singleton BranchTicketService provideBranchTicketService() { - return new BranchTicketService( - runtimeManager, - pluginManager, - notificationManager, - userManager, - repositoryManager); - } - - @Provides @Singleton RedisTicketService provideRedisTicketService() { - return new RedisTicketService( - runtimeManager, - pluginManager, - notificationManager, - userManager, - repositoryManager); - } } } diff --git a/src/main/java/com/gitblit/GitBlitServer.java b/src/main/java/com/gitblit/GitBlitServer.java index c79b1720..a1fd071f 100644 --- a/src/main/java/com/gitblit/GitBlitServer.java +++ b/src/main/java/com/gitblit/GitBlitServer.java @@ -217,22 +217,7 @@ public class GitBlitServer { } logger = LoggerFactory.getLogger(GitBlitServer.class); - logger.info(Constants.BORDER); - logger.info(" _____ _ _ _ _ _ _"); - logger.info(" | __ \\(_)| | | | | |(_)| |"); - logger.info(" | | \\/ _ | |_ | |__ | | _ | |_"); - logger.info(" | | __ | || __|| '_ \\ | || || __|"); - logger.info(" | |_\\ \\| || |_ | |_) || || || |_"); - logger.info(" \\____/|_| \\__||_.__/ |_||_| \\__|"); - int spacing = (Constants.BORDER.length() - Constants.getGitBlitVersion().length()) / 2; - StringBuilder sb = new StringBuilder(); - while (spacing > 0) { - spacing--; - sb.append(' '); - } - logger.info(sb.toString() + Constants.getGitBlitVersion()); - logger.info(""); - logger.info(Constants.BORDER); + logger.info("\n" + Constants.getASCIIArt()); System.setProperty("java.awt.headless", "true"); diff --git a/src/main/java/com/gitblit/auth/AuthenticationProvider.java b/src/main/java/com/gitblit/auth/AuthenticationProvider.java index 29051df8..da9a7af7 100644 --- a/src/main/java/com/gitblit/auth/AuthenticationProvider.java +++ b/src/main/java/com/gitblit/auth/AuthenticationProvider.java @@ -21,6 +21,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.gitblit.Constants.AccountType; +import com.gitblit.Constants.Role; import com.gitblit.IStoredSettings; import com.gitblit.manager.IRuntimeManager; import com.gitblit.manager.IUserManager; @@ -138,6 +139,24 @@ public abstract class AuthenticationProvider { */ public abstract boolean supportsTeamMembershipChanges(); + /** + * Returns true if the user's role can be changed. + * + * @param user + * @param role + * @return true if the user's role can be changed + */ + public abstract boolean supportsRoleChanges(UserModel user, Role role); + + /** + * Returns true if the team's role can be changed. + * + * @param user + * @param role + * @return true if the team's role can be changed + */ + public abstract boolean supportsRoleChanges(TeamModel team, Role role); + @Override public String toString() { return getServiceName() + " (" + getClass().getName() + ")"; @@ -199,5 +218,16 @@ public abstract class AuthenticationProvider { public boolean supportsTeamMembershipChanges() { return true; } + + @Override + public boolean supportsRoleChanges(UserModel user, Role role) { + return true; + } + + @Override + public boolean supportsRoleChanges(TeamModel team, Role role) { + return true; + } + } } diff --git a/src/main/java/com/gitblit/auth/HtpasswdAuthProvider.java b/src/main/java/com/gitblit/auth/HtpasswdAuthProvider.java index 5ffb6930..2cdabf6f 100644 --- a/src/main/java/com/gitblit/auth/HtpasswdAuthProvider.java +++ b/src/main/java/com/gitblit/auth/HtpasswdAuthProvider.java @@ -32,8 +32,10 @@ import org.apache.commons.codec.digest.Md5Crypt; import com.gitblit.Constants; import com.gitblit.Constants.AccountType; +import com.gitblit.Constants.Role; import com.gitblit.Keys; import com.gitblit.auth.AuthenticationProvider.UsernamePasswordAuthenticationProvider; +import com.gitblit.models.TeamModel; import com.gitblit.models.UserModel; @@ -124,6 +126,16 @@ public class HtpasswdAuthProvider extends UsernamePasswordAuthenticationProvider return true; } + @Override + public boolean supportsRoleChanges(UserModel user, Role role) { + return true; + } + + @Override + public boolean supportsRoleChanges(TeamModel team, Role role) { + return true; + } + /** * Authenticate a user based on a username and password. * diff --git a/src/main/java/com/gitblit/auth/LdapAuthProvider.java b/src/main/java/com/gitblit/auth/LdapAuthProvider.java index 5690073a..cc772e7b 100644 --- a/src/main/java/com/gitblit/auth/LdapAuthProvider.java +++ b/src/main/java/com/gitblit/auth/LdapAuthProvider.java @@ -30,6 +30,7 @@ import java.util.concurrent.TimeUnit; import com.gitblit.Constants; import com.gitblit.Constants.AccountType; +import com.gitblit.Constants.Role; import com.gitblit.Keys; import com.gitblit.auth.AuthenticationProvider.UsernamePasswordAuthenticationProvider; import com.gitblit.models.TeamModel; @@ -272,7 +273,6 @@ public class LdapAuthProvider extends UsernamePasswordAuthenticationProvider { return StringUtils.isEmpty(settings.getString(Keys.realm.ldap.email, "")); } - /** * If the LDAP server will maintain team memberships then LdapUserService * will not allow team membership changes. In this scenario all team @@ -286,6 +286,32 @@ public class LdapAuthProvider extends UsernamePasswordAuthenticationProvider { return !settings.getBoolean(Keys.realm.ldap.maintainTeams, false); } + @Override + public boolean supportsRoleChanges(UserModel user, Role role) { + if (Role.ADMIN == role) { + if (!supportsTeamMembershipChanges()) { + List<String> admins = settings.getStrings(Keys.realm.ldap.admins); + if (admins.contains(user.username)) { + return false; + } + } + } + return true; + } + + @Override + public boolean supportsRoleChanges(TeamModel team, Role role) { + if (Role.ADMIN == role) { + if (!supportsTeamMembershipChanges()) { + List<String> admins = settings.getStrings(Keys.realm.ldap.admins); + if (admins.contains("@" + team.name)) { + return false; + } + } + } + return true; + } + @Override public AccountType getAccountType() { return AccountType.LDAP; @@ -591,7 +617,8 @@ public class LdapAuthProvider extends UsernamePasswordAuthenticationProvider { if (ldapSyncService.isReady()) { long ldapSyncPeriod = getSynchronizationPeriodInMilliseconds(); int delay = 1; - logger.info("Ldap sync service will update users and groups every {} minutes.", ldapSyncPeriod); + logger.info("Ldap sync service will update users and groups every {} minutes.", + TimeUnit.MILLISECONDS.toMinutes(ldapSyncPeriod)); scheduledExecutorService.scheduleAtFixedRate(ldapSyncService, delay, ldapSyncPeriod, TimeUnit.MILLISECONDS); } else { logger.info("Ldap sync service is disabled."); diff --git a/src/main/java/com/gitblit/auth/PAMAuthProvider.java b/src/main/java/com/gitblit/auth/PAMAuthProvider.java index 5d441b84..46f4dd6a 100644 --- a/src/main/java/com/gitblit/auth/PAMAuthProvider.java +++ b/src/main/java/com/gitblit/auth/PAMAuthProvider.java @@ -23,8 +23,10 @@ import org.jvnet.libpam.impl.CLibrary; import com.gitblit.Constants; import com.gitblit.Constants.AccountType; +import com.gitblit.Constants.Role; import com.gitblit.Keys; import com.gitblit.auth.AuthenticationProvider.UsernamePasswordAuthenticationProvider; +import com.gitblit.models.TeamModel; import com.gitblit.models.UserModel; /** @@ -77,6 +79,16 @@ public class PAMAuthProvider extends UsernamePasswordAuthenticationProvider { return true; } + @Override + public boolean supportsRoleChanges(UserModel user, Role role) { + return true; + } + + @Override + public boolean supportsRoleChanges(TeamModel team, Role role) { + return true; + } + @Override public AccountType getAccountType() { return AccountType.PAM; diff --git a/src/main/java/com/gitblit/auth/RedmineAuthProvider.java b/src/main/java/com/gitblit/auth/RedmineAuthProvider.java index ae4f28ed..27cece29 100644 --- a/src/main/java/com/gitblit/auth/RedmineAuthProvider.java +++ b/src/main/java/com/gitblit/auth/RedmineAuthProvider.java @@ -23,8 +23,10 @@ import org.apache.commons.io.IOUtils; import com.gitblit.Constants; import com.gitblit.Constants.AccountType; +import com.gitblit.Constants.Role; import com.gitblit.Keys; import com.gitblit.auth.AuthenticationProvider.UsernamePasswordAuthenticationProvider; +import com.gitblit.models.TeamModel; import com.gitblit.models.UserModel; import com.gitblit.utils.ConnectionUtils; import com.gitblit.utils.StringUtils; @@ -77,6 +79,16 @@ public class RedmineAuthProvider extends UsernamePasswordAuthenticationProvider return false; } + @Override + public boolean supportsRoleChanges(UserModel user, Role role) { + return true; + } + + @Override + public boolean supportsRoleChanges(TeamModel team, Role role) { + return true; + } + @Override public AccountType getAccountType() { return AccountType.REDMINE; @@ -154,7 +166,7 @@ public class RedmineAuthProvider extends UsernamePasswordAuthenticationProvider url = url.concat("/"); } String apiUrl = url + "users/current.json"; - + HttpURLConnection http; if (username == null) { // apikey authentication diff --git a/src/main/java/com/gitblit/auth/SalesforceAuthProvider.java b/src/main/java/com/gitblit/auth/SalesforceAuthProvider.java index e4273ff6..df033c27 100644 --- a/src/main/java/com/gitblit/auth/SalesforceAuthProvider.java +++ b/src/main/java/com/gitblit/auth/SalesforceAuthProvider.java @@ -2,8 +2,10 @@ package com.gitblit.auth; import com.gitblit.Constants; import com.gitblit.Constants.AccountType; +import com.gitblit.Constants.Role; import com.gitblit.Keys; import com.gitblit.auth.AuthenticationProvider.UsernamePasswordAuthenticationProvider; +import com.gitblit.models.TeamModel; import com.gitblit.models.UserModel; import com.sforce.soap.partner.Connector; import com.sforce.soap.partner.GetUserInfoResult; @@ -119,4 +121,15 @@ public class SalesforceAuthProvider extends UsernamePasswordAuthenticationProvid public boolean supportsTeamMembershipChanges() { return true; } + + @Override + public boolean supportsRoleChanges(UserModel user, Role role) { + return true; + } + + @Override + public boolean supportsRoleChanges(TeamModel team, Role role) { + return true; + } + } diff --git a/src/main/java/com/gitblit/auth/WindowsAuthProvider.java b/src/main/java/com/gitblit/auth/WindowsAuthProvider.java index ac15b28f..aee51008 100644 --- a/src/main/java/com/gitblit/auth/WindowsAuthProvider.java +++ b/src/main/java/com/gitblit/auth/WindowsAuthProvider.java @@ -26,8 +26,10 @@ import waffle.windows.auth.impl.WindowsAuthProviderImpl; import com.gitblit.Constants; import com.gitblit.Constants.AccountType; +import com.gitblit.Constants.Role; import com.gitblit.Keys; import com.gitblit.auth.AuthenticationProvider.UsernamePasswordAuthenticationProvider; +import com.gitblit.models.TeamModel; import com.gitblit.models.UserModel; import com.gitblit.utils.StringUtils; import com.sun.jna.platform.win32.Win32Exception; @@ -90,6 +92,16 @@ public class WindowsAuthProvider extends UsernamePasswordAuthenticationProvider return true; } + @Override + public boolean supportsRoleChanges(UserModel user, Role role) { + return true; + } + + @Override + public boolean supportsRoleChanges(TeamModel team, Role role) { + return true; + } + @Override public AccountType getAccountType() { return AccountType.WINDOWS; diff --git a/src/main/java/com/gitblit/dagger/DaggerContext.java b/src/main/java/com/gitblit/dagger/DaggerContext.java deleted file mode 100644 index 0e6a3fc4..00000000 --- a/src/main/java/com/gitblit/dagger/DaggerContext.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2013 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.dagger; - -import javax.servlet.ServletContext; -import javax.servlet.ServletContextEvent; -import javax.servlet.ServletContextListener; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import dagger.ObjectGraph; - -/** - * Dagger servlet context listener is a context listener that uses Dagger to - * instantiate and inject servlets, filters, and anything else you might want. - * - * @author James Moger - * - */ -public abstract class DaggerContext implements ServletContextListener { - - public static final String INJECTOR_NAME = ObjectGraph.class.getName(); - - protected final Logger logger = LoggerFactory.getLogger(getClass()); - - protected abstract Object [] getModules(); - - protected abstract void destroyContext(ServletContext context); - - protected ObjectGraph getInjector(ServletContext context) { - Object o = context.getAttribute(INJECTOR_NAME); - if (o == null) { - logger.debug("instantiating Dagger modules"); - Object [] modules = getModules(); - logger.debug("getting Dagger injector"); - try { - o = ObjectGraph.create(modules); - logger.debug("setting Dagger injector into {} attribute", INJECTOR_NAME); - context.setAttribute(INJECTOR_NAME, o); - } catch (Throwable t) { - logger.error("an error occurred creating the Dagger injector", t); - } - } - return (ObjectGraph) o; - } - - @Override - public final void contextDestroyed(ServletContextEvent contextEvent) { - ServletContext context = contextEvent.getServletContext(); - context.removeAttribute(INJECTOR_NAME); - destroyContext(context); - } -} diff --git a/src/main/java/com/gitblit/dagger/DaggerFilter.java b/src/main/java/com/gitblit/dagger/DaggerFilter.java deleted file mode 100644 index 01c07a4a..00000000 --- a/src/main/java/com/gitblit/dagger/DaggerFilter.java +++ /dev/null @@ -1,47 +0,0 @@ -/*
- * Copyright 2013 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.dagger;
-
-import javax.servlet.Filter;
-import javax.servlet.FilterConfig;
-import javax.servlet.ServletContext;
-import javax.servlet.ServletException;
-
-import dagger.ObjectGraph;
-
-/**
- * Uses Dagger to manually inject dependencies into a servlet filter.
- * This class is useful for servlet containers that offer CDI and are
- * confused by Dagger.
- *
- * @author James Moger
- *
- */
-public abstract class DaggerFilter implements Filter {
-
- @Override
- public final void init(FilterConfig filterConfig) throws ServletException {
- ServletContext context = filterConfig.getServletContext();
- ObjectGraph objectGraph = (ObjectGraph) context.getAttribute(DaggerContext.INJECTOR_NAME);
- inject(objectGraph, filterConfig);
- }
-
- protected abstract void inject(ObjectGraph dagger, FilterConfig filterConfig) throws ServletException;
-
- @Override
- public void destroy() {
- }
-}
diff --git a/src/main/java/com/gitblit/dagger/DaggerServlet.java b/src/main/java/com/gitblit/dagger/DaggerServlet.java deleted file mode 100644 index 88331a43..00000000 --- a/src/main/java/com/gitblit/dagger/DaggerServlet.java +++ /dev/null @@ -1,44 +0,0 @@ -/*
- * Copyright 2013 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.dagger;
-
-import javax.servlet.ServletContext;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-
-import dagger.ObjectGraph;
-
-/**
- * Uses Dagger to manually inject dependencies into a servlet.
- * This class is useful for servlet containers that offer CDI and are
- * confused by Dagger.
- *
- * @author James Moger
- *
- */
-public abstract class DaggerServlet extends HttpServlet {
-
- private static final long serialVersionUID = 1L;
-
- @Override
- public final void init() throws ServletException {
- ServletContext context = getServletContext();
- ObjectGraph objectGraph = (ObjectGraph) context.getAttribute(DaggerContext.INJECTOR_NAME);
- inject(objectGraph);
- }
-
- protected abstract void inject(ObjectGraph dagger);
-}
diff --git a/src/main/java/com/gitblit/dagger/DaggerWicketFilter.java b/src/main/java/com/gitblit/dagger/DaggerWicketFilter.java deleted file mode 100644 index c2fd4d67..00000000 --- a/src/main/java/com/gitblit/dagger/DaggerWicketFilter.java +++ /dev/null @@ -1,45 +0,0 @@ -/*
- * Copyright 2013 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.dagger;
-
-import javax.servlet.FilterConfig;
-import javax.servlet.ServletContext;
-import javax.servlet.ServletException;
-
-import org.apache.wicket.protocol.http.WicketFilter;
-
-import dagger.ObjectGraph;
-
-/**
- * Uses Dagger to manually inject dependencies into a Wicket filter.
- * This class is useful for servlet containers that offer CDI and are
- * confused by Dagger.
- *
- * @author James Moger
- *
- */
-public abstract class DaggerWicketFilter extends WicketFilter {
-
- @Override
- public final void init(FilterConfig filterConfig) throws ServletException {
- ServletContext context = filterConfig.getServletContext();
- ObjectGraph objectGraph = (ObjectGraph) context.getAttribute(DaggerContext.INJECTOR_NAME);
- inject(objectGraph);
- super.init(filterConfig);
- }
-
- protected abstract void inject(ObjectGraph dagger);
-}
diff --git a/src/main/java/com/gitblit/guice/CoreModule.java b/src/main/java/com/gitblit/guice/CoreModule.java new file mode 100644 index 00000000..a942b2ec --- /dev/null +++ b/src/main/java/com/gitblit/guice/CoreModule.java @@ -0,0 +1,82 @@ +/* + * Copyright 2014 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.guice; + +import com.gitblit.FileSettings; +import com.gitblit.GitBlit; +import com.gitblit.IStoredSettings; +import com.gitblit.manager.AuthenticationManager; +import com.gitblit.manager.FederationManager; +import com.gitblit.manager.IAuthenticationManager; +import com.gitblit.manager.IFederationManager; +import com.gitblit.manager.IGitblit; +import com.gitblit.manager.INotificationManager; +import com.gitblit.manager.IPluginManager; +import com.gitblit.manager.IProjectManager; +import com.gitblit.manager.IRepositoryManager; +import com.gitblit.manager.IRuntimeManager; +import com.gitblit.manager.IServicesManager; +import com.gitblit.manager.IUserManager; +import com.gitblit.manager.NotificationManager; +import com.gitblit.manager.PluginManager; +import com.gitblit.manager.ProjectManager; +import com.gitblit.manager.RepositoryManager; +import com.gitblit.manager.RuntimeManager; +import com.gitblit.manager.ServicesManager; +import com.gitblit.manager.UserManager; +import com.gitblit.tickets.ITicketService; +import com.gitblit.transport.ssh.IPublicKeyManager; +import com.gitblit.utils.JSoupXssFilter; +import com.gitblit.utils.WorkQueue; +import com.gitblit.utils.XssFilter; +import com.google.inject.AbstractModule; + +/** + * CoreModule references all the core business objects. + * + * @author James Moger + * + */ +public class CoreModule extends AbstractModule { + + @Override + protected void configure() { + + bind(IStoredSettings.class).toInstance(new FileSettings()); + bind(XssFilter.class).to(JSoupXssFilter.class); + + // bind complex providers + bind(IPublicKeyManager.class).toProvider(IPublicKeyManagerProvider.class); + bind(ITicketService.class).toProvider(ITicketServiceProvider.class); + bind(WorkQueue.class).toProvider(WorkQueueProvider.class); + + // core managers + bind(IRuntimeManager.class).to(RuntimeManager.class); + bind(IPluginManager.class).to(PluginManager.class); + bind(INotificationManager.class).to(NotificationManager.class); + bind(IUserManager.class).to(UserManager.class); + bind(IAuthenticationManager.class).to(AuthenticationManager.class); + bind(IRepositoryManager.class).to(RepositoryManager.class); + bind(IProjectManager.class).to(ProjectManager.class); + bind(IFederationManager.class).to(FederationManager.class); + + // the monolithic manager + bind(IGitblit.class).to(GitBlit.class); + + // manager for long-running daemons and services + bind(IServicesManager.class).to(ServicesManager.class); + } +}
\ No newline at end of file diff --git a/src/main/java/com/gitblit/guice/IPublicKeyManagerProvider.java b/src/main/java/com/gitblit/guice/IPublicKeyManagerProvider.java new file mode 100644 index 00000000..8075aa94 --- /dev/null +++ b/src/main/java/com/gitblit/guice/IPublicKeyManagerProvider.java @@ -0,0 +1,72 @@ +/* + * Copyright 2014 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.guice; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.gitblit.IStoredSettings; +import com.gitblit.Keys; +import com.gitblit.manager.IRuntimeManager; +import com.gitblit.transport.ssh.FileKeyManager; +import com.gitblit.transport.ssh.IPublicKeyManager; +import com.gitblit.transport.ssh.NullKeyManager; +import com.gitblit.utils.StringUtils; +import com.google.inject.Inject; +import com.google.inject.Provider; +import com.google.inject.Singleton; + +/** + * Provides a lazily-instantiated IPublicKeyManager configured from IStoredSettings. + * + * @author James Moger + * + */ +@Singleton +public class IPublicKeyManagerProvider implements Provider<IPublicKeyManager> { + + private final Logger logger = LoggerFactory.getLogger(getClass()); + + private final IRuntimeManager runtimeManager; + + private volatile IPublicKeyManager manager; + + @Inject + public IPublicKeyManagerProvider(IRuntimeManager runtimeManager) { + this.runtimeManager = runtimeManager; + } + + @Override + public synchronized IPublicKeyManager get() { + if (manager != null) { + return manager; + } + + IStoredSettings settings = runtimeManager.getSettings(); + String clazz = settings.getString(Keys.git.sshKeysManager, FileKeyManager.class.getName()); + if (StringUtils.isEmpty(clazz)) { + clazz = FileKeyManager.class.getName(); + } + try { + Class<? extends IPublicKeyManager> mgrClass = (Class<? extends IPublicKeyManager>) Class.forName(clazz); + manager = runtimeManager.getInjector().getInstance(mgrClass); + } catch (Exception e) { + logger.error("failed to create public key manager", e); + manager = new NullKeyManager(); + } + return manager; + } +}
\ No newline at end of file diff --git a/src/main/java/com/gitblit/guice/ITicketServiceProvider.java b/src/main/java/com/gitblit/guice/ITicketServiceProvider.java new file mode 100644 index 00000000..fd39955d --- /dev/null +++ b/src/main/java/com/gitblit/guice/ITicketServiceProvider.java @@ -0,0 +1,73 @@ +/* + * Copyright 2014 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.guice; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.gitblit.IStoredSettings; +import com.gitblit.Keys; +import com.gitblit.manager.IRuntimeManager; +import com.gitblit.tickets.ITicketService; +import com.gitblit.tickets.NullTicketService; +import com.gitblit.utils.StringUtils; +import com.google.inject.Inject; +import com.google.inject.Provider; +import com.google.inject.Singleton; + +/** + * Provides a lazily-instantiated ITicketService configured from IStoredSettings. + * + * @author James Moger + * + */ +@Singleton +public class ITicketServiceProvider implements Provider<ITicketService> { + + private final Logger logger = LoggerFactory.getLogger(getClass()); + + private final IRuntimeManager runtimeManager; + + private volatile ITicketService service; + + @Inject + public ITicketServiceProvider(IRuntimeManager runtimeManager) { + this.runtimeManager = runtimeManager; + } + + @Override + public synchronized ITicketService get() { + if (service != null) { + return service; + } + + IStoredSettings settings = runtimeManager.getSettings(); + String clazz = settings.getString(Keys.tickets.service, NullTicketService.class.getName()); + if (StringUtils.isEmpty(clazz)) { + clazz = NullTicketService.class.getName(); + } + + try { + Class<? extends ITicketService> serviceClass = (Class<? extends ITicketService>) Class.forName(clazz); + service = runtimeManager.getInjector().getInstance(serviceClass); + } catch (Exception e) { + logger.error("failed to create ticket service", e); + service = runtimeManager.getInjector().getInstance(NullTicketService.class); + } + + return service; + } +}
\ No newline at end of file diff --git a/src/main/java/com/gitblit/guice/WebModule.java b/src/main/java/com/gitblit/guice/WebModule.java new file mode 100644 index 00000000..5b569182 --- /dev/null +++ b/src/main/java/com/gitblit/guice/WebModule.java @@ -0,0 +1,105 @@ +/* + * Copyright 2014 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.guice; + +import java.util.HashMap; +import java.util.Map; + +import com.gitblit.Constants; +import com.gitblit.servlet.BranchGraphServlet; +import com.gitblit.servlet.DownloadZipFilter; +import com.gitblit.servlet.DownloadZipServlet; +import com.gitblit.servlet.EnforceAuthenticationFilter; +import com.gitblit.servlet.FederationServlet; +import com.gitblit.servlet.GitFilter; +import com.gitblit.servlet.GitServlet; +import com.gitblit.servlet.LogoServlet; +import com.gitblit.servlet.PagesFilter; +import com.gitblit.servlet.PagesServlet; +import com.gitblit.servlet.ProxyFilter; +import com.gitblit.servlet.PtServlet; +import com.gitblit.servlet.RawFilter; +import com.gitblit.servlet.RawServlet; +import com.gitblit.servlet.RobotsTxtServlet; +import com.gitblit.servlet.RpcFilter; +import com.gitblit.servlet.RpcServlet; +import com.gitblit.servlet.SparkleShareInviteServlet; +import com.gitblit.servlet.SyndicationFilter; +import com.gitblit.servlet.SyndicationServlet; +import com.gitblit.wicket.GitblitWicketFilter; +import com.google.common.base.Joiner; +import com.google.inject.servlet.ServletModule; + +/** + * Defines all the web servlets & filters. + * + * @author James Moger + * + */ +public class WebModule extends ServletModule { + + final static String ALL = "/*"; + + @Override + protected void configureServlets() { + // servlets + serve(fuzzy(Constants.R_PATH), fuzzy(Constants.GIT_PATH)).with(GitServlet.class); + serve(fuzzy(Constants.RAW_PATH)).with(RawServlet.class); + serve(fuzzy(Constants.PAGES)).with(PagesServlet.class); + serve(fuzzy(Constants.RPC_PATH)).with(RpcServlet.class); + serve(fuzzy(Constants.ZIP_PATH)).with(DownloadZipServlet.class); + serve(fuzzy(Constants.SYNDICATION_PATH)).with(SyndicationServlet.class); + + serve(fuzzy(Constants.FEDERATION_PATH)).with(FederationServlet.class); + serve(fuzzy(Constants.SPARKLESHARE_INVITE_PATH)).with(SparkleShareInviteServlet.class); + serve(fuzzy(Constants.BRANCH_GRAPH_PATH)).with(BranchGraphServlet.class); + serve(Constants.PT_PATH).with(PtServlet.class); + serve("/robots.txt").with(RobotsTxtServlet.class); + serve("/logo.png").with(LogoServlet.class); + + // global filters + filter(ALL).through(ProxyFilter.class); + filter(ALL).through(EnforceAuthenticationFilter.class); + + // security filters + filter(fuzzy(Constants.R_PATH), fuzzy(Constants.GIT_PATH)).through(GitFilter.class); + filter(fuzzy(Constants.RAW_PATH)).through(RawFilter.class); + filter(fuzzy(Constants.PAGES)).through(PagesFilter.class); + filter(fuzzy(Constants.RPC_PATH)).through(RpcFilter.class); + filter(fuzzy(Constants.ZIP_PATH)).through(DownloadZipFilter.class); + filter(fuzzy(Constants.SYNDICATION_PATH)).through(SyndicationFilter.class); + + // Wicket + String toIgnore = Joiner.on(",").join(Constants.R_PATH, Constants.GIT_PATH, Constants.RAW_PATH, + Constants.PAGES, Constants.RPC_PATH, Constants.ZIP_PATH, Constants.SYNDICATION_PATH, + Constants.FEDERATION_PATH, Constants.SPARKLESHARE_INVITE_PATH, Constants.BRANCH_GRAPH_PATH, + Constants.PT_PATH, "/robots.txt", "/logo.png"); + + Map<String, String> params = new HashMap<String, String>(); + params.put(GitblitWicketFilter.FILTER_MAPPING_PARAM, ALL); + params.put(GitblitWicketFilter.IGNORE_PATHS_PARAM, toIgnore); + filter(ALL).through(GitblitWicketFilter.class, params); + } + + private String fuzzy(String path) { + if (path.endsWith(ALL)) { + return path; + } else if (path.endsWith("/")) { + return path + "*"; + } + return path + ALL; + } +} diff --git a/src/main/java/com/gitblit/guice/WorkQueueProvider.java b/src/main/java/com/gitblit/guice/WorkQueueProvider.java new file mode 100644 index 00000000..cde27ea9 --- /dev/null +++ b/src/main/java/com/gitblit/guice/WorkQueueProvider.java @@ -0,0 +1,57 @@ +/* + * Copyright 2014 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.guice; + +import com.gitblit.IStoredSettings; +import com.gitblit.Keys; +import com.gitblit.manager.IRuntimeManager; +import com.gitblit.utils.IdGenerator; +import com.gitblit.utils.WorkQueue; +import com.google.inject.Inject; +import com.google.inject.Provider; +import com.google.inject.Singleton; + +/** + * Provides a lazily-instantiated WorkQueue configured from IStoredSettings. + * + * @author James Moger + * + */ +@Singleton +public class WorkQueueProvider implements Provider<WorkQueue> { + + private final IRuntimeManager runtimeManager; + + private volatile WorkQueue workQueue; + + @Inject + public WorkQueueProvider(IRuntimeManager runtimeManager) { + this.runtimeManager = runtimeManager; + } + + @Override + public synchronized WorkQueue get() { + if (workQueue != null) { + return workQueue; + } + + IStoredSettings settings = runtimeManager.getSettings(); + int defaultThreadPoolSize = settings.getInteger(Keys.execution.defaultThreadPoolSize, 1); + IdGenerator idGenerator = new IdGenerator(); + workQueue = new WorkQueue(idGenerator, defaultThreadPoolSize); + return workQueue; + } +}
\ No newline at end of file diff --git a/src/main/java/com/gitblit/manager/AuthenticationManager.java b/src/main/java/com/gitblit/manager/AuthenticationManager.java index f98f7b64..29221e6f 100644 --- a/src/main/java/com/gitblit/manager/AuthenticationManager.java +++ b/src/main/java/com/gitblit/manager/AuthenticationManager.java @@ -35,6 +35,7 @@ import org.slf4j.LoggerFactory; import com.gitblit.Constants; import com.gitblit.Constants.AccountType; import com.gitblit.Constants.AuthenticationType; +import com.gitblit.Constants.Role; import com.gitblit.IStoredSettings; import com.gitblit.Keys; import com.gitblit.auth.AuthenticationProvider; @@ -52,6 +53,8 @@ import com.gitblit.utils.Base64; import com.gitblit.utils.HttpUtils; import com.gitblit.utils.StringUtils; import com.gitblit.utils.X509Utils.X509Metadata; +import com.google.inject.Inject; +import com.google.inject.Singleton; /** * The authentication manager handles user login & logout. @@ -59,6 +62,7 @@ import com.gitblit.utils.X509Utils.X509Metadata; * @author James Moger * */ +@Singleton public class AuthenticationManager implements IAuthenticationManager { private final Logger logger = LoggerFactory.getLogger(getClass()); @@ -75,6 +79,7 @@ public class AuthenticationManager implements IAuthenticationManager { private final Map<String, String> legacyRedirects; + @Inject public AuthenticationManager( IRuntimeManager runtimeManager, IUserManager userManager) { @@ -576,6 +581,28 @@ public class AuthenticationManager implements IAuthenticationManager { return (team != null && team.isLocalTeam()) || findProvider(team).supportsTeamMembershipChanges(); } + /** + * Returns true if the user's role can be changed. + * + * @param user + * @return true if the user's role can be changed + */ + @Override + public boolean supportsRoleChanges(UserModel user, Role role) { + return (user != null && user.isLocalAccount()) || findProvider(user).supportsRoleChanges(user, role); + } + + /** + * Returns true if the team's role can be changed. + * + * @param user + * @return true if the team's role can be changed + */ + @Override + public boolean supportsRoleChanges(TeamModel team, Role role) { + return (team != null && team.isLocalTeam()) || findProvider(team).supportsRoleChanges(team, role); + } + protected AuthenticationProvider findProvider(UserModel user) { for (AuthenticationProvider provider : authenticationProviders) { if (provider.getAccountType().equals(user.accountType)) { diff --git a/src/main/java/com/gitblit/manager/FederationManager.java b/src/main/java/com/gitblit/manager/FederationManager.java index 95d38af1..f009c1c8 100644 --- a/src/main/java/com/gitblit/manager/FederationManager.java +++ b/src/main/java/com/gitblit/manager/FederationManager.java @@ -45,6 +45,8 @@ import com.gitblit.utils.Base64; import com.gitblit.utils.FederationUtils; import com.gitblit.utils.JsonUtils; import com.gitblit.utils.StringUtils; +import com.google.inject.Inject; +import com.google.inject.Singleton; /** * Federation manager controls all aspects of handling federation sets, tokens, @@ -53,6 +55,7 @@ import com.gitblit.utils.StringUtils; * @author James Moger * */ +@Singleton public class FederationManager implements IFederationManager { private final Logger logger = LoggerFactory.getLogger(getClass()); @@ -70,6 +73,7 @@ public class FederationManager implements IFederationManager { private final IRepositoryManager repositoryManager; + @Inject public FederationManager( IRuntimeManager runtimeManager, INotificationManager notificationManager, diff --git a/src/main/java/com/gitblit/manager/GitblitManager.java b/src/main/java/com/gitblit/manager/GitblitManager.java index 88fa804e..9692e654 100644 --- a/src/main/java/com/gitblit/manager/GitblitManager.java +++ b/src/main/java/com/gitblit/manager/GitblitManager.java @@ -49,12 +49,11 @@ import ro.fortsoft.pf4j.Version; import com.gitblit.Constants; import com.gitblit.Constants.AccessPermission; -import com.gitblit.Constants.AccessRestrictionType; import com.gitblit.Constants.FederationRequest; import com.gitblit.Constants.FederationToken; +import com.gitblit.Constants.Role; import com.gitblit.GitBlitException; import com.gitblit.IStoredSettings; -import com.gitblit.Keys; import com.gitblit.models.FederationModel; import com.gitblit.models.FederationProposal; import com.gitblit.models.FederationSet; @@ -68,7 +67,6 @@ import com.gitblit.models.PluginRegistry.PluginRelease; import com.gitblit.models.ProjectModel; import com.gitblit.models.RegistrantAccessPermission; import com.gitblit.models.RepositoryModel; -import com.gitblit.models.RepositoryUrl; import com.gitblit.models.SearchResult; import com.gitblit.models.ServerSettings; import com.gitblit.models.ServerStatus; @@ -79,7 +77,6 @@ import com.gitblit.tickets.ITicketService; import com.gitblit.transport.ssh.IPublicKeyManager; import com.gitblit.transport.ssh.SshKey; import com.gitblit.utils.ArrayUtils; -import com.gitblit.utils.HttpUtils; import com.gitblit.utils.JsonUtils; import com.gitblit.utils.ObjectCache; import com.gitblit.utils.StringUtils; @@ -88,6 +85,10 @@ import com.google.gson.Gson; import com.google.gson.JsonIOException; import com.google.gson.JsonSyntaxException; import com.google.gson.reflect.TypeToken; +import com.google.inject.Inject; +import com.google.inject.Injector; +import com.google.inject.Provider; +import com.google.inject.Singleton; /** * GitblitManager is an aggregate interface delegate. It implements all the manager @@ -101,12 +102,17 @@ import com.google.gson.reflect.TypeToken; * @author James Moger * */ +@Singleton public class GitblitManager implements IGitblit { protected final Logger logger = LoggerFactory.getLogger(getClass()); protected final ObjectCache<Collection<GitClientApplication>> clientApplications = new ObjectCache<Collection<GitClientApplication>>(); + protected final Provider<IPublicKeyManager> publicKeyManagerProvider; + + protected final Provider<ITicketService> ticketServiceProvider; + protected final IStoredSettings settings; protected final IRuntimeManager runtimeManager; @@ -119,32 +125,34 @@ public class GitblitManager implements IGitblit { protected final IAuthenticationManager authenticationManager; - protected final IPublicKeyManager publicKeyManager; - protected final IRepositoryManager repositoryManager; protected final IProjectManager projectManager; protected final IFederationManager federationManager; + @Inject public GitblitManager( + Provider<IPublicKeyManager> publicKeyManagerProvider, + Provider<ITicketService> ticketServiceProvider, IRuntimeManager runtimeManager, IPluginManager pluginManager, INotificationManager notificationManager, IUserManager userManager, IAuthenticationManager authenticationManager, - IPublicKeyManager publicKeyManager, IRepositoryManager repositoryManager, IProjectManager projectManager, IFederationManager federationManager) { + this.publicKeyManagerProvider = publicKeyManagerProvider; + this.ticketServiceProvider = ticketServiceProvider; + this.settings = runtimeManager.getSettings(); this.runtimeManager = runtimeManager; this.pluginManager = pluginManager; this.notificationManager = notificationManager; this.userManager = userManager; this.authenticationManager = authenticationManager; - this.publicKeyManager = publicKeyManager; this.repositoryManager = repositoryManager; this.projectManager = projectManager; this.federationManager = federationManager; @@ -358,66 +366,6 @@ public class GitblitManager implements IGitblit { } /** - * Returns a list of repository URLs and the user access permission. - * - * @param request - * @param user - * @param repository - * @return a list of repository urls - */ - @Override - public List<RepositoryUrl> getRepositoryUrls(HttpServletRequest request, UserModel user, RepositoryModel repository) { - if (user == null) { - user = UserModel.ANONYMOUS; - } - String username = StringUtils.encodeUsername(UserModel.ANONYMOUS.equals(user) ? "" : user.username); - - List<RepositoryUrl> list = new ArrayList<RepositoryUrl>(); - // http/https url - if (settings.getBoolean(Keys.git.enableGitServlet, true)) { - AccessPermission permission = user.getRepositoryPermission(repository).permission; - if (permission.exceeds(AccessPermission.NONE)) { - list.add(new RepositoryUrl(getRepositoryUrl(request, username, repository), permission)); - } - } - - // add all other urls - // {0} = repository - // {1} = username - for (String url : settings.getStrings(Keys.web.otherUrls)) { - if (url.contains("{1}")) { - // external url requires username, only add url IF we have one - if (!StringUtils.isEmpty(username)) { - list.add(new RepositoryUrl(MessageFormat.format(url, repository.name, username), null)); - } - } else { - // external url does not require username - list.add(new RepositoryUrl(MessageFormat.format(url, repository.name), null)); - } - } - return list; - } - - protected String getRepositoryUrl(HttpServletRequest request, String username, RepositoryModel repository) { - String gitblitUrl = settings.getString(Keys.web.canonicalUrl, null); - if (StringUtils.isEmpty(gitblitUrl)) { - gitblitUrl = HttpUtils.getGitblitURL(request); - } - StringBuilder sb = new StringBuilder(); - sb.append(gitblitUrl); - sb.append(Constants.R_PATH); - sb.append(repository.name); - - // inject username into repository url if authentication is required - if (repository.accessRestriction.exceeds(AccessRestrictionType.NONE) - && !StringUtils.isEmpty(username)) { - sb.insert(sb.indexOf("://") + 3, username + "@"); - } - return sb.toString(); - } - - - /** * Returns the list of custom client applications to be used for the * repository url panel; * @@ -492,7 +440,7 @@ public class GitblitManager implements IGitblit { // Read bundled Gitblit properties to extract setting descriptions. // This copy is pristine and only used for populating the setting // models map. - InputStream is = GitblitManager.class.getResourceAsStream("/reference.properties"); + InputStream is = GitblitManager.class.getResourceAsStream("/defaults.properties"); BufferedReader propertiesReader = new BufferedReader(new InputStreamReader(is)); StringBuilder description = new StringBuilder(); SettingModel setting = new SettingModel(); @@ -537,24 +485,20 @@ public class GitblitManager implements IGitblit { } propertiesReader.close(); } catch (NullPointerException e) { - logger.error("Failed to find resource copy of gitblit.properties"); + logger.error("Failed to find classpath resource 'defaults.properties'"); } catch (IOException e) { - logger.error("Failed to load resource copy of gitblit.properties"); + logger.error("Failed to load classpath resource 'defaults.properties'"); } } - /** - * Throws an exception if trying to get a ticket service. - * - */ @Override public ITicketService getTicketService() { - throw new RuntimeException("This class does not have a ticket service!"); + return ticketServiceProvider.get(); } @Override public IPublicKeyManager getPublicKeyManager() { - return publicKeyManager; + return publicKeyManagerProvider.get(); } /* @@ -605,26 +549,6 @@ public class GitblitManager implements IGitblit { } @Override - public boolean isServingRepositories() { - return runtimeManager.isServingRepositories(); - } - - @Override - public boolean isServingHTTP() { - return runtimeManager.isServingHTTP(); - } - - @Override - public boolean isServingGIT() { - return runtimeManager.isServingGIT(); - } - - @Override - public boolean isServingSSH() { - return runtimeManager.isServingSSH(); - } - - @Override public TimeZone getTimezone() { return runtimeManager.getTimezone(); } @@ -665,6 +589,11 @@ public class GitblitManager implements IGitblit { } @Override + public Injector getInjector() { + return runtimeManager.getInjector(); + } + + @Override public XssFilter getXssFilter() { return runtimeManager.getXssFilter(); } @@ -782,6 +711,16 @@ public class GitblitManager implements IGitblit { return authenticationManager.supportsTeamMembershipChanges(team); } + @Override + public boolean supportsRoleChanges(UserModel user, Role role) { + return authenticationManager.supportsRoleChanges(user, role); + } + + @Override + public boolean supportsRoleChanges(TeamModel team, Role role) { + return authenticationManager.supportsRoleChanges(team, role); + } + /* * USER MANAGER */ @@ -806,11 +745,6 @@ public class GitblitManager implements IGitblit { } @Override - public boolean deleteUser(String username) { - return userManager.deleteUser(username); - } - - @Override public UserModel getUserModel(String username) { return userManager.getUserModel(username); } @@ -851,8 +785,22 @@ public class GitblitManager implements IGitblit { } @Override + public boolean deleteUser(String username) { + // delegate to deleteUserModel() to delete public ssh keys + UserModel user = userManager.getUserModel(username); + return deleteUserModel(user); + } + + /** + * Delete the user and all associated public ssh keys. + */ + @Override public boolean deleteUserModel(UserModel model) { - return userManager.deleteUserModel(model); + boolean success = userManager.deleteUserModel(model); + if (success) { + getPublicKeyManager().removeAllKeys(model.username); + } + return success; } @Override @@ -1053,10 +1001,23 @@ public class GitblitManager implements IGitblit { return repositoryManager.getRepositoryDefaultMetrics(model, repository); } + /** + * Detect renames and reindex as appropriate. + */ @Override public void updateRepositoryModel(String repositoryName, RepositoryModel repository, boolean isCreate) throws GitBlitException { + RepositoryModel oldModel = null; + boolean isRename = !isCreate && !repositoryName.equalsIgnoreCase(repository.name); + if (isRename) { + oldModel = repositoryManager.getRepositoryModel(repositoryName); + } + repositoryManager.updateRepositoryModel(repositoryName, repository, isCreate); + + if (isRename && ticketServiceProvider.get() != null) { + ticketServiceProvider.get().rename(oldModel, repository); + } } @Override @@ -1069,14 +1030,23 @@ public class GitblitManager implements IGitblit { return repositoryManager.canDelete(model); } + /** + * Delete the repository and all associated tickets. + */ @Override public boolean deleteRepositoryModel(RepositoryModel model) { - return repositoryManager.deleteRepositoryModel(model); + boolean success = repositoryManager.deleteRepositoryModel(model); + if (success && ticketServiceProvider.get() != null) { + ticketServiceProvider.get().deleteAll(model); + } + return success; } @Override public boolean deleteRepository(String repositoryName) { - return repositoryManager.deleteRepository(repositoryName); + // delegate to deleteRepositoryModel() to destroy indexed tickets + RepositoryModel repository = repositoryManager.getRepositoryModel(repositoryName); + return deleteRepositoryModel(repository); } @Override diff --git a/src/main/java/com/gitblit/manager/IAuthenticationManager.java b/src/main/java/com/gitblit/manager/IAuthenticationManager.java index 3600b325..d48ec534 100644 --- a/src/main/java/com/gitblit/manager/IAuthenticationManager.java +++ b/src/main/java/com/gitblit/manager/IAuthenticationManager.java @@ -18,6 +18,7 @@ package com.gitblit.manager; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import com.gitblit.Constants.Role; import com.gitblit.models.TeamModel; import com.gitblit.models.UserModel; import com.gitblit.transport.ssh.SshKey; @@ -161,4 +162,22 @@ public interface IAuthenticationManager extends IManager { */ boolean supportsTeamMembershipChanges(TeamModel team); + /** + * Returns true if the specified role can be changed. + * + * @param user + * @return true if the specified role can be changed + * @since 1.6.1 + */ + boolean supportsRoleChanges(UserModel user, Role role); + + /** + * Returns true if the specified role can be changed. + * + * @param team + * @return true if the specified role can be changed + * @since 1.6.1 + */ + boolean supportsRoleChanges(TeamModel team, Role role); + }
\ No newline at end of file diff --git a/src/main/java/com/gitblit/manager/IGitblit.java b/src/main/java/com/gitblit/manager/IGitblit.java index 50ec8b1f..6c5b374c 100644 --- a/src/main/java/com/gitblit/manager/IGitblit.java +++ b/src/main/java/com/gitblit/manager/IGitblit.java @@ -16,14 +16,10 @@ package com.gitblit.manager; import java.util.Collection; -import java.util.List; - -import javax.servlet.http.HttpServletRequest; import com.gitblit.GitBlitException; import com.gitblit.models.GitClientApplication; import com.gitblit.models.RepositoryModel; -import com.gitblit.models.RepositoryUrl; import com.gitblit.models.TeamModel; import com.gitblit.models.UserModel; import com.gitblit.tickets.ITicketService; @@ -40,17 +36,6 @@ public interface IGitblit extends IManager, IFederationManager { /** - * Returns a list of repository URLs and the user access permission. - * - * @param request - * @param user - * @param repository - * @return a list of repository urls - * @since 1.4.0 - */ - List<RepositoryUrl> getRepositoryUrls(HttpServletRequest request, UserModel user, RepositoryModel repository); - - /** * Creates a complete user object. * * @param user diff --git a/src/main/java/com/gitblit/manager/IRuntimeManager.java b/src/main/java/com/gitblit/manager/IRuntimeManager.java index 132534c3..2203b7ff 100644 --- a/src/main/java/com/gitblit/manager/IRuntimeManager.java +++ b/src/main/java/com/gitblit/manager/IRuntimeManager.java @@ -25,9 +25,12 @@ import com.gitblit.IStoredSettings; import com.gitblit.models.ServerSettings; import com.gitblit.models.ServerStatus; import com.gitblit.utils.XssFilter; +import com.google.inject.Injector; public interface IRuntimeManager extends IManager { + Injector getInjector(); + void setBaseFolder(File folder); File getBaseFolder(); @@ -49,42 +52,6 @@ public interface IRuntimeManager extends IManager { Locale getLocale(); /** - * Determine if this Gitblit instance is actively serving git repositories - * or if it is merely a repository viewer. - * - * @return true if Gitblit is serving repositories - * @since 1.4.0 - */ - boolean isServingRepositories(); - - /** - * Determine if this Gitblit instance is actively serving git repositories - * over HTTP. - * - * @return true if Gitblit is serving repositories over HTTP - * @since 1.6.0 - */ - boolean isServingHTTP(); - - /** - * Determine if this Gitblit instance is actively serving git repositories - * over the GIT Daemon protocol. - * - * @return true if Gitblit is serving repositories over the GIT Daemon protocol - * @since 1.6.0 - */ - boolean isServingGIT(); - - /** - * Determine if this Gitblit instance is actively serving git repositories - * over the SSH protocol. - * - * @return true if Gitblit is serving repositories over the SSH protocol - * @since 1.6.0 - */ - boolean isServingSSH(); - - /** * Determine if this Gitblit instance is running in debug mode * * @return true if Gitblit is running in debug mode diff --git a/src/main/java/com/gitblit/manager/IServicesManager.java b/src/main/java/com/gitblit/manager/IServicesManager.java new file mode 100644 index 00000000..b3a973b3 --- /dev/null +++ b/src/main/java/com/gitblit/manager/IServicesManager.java @@ -0,0 +1,94 @@ +/* + * Copyright 2014 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.manager; + +import java.util.List; + +import javax.servlet.http.HttpServletRequest; + +import com.gitblit.Constants.Transport; +import com.gitblit.models.RepositoryModel; +import com.gitblit.models.RepositoryUrl; +import com.gitblit.models.UserModel; + +public interface IServicesManager extends IManager { + + /** + * Determine if this Gitblit instance is actively serving git repositories + * or if it is merely a repository viewer. + * + * @return true if Gitblit is serving repositories + * @since 1.7.0 + */ + boolean isServingRepositories(); + + /** + * Determine if this Gitblit instance is actively serving git repositories + * over HTTP. + * + * @return true if Gitblit is serving repositories over HTTP + * @since 1.7.0 + */ + boolean isServingHTTP(); + + /** + * Determine if this Gitblit instance is actively serving git repositories + * over HTTP. + * + * @return true if Gitblit is serving repositories over HTTPS + * @since 1.7.0 + */ + boolean isServingHTTPS(); + + /** + * Determine if this Gitblit instance is actively serving git repositories + * over the GIT Daemon protocol. + * + * @return true if Gitblit is serving repositories over the GIT Daemon protocol + * @since 1.7.0 + */ + boolean isServingGIT(); + + /** + * Determine if this Gitblit instance is actively serving git repositories + * over the SSH protocol. + * + * @return true if Gitblit is serving repositories over the SSH protocol + * @since 1.7.0 + */ + boolean isServingSSH(); + + /** + * Returns a list of repository URLs and the user access permission. + * + * @param request + * @param user + * @param repository + * @return a list of repository urls + * @since 1.7.0 + */ + List<RepositoryUrl> getRepositoryUrls(HttpServletRequest request, UserModel user, RepositoryModel repository); + + /** + * Returns true if the transport may be used for pushing. + * + * @param byTransport + * @return true if the transport can be used for pushes. + * @since 1.7.0 + */ + boolean acceptsPush(Transport byTransport); + +}
\ No newline at end of file diff --git a/src/main/java/com/gitblit/manager/NotificationManager.java b/src/main/java/com/gitblit/manager/NotificationManager.java index 69a611bb..4bbc2abf 100644 --- a/src/main/java/com/gitblit/manager/NotificationManager.java +++ b/src/main/java/com/gitblit/manager/NotificationManager.java @@ -29,6 +29,8 @@ import com.gitblit.IStoredSettings; import com.gitblit.Keys; import com.gitblit.models.Mailing; import com.gitblit.service.MailService; +import com.google.inject.Inject; +import com.google.inject.Singleton; /** * The notification manager dispatches notifications. Currently, email is the @@ -38,6 +40,7 @@ import com.gitblit.service.MailService; * @author James Moger * */ +@Singleton public class NotificationManager implements INotificationManager { private final Logger logger = LoggerFactory.getLogger(getClass()); @@ -48,6 +51,7 @@ public class NotificationManager implements INotificationManager { private final MailService mailService; + @Inject public NotificationManager(IStoredSettings settings) { this.settings = settings; this.mailService = new MailService(settings); diff --git a/src/main/java/com/gitblit/manager/PluginManager.java b/src/main/java/com/gitblit/manager/PluginManager.java index 5e25caa1..bc3be52f 100644 --- a/src/main/java/com/gitblit/manager/PluginManager.java +++ b/src/main/java/com/gitblit/manager/PluginManager.java @@ -22,6 +22,7 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; +import java.net.InetSocketAddress; import java.net.Proxy; import java.net.URL; import java.net.URLConnection; @@ -37,8 +38,12 @@ import java.util.TreeMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import ro.fortsoft.pf4j.DefaultPluginFactory; import ro.fortsoft.pf4j.DefaultPluginManager; +import ro.fortsoft.pf4j.ExtensionFactory; +import ro.fortsoft.pf4j.Plugin; import ro.fortsoft.pf4j.PluginClassLoader; +import ro.fortsoft.pf4j.PluginFactory; import ro.fortsoft.pf4j.PluginState; import ro.fortsoft.pf4j.PluginStateEvent; import ro.fortsoft.pf4j.PluginStateListener; @@ -58,6 +63,8 @@ import com.gitblit.utils.JsonUtils; import com.gitblit.utils.StringUtils; import com.google.common.io.Files; import com.google.common.io.InputSupplier; +import com.google.inject.Inject; +import com.google.inject.Singleton; /** * The plugin manager maintains the lifecycle of plugins. It is exposed as @@ -68,32 +75,23 @@ import com.google.common.io.InputSupplier; * @author James Moger * */ +@Singleton public class PluginManager implements IPluginManager, PluginStateListener { private final Logger logger = LoggerFactory.getLogger(getClass()); - private final DefaultPluginManager pf4j; - private final IRuntimeManager runtimeManager; + private DefaultPluginManager pf4j; + // timeout defaults of Maven 3.0.4 in seconds private int connectTimeout = 20; private int readTimeout = 12800; + @Inject public PluginManager(IRuntimeManager runtimeManager) { - File dir = runtimeManager.getFileOrFolder(Keys.plugins.folder, "${baseFolder}/plugins"); - dir.mkdirs(); this.runtimeManager = runtimeManager; - - this.pf4j = new DefaultPluginManager(dir); - - try { - Version systemVersion = Version.createVersion(Constants.getVersion()); - pf4j.setSystemVersion(systemVersion); - } catch (Exception e) { - logger.error(null, e); - } } @Override @@ -108,6 +106,28 @@ public class PluginManager implements IPluginManager, PluginStateListener { @Override public PluginManager start() { + File dir = runtimeManager.getFileOrFolder(Keys.plugins.folder, "${baseFolder}/plugins"); + dir.mkdirs(); + + pf4j = new DefaultPluginManager(dir) { + + @Override + protected PluginFactory createPluginFactory() { + return new GuicePluginFactory(); + } + + @Override + protected ExtensionFactory createExtensionFactory() { + return new GuiceExtensionFactory(); + } + }; + + try { + Version systemVersion = Version.createVersion(Constants.getVersion()); + pf4j.setSystemVersion(systemVersion); + } catch (Exception e) { + logger.error(null, e); + } pf4j.loadPlugins(); logger.debug("Starting plugins"); pf4j.startPlugins(); @@ -438,7 +458,7 @@ public class PluginManager implements IPluginManager, PluginStateListener { } - if (sha1File == null && md5File == null && verifyChecksum) { + if (sha1File == null && md5File == null) { throw new IOException("Missing SHA1 and MD5 checksums for " + url); } @@ -567,10 +587,55 @@ public class PluginManager implements IPluginManager, PluginStateListener { } protected Proxy getProxy(URL url) { - return java.net.Proxy.NO_PROXY; + String proxyHost = runtimeManager.getSettings().getString(Keys.plugins.httpProxyHost, ""); + String proxyPort = runtimeManager.getSettings().getString(Keys.plugins.httpProxyPort, ""); + + if (!StringUtils.isEmpty(proxyHost) && !StringUtils.isEmpty(proxyPort)) { + return new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, Integer.parseInt(proxyPort))); + } else { + return java.net.Proxy.NO_PROXY; + } } protected String getProxyAuthorization(URL url) { - return ""; + String proxyAuth = runtimeManager.getSettings().getString(Keys.plugins.httpProxyAuthorization, ""); + return proxyAuth; + } + + /** + * Instantiates a plugin using pf4j but injects member fields + * with Guice. + */ + private class GuicePluginFactory extends DefaultPluginFactory { + + @Override + public Plugin create(PluginWrapper pluginWrapper) { + // use pf4j to create the plugin + Plugin plugin = super.create(pluginWrapper); + + if (plugin != null) { + // allow Guice to inject member fields + runtimeManager.getInjector().injectMembers(plugin); + } + + return plugin; + } + } + + /** + * Instantiates an extension using Guice. + */ + private class GuiceExtensionFactory implements ExtensionFactory { + @Override + public Object create(Class<?> extensionClass) { + // instantiate && inject the extension + logger.debug("Create instance for extension '{}'", extensionClass.getName()); + try { + return runtimeManager.getInjector().getInstance(extensionClass); + } catch (Exception e) { + logger.error(e.getMessage(), e); + } + return null; + } } } diff --git a/src/main/java/com/gitblit/manager/ProjectManager.java b/src/main/java/com/gitblit/manager/ProjectManager.java index 666f5210..ae46bdfc 100644 --- a/src/main/java/com/gitblit/manager/ProjectManager.java +++ b/src/main/java/com/gitblit/manager/ProjectManager.java @@ -41,6 +41,8 @@ import com.gitblit.utils.DeepCopier; import com.gitblit.utils.ModelUtils; import com.gitblit.utils.ObjectCache; import com.gitblit.utils.StringUtils; +import com.google.inject.Inject; +import com.google.inject.Singleton; /** * Project manager handles project-related functions. @@ -48,6 +50,7 @@ import com.gitblit.utils.StringUtils; * @author James Moger * */ +@Singleton public class ProjectManager implements IProjectManager { private final Logger logger = LoggerFactory.getLogger(getClass()); @@ -68,6 +71,7 @@ public class ProjectManager implements IProjectManager { private FileBasedConfig projectConfigs; + @Inject public ProjectManager( IRuntimeManager runtimeManager, IUserManager userManager, diff --git a/src/main/java/com/gitblit/manager/RepositoryManager.java b/src/main/java/com/gitblit/manager/RepositoryManager.java index 2db41323..c2c6088e 100644 --- a/src/main/java/com/gitblit/manager/RepositoryManager.java +++ b/src/main/java/com/gitblit/manager/RepositoryManager.java @@ -91,6 +91,8 @@ import com.gitblit.utils.ModelUtils; import com.gitblit.utils.ObjectCache; import com.gitblit.utils.StringUtils; import com.gitblit.utils.TimeUtils; +import com.google.inject.Inject; +import com.google.inject.Singleton; /** * Repository manager creates, updates, deletes and caches git repositories. It @@ -99,6 +101,7 @@ import com.gitblit.utils.TimeUtils; * @author James Moger * */ +@Singleton public class RepositoryManager implements IRepositoryManager { private final Logger logger = LoggerFactory.getLogger(getClass()); @@ -121,7 +124,7 @@ public class RepositoryManager implements IRepositoryManager { private final IUserManager userManager; - private final File repositoriesFolder; + private File repositoriesFolder; private LuceneService luceneExecutor; @@ -129,6 +132,7 @@ public class RepositoryManager implements IRepositoryManager { private MirrorService mirrorExecutor; + @Inject public RepositoryManager( IRuntimeManager runtimeManager, IPluginManager pluginManager, @@ -138,11 +142,11 @@ public class RepositoryManager implements IRepositoryManager { this.runtimeManager = runtimeManager; this.pluginManager = pluginManager; this.userManager = userManager; - this.repositoriesFolder = runtimeManager.getFileOrFolder(Keys.git.repositoriesFolder, "${baseFolder}/git"); } @Override public RepositoryManager start() { + repositoriesFolder = runtimeManager.getFileOrFolder(Keys.git.repositoriesFolder, "${baseFolder}/git"); logger.info("Repositories folder : {}", repositoriesFolder.getAbsolutePath()); // initialize utilities @@ -1967,21 +1971,19 @@ public class RepositoryManager implements IRepositoryManager { } protected void confirmWriteAccess() { - if (runtimeManager.isServingRepositories()) { - try { - if (!getRepositoriesFolder().exists()) { - getRepositoriesFolder().mkdirs(); - } - File file = File.createTempFile(".test-", ".txt", getRepositoriesFolder()); - file.delete(); - } catch (Exception e) { - logger.error(""); - logger.error(Constants.BORDER2); - logger.error("Please check filesystem permissions!"); - logger.error("FAILED TO WRITE TO REPOSITORIES FOLDER!!", e); - logger.error(Constants.BORDER2); - logger.error(""); + try { + if (!getRepositoriesFolder().exists()) { + getRepositoriesFolder().mkdirs(); } + File file = File.createTempFile(".test-", ".txt", getRepositoriesFolder()); + file.delete(); + } catch (Exception e) { + logger.error(""); + logger.error(Constants.BORDER2); + logger.error("Please check filesystem permissions!"); + logger.error("FAILED TO WRITE TO REPOSITORIES FOLDER!!", e); + logger.error(Constants.BORDER2); + logger.error(""); } } } diff --git a/src/main/java/com/gitblit/manager/RuntimeManager.java b/src/main/java/com/gitblit/manager/RuntimeManager.java index 219bf801..18d6b9c2 100644 --- a/src/main/java/com/gitblit/manager/RuntimeManager.java +++ b/src/main/java/com/gitblit/manager/RuntimeManager.java @@ -33,7 +33,11 @@ import com.gitblit.models.ServerStatus; import com.gitblit.models.SettingModel; import com.gitblit.utils.StringUtils; import com.gitblit.utils.XssFilter; +import com.google.inject.Inject; +import com.google.inject.Injector; +import com.google.inject.Singleton; +@Singleton public class RuntimeManager implements IRuntimeManager { private final Logger logger = LoggerFactory.getLogger(getClass()); @@ -50,6 +54,10 @@ public class RuntimeManager implements IRuntimeManager { private TimeZone timezone; + @Inject + private Injector injector; + + @Inject public RuntimeManager(IStoredSettings settings, XssFilter xssFilter) { this(settings, xssFilter, null); } @@ -79,6 +87,11 @@ public class RuntimeManager implements IRuntimeManager { } @Override + public Injector getInjector() { + return injector; + } + + @Override public File getBaseFolder() { return baseFolder; } @@ -116,52 +129,6 @@ public class RuntimeManager implements IRuntimeManager { } /** - * Determine if this Gitblit instance is actively serving git repositories - * or if it is merely a repository viewer. - * - * @return true if Gitblit is serving repositories - */ - @Override - public boolean isServingRepositories() { - return isServingHTTP() - || isServingGIT() - || isServingSSH(); - } - - /** - * Determine if this Gitblit instance is actively serving git repositories - * over the HTTP protocol. - * - * @return true if Gitblit is serving repositories over the HTTP protocol - */ - @Override - public boolean isServingHTTP() { - return settings.getBoolean(Keys.git.enableGitServlet, true); - } - - /** - * Determine if this Gitblit instance is actively serving git repositories - * over the Git Daemon protocol. - * - * @return true if Gitblit is serving repositories over the Git Daemon protocol - */ - @Override - public boolean isServingGIT() { - return settings.getInteger(Keys.git.daemonPort, 0) > 0; - } - - /** - * Determine if this Gitblit instance is actively serving git repositories - * over the SSH protocol. - * - * @return true if Gitblit is serving repositories over the SSH protocol - */ - @Override - public boolean isServingSSH() { - return settings.getInteger(Keys.git.sshPort, 0) > 0; - } - - /** * Returns the preferred timezone for the Gitblit instance. * * @return a timezone diff --git a/src/main/java/com/gitblit/manager/ServicesManager.java b/src/main/java/com/gitblit/manager/ServicesManager.java index 437fd106..69e5e408 100644 --- a/src/main/java/com/gitblit/manager/ServicesManager.java +++ b/src/main/java/com/gitblit/manager/ServicesManager.java @@ -18,9 +18,15 @@ package com.gitblit.manager; import java.io.IOException; import java.net.URI; import java.text.MessageFormat; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; import java.util.Date; +import java.util.HashSet; +import java.util.Iterator; import java.util.List; +import java.util.Set; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; @@ -30,9 +36,11 @@ import javax.servlet.http.HttpServletRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.gitblit.Constants; import com.gitblit.Constants.AccessPermission; import com.gitblit.Constants.AccessRestrictionType; import com.gitblit.Constants.FederationToken; +import com.gitblit.Constants.Transport; import com.gitblit.IStoredSettings; import com.gitblit.Keys; import com.gitblit.fanout.FanoutNioService; @@ -40,14 +48,18 @@ import com.gitblit.fanout.FanoutService; import com.gitblit.fanout.FanoutSocketService; import com.gitblit.models.FederationModel; import com.gitblit.models.RepositoryModel; +import com.gitblit.models.RepositoryUrl; import com.gitblit.models.UserModel; import com.gitblit.service.FederationPullService; import com.gitblit.transport.git.GitDaemon; import com.gitblit.transport.ssh.SshDaemon; -import com.gitblit.utils.IdGenerator; +import com.gitblit.utils.HttpUtils; import com.gitblit.utils.StringUtils; import com.gitblit.utils.TimeUtils; import com.gitblit.utils.WorkQueue; +import com.google.inject.Inject; +import com.google.inject.Provider; +import com.google.inject.Singleton; /** * Services manager manages long-running services/processes that either have no @@ -57,32 +69,35 @@ import com.gitblit.utils.WorkQueue; * @author James Moger * */ -public class ServicesManager implements IManager { +@Singleton +public class ServicesManager implements IServicesManager { private final Logger logger = LoggerFactory.getLogger(getClass()); private final ScheduledExecutorService scheduledExecutor = Executors.newScheduledThreadPool(5); + private final Provider<WorkQueue> workQueueProvider; + private final IStoredSettings settings; private final IGitblit gitblit; - private final IdGenerator idGenerator; - - private final WorkQueue workQueue; - private FanoutService fanoutService; private GitDaemon gitDaemon; private SshDaemon sshDaemon; - public ServicesManager(IGitblit gitblit) { - this.settings = gitblit.getSettings(); + @Inject + public ServicesManager( + Provider<WorkQueue> workQueueProvider, + IStoredSettings settings, + IGitblit gitblit) { + + this.workQueueProvider = workQueueProvider; + + this.settings = settings; this.gitblit = gitblit; - int defaultThreadPoolSize = settings.getInteger(Keys.execution.defaultThreadPoolSize, 1); - this.idGenerator = new IdGenerator(); - this.workQueue = new WorkQueue(idGenerator, defaultThreadPoolSize); } @Override @@ -107,24 +122,194 @@ public class ServicesManager implements IManager { if (sshDaemon != null) { sshDaemon.stop(); } - workQueue.stop(); + workQueueProvider.get().stop(); return this; } + protected String getRepositoryUrl(HttpServletRequest request, String username, RepositoryModel repository) { + String gitblitUrl = settings.getString(Keys.web.canonicalUrl, null); + if (StringUtils.isEmpty(gitblitUrl)) { + gitblitUrl = HttpUtils.getGitblitURL(request); + } + StringBuilder sb = new StringBuilder(); + sb.append(gitblitUrl); + sb.append(Constants.R_PATH); + sb.append(repository.name); + + // inject username into repository url if authentication is required + if (repository.accessRestriction.exceeds(AccessRestrictionType.NONE) + && !StringUtils.isEmpty(username)) { + sb.insert(sb.indexOf("://") + 3, username + "@"); + } + return sb.toString(); + } + + /** + * Returns a list of repository URLs and the user access permission. + * + * @param request + * @param user + * @param repository + * @return a list of repository urls + */ + @Override + public List<RepositoryUrl> getRepositoryUrls(HttpServletRequest request, UserModel user, RepositoryModel repository) { + if (user == null) { + user = UserModel.ANONYMOUS; + } + String username = StringUtils.encodeUsername(UserModel.ANONYMOUS.equals(user) ? "" : user.username); + + List<RepositoryUrl> list = new ArrayList<RepositoryUrl>(); + + // http/https url + if (settings.getBoolean(Keys.git.enableGitServlet, true)) { + AccessPermission permission = user.getRepositoryPermission(repository).permission; + if (permission.exceeds(AccessPermission.NONE)) { + Transport transport = Transport.fromString(request.getScheme()); + if (permission.atLeast(AccessPermission.PUSH) && !acceptsPush(transport)) { + // downgrade the repo permission for this transport + // because it is not an acceptable PUSH transport + permission = AccessPermission.CLONE; + } + list.add(new RepositoryUrl(getRepositoryUrl(request, username, repository), permission)); + } + } + + // ssh daemon url + String sshDaemonUrl = getSshDaemonUrl(request, user, repository); + if (!StringUtils.isEmpty(sshDaemonUrl)) { + AccessPermission permission = user.getRepositoryPermission(repository).permission; + if (permission.exceeds(AccessPermission.NONE)) { + if (permission.atLeast(AccessPermission.PUSH) && !acceptsPush(Transport.SSH)) { + // downgrade the repo permission for this transport + // because it is not an acceptable PUSH transport + permission = AccessPermission.CLONE; + } + + list.add(new RepositoryUrl(sshDaemonUrl, permission)); + } + } + + // git daemon url + String gitDaemonUrl = getGitDaemonUrl(request, user, repository); + if (!StringUtils.isEmpty(gitDaemonUrl)) { + AccessPermission permission = getGitDaemonAccessPermission(user, repository); + if (permission.exceeds(AccessPermission.NONE)) { + if (permission.atLeast(AccessPermission.PUSH) && !acceptsPush(Transport.GIT)) { + // downgrade the repo permission for this transport + // because it is not an acceptable PUSH transport + permission = AccessPermission.CLONE; + } + list.add(new RepositoryUrl(gitDaemonUrl, permission)); + } + } + + // add all other urls + // {0} = repository + // {1} = username + for (String url : settings.getStrings(Keys.web.otherUrls)) { + if (url.contains("{1}")) { + // external url requires username, only add url IF we have one + if (!StringUtils.isEmpty(username)) { + list.add(new RepositoryUrl(MessageFormat.format(url, repository.name, username), null)); + } + } else { + // external url does not require username + list.add(new RepositoryUrl(MessageFormat.format(url, repository.name), null)); + } + } + + // sort transports by highest permission and then by transport security + Collections.sort(list, new Comparator<RepositoryUrl>() { + + @Override + public int compare(RepositoryUrl o1, RepositoryUrl o2) { + if (!o1.isExternal() && o2.isExternal()) { + // prefer Gitblit over external + return -1; + } else if (o1.isExternal() && !o2.isExternal()) { + // prefer Gitblit over external + return 1; + } else if (o1.isExternal() && o2.isExternal()) { + // sort by Transport ordinal + return o1.transport.compareTo(o2.transport); + } else if (o1.permission.exceeds(o2.permission)) { + // prefer highest permission + return -1; + } else if (o2.permission.exceeds(o1.permission)) { + // prefer highest permission + return 1; + } + + // prefer more secure transports + return o1.transport.compareTo(o2.transport); + } + }); + + // consider the user's transport preference + RepositoryUrl preferredUrl = null; + Transport preferredTransport = user.getPreferences().getTransport(); + if (preferredTransport != null) { + Iterator<RepositoryUrl> itr = list.iterator(); + while (itr.hasNext()) { + RepositoryUrl url = itr.next(); + if (url.transport.equals(preferredTransport)) { + itr.remove(); + preferredUrl = url; + break; + } + } + } + if (preferredUrl != null) { + list.add(0, preferredUrl); + } + + return list; + } + + /* (non-Javadoc) + * @see com.gitblit.manager.IServicesManager#isServingRepositories() + */ + @Override public boolean isServingRepositories() { - return isServingHTTP() + return isServingHTTPS() + || isServingHTTP() || isServingGIT() || isServingSSH(); } + /* (non-Javadoc) + * @see com.gitblit.manager.IServicesManager#isServingHTTP() + */ + @Override public boolean isServingHTTP() { - return settings.getBoolean(Keys.git.enableGitServlet, true); + return settings.getBoolean(Keys.git.enableGitServlet, true) + && ((gitblit.getStatus().isGO && settings.getInteger(Keys.server.httpPort, 0) > 0) + || !gitblit.getStatus().isGO); } + /* (non-Javadoc) + * @see com.gitblit.manager.IServicesManager#isServingHTTPS() + */ + @Override + public boolean isServingHTTPS() { + return settings.getBoolean(Keys.git.enableGitServlet, true) + && ((gitblit.getStatus().isGO && settings.getInteger(Keys.server.httpsPort, 0) > 0) + || !gitblit.getStatus().isGO); + } + + /* (non-Javadoc) + * @see com.gitblit.manager.IServicesManager#isServingGIT() + */ + @Override public boolean isServingGIT() { return gitDaemon != null && gitDaemon.isRunning(); } + /* (non-Javadoc) + * @see com.gitblit.manager.IServicesManager#isServingSSH() + */ + @Override public boolean isServingSSH() { return sshDaemon != null && sshDaemon.isRunning(); } @@ -158,6 +343,33 @@ public class ServicesManager implements IManager { } } + @Override + public boolean acceptsPush(Transport byTransport) { + if (byTransport == null) { + logger.info("Unknown transport, push rejected!"); + return false; + } + + Set<Transport> transports = new HashSet<Transport>(); + for (String value : settings.getStrings(Keys.git.acceptedPushTransports)) { + Transport transport = Transport.fromString(value); + if (transport == null) { + logger.info(String.format("Ignoring unknown registered transport %s", value)); + continue; + } + + transports.add(transport); + } + + if (transports.isEmpty()) { + // no transports are explicitly specified, all are acceptable + return true; + } + + // verify that the transport is permitted + return transports.contains(byTransport); + } + protected void configureGitDaemon() { int port = settings.getInteger(Keys.git.daemonPort, 0); String bindInterface = settings.getString(Keys.git.daemonBindInterface, "localhost"); @@ -179,7 +391,7 @@ public class ServicesManager implements IManager { String bindInterface = settings.getString(Keys.git.sshBindInterface, "localhost"); if (port > 0) { try { - sshDaemon = new SshDaemon(gitblit, workQueue); + sshDaemon = new SshDaemon(gitblit, workQueueProvider.get()); sshDaemon.start(); } catch (IOException e) { sshDaemon = null; @@ -285,7 +497,7 @@ public class ServicesManager implements IManager { */ protected String getHostname(HttpServletRequest request) { String hostname = request.getServerName(); - String canonicalUrl = gitblit.getSettings().getString(Keys.web.canonicalUrl, null); + String canonicalUrl = settings.getString(Keys.web.canonicalUrl, null); if (!StringUtils.isEmpty(canonicalUrl)) { try { URI uri = new URI(canonicalUrl); diff --git a/src/main/java/com/gitblit/manager/UserManager.java b/src/main/java/com/gitblit/manager/UserManager.java index 2b82ffb6..86be8bcd 100644 --- a/src/main/java/com/gitblit/manager/UserManager.java +++ b/src/main/java/com/gitblit/manager/UserManager.java @@ -36,6 +36,8 @@ import com.gitblit.extensions.UserTeamLifeCycleListener; import com.gitblit.models.TeamModel; import com.gitblit.models.UserModel; import com.gitblit.utils.StringUtils; +import com.google.inject.Inject; +import com.google.inject.Singleton; /** * The user manager manages persistence and retrieval of users and teams. @@ -43,6 +45,7 @@ import com.gitblit.utils.StringUtils; * @author James Moger * */ +@Singleton public class UserManager implements IUserManager { private final Logger logger = LoggerFactory.getLogger(getClass()); @@ -57,6 +60,7 @@ public class UserManager implements IUserManager { private IUserService userService; + @Inject public UserManager(IRuntimeManager runtimeManager, IPluginManager pluginManager) { this.settings = runtimeManager.getSettings(); this.runtimeManager = runtimeManager; diff --git a/src/main/java/com/gitblit/models/TicketModel.java b/src/main/java/com/gitblit/models/TicketModel.java index a4880ead..fd0b09eb 100644 --- a/src/main/java/com/gitblit/models/TicketModel.java +++ b/src/main/java/com/gitblit/models/TicketModel.java @@ -91,6 +91,10 @@ public class TicketModel implements Serializable, Comparable<TicketModel> { public Integer deletions; + public Priority priority; + + public Severity severity; + /** * Builds an effective ticket from the collection of changes. A change may * Add or Subtract information from a ticket, but the collection of changes @@ -141,6 +145,8 @@ public class TicketModel implements Serializable, Comparable<TicketModel> { changes = new ArrayList<Change>(); status = Status.New; type = Type.defaultType; + priority = Priority.defaultPriority; + severity = Severity.defaultSeverity; } public boolean isOpen() { @@ -517,6 +523,12 @@ public class TicketModel implements Serializable, Comparable<TicketModel> { case mergeSha: mergeSha = toString(value); break; + case priority: + priority = TicketModel.Priority.fromObject(value, priority); + break; + case severity: + severity = TicketModel.Severity.fromObject(value, severity); + break; default: // unknown break; @@ -1183,16 +1195,16 @@ public class TicketModel implements Serializable, Comparable<TicketModel> { public static enum Field { title, body, responsible, type, status, milestone, mergeSha, mergeTo, - topic, labels, watchers, reviewers, voters, mentions; + topic, labels, watchers, reviewers, voters, mentions, priority, severity; } public static enum Type { - Enhancement, Task, Bug, Proposal, Question; + Enhancement, Task, Bug, Proposal, Question, Maintenance; public static Type defaultType = Task; public static Type [] choices() { - return new Type [] { Enhancement, Task, Bug, Question }; + return new Type [] { Enhancement, Task, Bug, Question, Maintenance }; } @Override @@ -1310,4 +1322,110 @@ public class TicketModel implements Serializable, Comparable<TicketModel> { return null; } } + + public static enum Priority { + Low(-1), Normal(0), High(1), Urgent(2); + + public static Priority defaultPriority = Normal; + + final int value; + + Priority(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static Priority [] choices() { + return new Priority [] { Urgent, High, Normal, Low }; + } + + @Override + public String toString() { + return name().toLowerCase().replace('_', ' '); + } + + public static Priority fromObject(Object o, Priority defaultPriority) { + if (o instanceof Priority) { + // cast and return + return (Priority) o; + } else if (o instanceof String) { + // find by name + for (Priority priority : values()) { + String str = o.toString(); + if (priority.name().equalsIgnoreCase(str) + || priority.toString().equalsIgnoreCase(str)) { + return priority; + } + } + } else if (o instanceof Number) { + + switch (((Number) o).intValue()) { + case -1: return Priority.Low; + case 0: return Priority.Normal; + case 1: return Priority.High; + case 2: return Priority.Urgent; + default: return Priority.Normal; + } + } + + return defaultPriority; + } + } + + public static enum Severity { + Unrated(-1), Negligible(1), Minor(2), Serious(3), Critical(4), Catastrophic(5); + + public static Severity defaultSeverity = Unrated; + + final int value; + + Severity(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static Severity [] choices() { + return new Severity [] { Unrated, Negligible, Minor, Serious, Critical, Catastrophic }; + } + + @Override + public String toString() { + return name().toLowerCase().replace('_', ' '); + } + + public static Severity fromObject(Object o, Severity defaultSeverity) { + if (o instanceof Severity) { + // cast and return + return (Severity) o; + } else if (o instanceof String) { + // find by name + for (Severity severity : values()) { + String str = o.toString(); + if (severity.name().equalsIgnoreCase(str) + || severity.toString().equalsIgnoreCase(str)) { + return severity; + } + } + } else if (o instanceof Number) { + + switch (((Number) o).intValue()) { + case -1: return Severity.Unrated; + case 1: return Severity.Negligible; + case 2: return Severity.Minor; + case 3: return Severity.Serious; + case 4: return Severity.Critical; + case 5: return Severity.Catastrophic; + default: return Severity.Unrated; + } + } + + return defaultSeverity; + } + } } diff --git a/src/main/java/com/gitblit/service/LuceneService.java b/src/main/java/com/gitblit/service/LuceneService.java index 482be5c6..3f7635f4 100644 --- a/src/main/java/com/gitblit/service/LuceneService.java +++ b/src/main/java/com/gitblit/service/LuceneService.java @@ -105,7 +105,7 @@ import com.gitblit.utils.StringUtils; public class LuceneService implements Runnable {
- private static final int INDEX_VERSION = 5;
+ private static final int INDEX_VERSION = 6;
private static final String FIELD_OBJECT_TYPE = "type";
private static final String FIELD_PATH = "path";
@@ -125,7 +125,7 @@ public class LuceneService implements Runnable { private static final String CONF_ALIAS = "aliases";
private static final String CONF_BRANCH = "branches";
- private static final Version LUCENE_VERSION = Version.LUCENE_46;
+ private static final Version LUCENE_VERSION = Version.LUCENE_4_10_0;
private final Logger logger = LoggerFactory.getLogger(LuceneService.class);
diff --git a/src/main/java/com/gitblit/servlet/AccessRestrictionFilter.java b/src/main/java/com/gitblit/servlet/AccessRestrictionFilter.java index 7f691196..ee4a91aa 100644 --- a/src/main/java/com/gitblit/servlet/AccessRestrictionFilter.java +++ b/src/main/java/com/gitblit/servlet/AccessRestrictionFilter.java @@ -19,21 +19,19 @@ import java.io.IOException; import java.text.MessageFormat;
import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import com.gitblit.manager.IAuthenticationManager;
import com.gitblit.manager.IRepositoryManager;
import com.gitblit.manager.IRuntimeManager;
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.UserModel;
import com.gitblit.utils.StringUtils;
-import dagger.ObjectGraph;
-
/**
* The AccessRestrictionFilter is an AuthenticationFilter that confirms that the
* requested repository can be accessed by the anonymous or named user.
@@ -54,11 +52,15 @@ public abstract class AccessRestrictionFilter extends AuthenticationFilter { protected IRepositoryManager repositoryManager;
- @Override
- protected void inject(ObjectGraph dagger, FilterConfig filterConfig) {
- super.inject(dagger, filterConfig);
- this.runtimeManager = dagger.get(IRuntimeManager.class);
- this.repositoryManager = dagger.get(IRepositoryManager.class);
+ protected AccessRestrictionFilter(
+ IRuntimeManager runtimeManager,
+ IAuthenticationManager authenticationManager,
+ IRepositoryManager repositoryManager) {
+
+ super(authenticationManager);
+
+ this.runtimeManager = runtimeManager;
+ this.repositoryManager = repositoryManager;
}
/**
diff --git a/src/main/java/com/gitblit/servlet/AuthenticationFilter.java b/src/main/java/com/gitblit/servlet/AuthenticationFilter.java index c21f8692..3093e635 100644 --- a/src/main/java/com/gitblit/servlet/AuthenticationFilter.java +++ b/src/main/java/com/gitblit/servlet/AuthenticationFilter.java @@ -1,184 +1,190 @@ -/*
- * 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.servlet;
-
-import java.io.IOException;
-import java.security.Principal;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.Map;
-
-import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletRequestWrapper;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSession;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.gitblit.Constants;
-import com.gitblit.dagger.DaggerFilter;
-import com.gitblit.manager.IAuthenticationManager;
-import com.gitblit.models.UserModel;
-import com.gitblit.utils.DeepCopier;
-import com.gitblit.utils.StringUtils;
-
-import dagger.ObjectGraph;
-
-/**
- * The AuthenticationFilter is a servlet filter that preprocesses requests that
- * match its url pattern definition in the web.xml file.
- *
- * http://en.wikipedia.org/wiki/Basic_access_authentication
- *
- * @author James Moger
- *
- */
-public abstract class AuthenticationFilter extends DaggerFilter {
-
- protected static final String CHALLENGE = "Basic realm=\"" + Constants.NAME + "\"";
-
- protected static final String SESSION_SECURED = "com.gitblit.secured";
-
- protected transient Logger logger = LoggerFactory.getLogger(getClass());
-
- protected IAuthenticationManager authenticationManager;
-
- @Override
- protected void inject(ObjectGraph dagger, FilterConfig filterConfig) {
- this.authenticationManager = dagger.get(IAuthenticationManager.class);
- }
-
- /**
- * doFilter does the actual work of preprocessing the request to ensure that
- * the user may proceed.
- *
- * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest,
- * javax.servlet.ServletResponse, javax.servlet.FilterChain)
- */
- @Override
- public abstract void doFilter(final ServletRequest request, final ServletResponse response,
- final FilterChain chain) throws IOException, ServletException;
-
- /**
- * Allow the filter to require a client certificate to continue processing.
- *
- * @return true, if a client certificate is required
- */
- protected boolean requiresClientCertificate() {
- return false;
- }
-
- /**
- * Returns the full relative url of the request.
- *
- * @param httpRequest
- * @return url
- */
- protected String getFullUrl(HttpServletRequest httpRequest) {
- String servletUrl = httpRequest.getContextPath() + httpRequest.getServletPath();
- String url = httpRequest.getRequestURI().substring(servletUrl.length());
- String params = httpRequest.getQueryString();
- if (url.length() > 0 && url.charAt(0) == '/') {
- url = url.substring(1);
- }
- String fullUrl = url + (StringUtils.isEmpty(params) ? "" : ("?" + params));
- return fullUrl;
- }
-
- /**
- * Returns the user making the request, if the user has authenticated.
- *
- * @param httpRequest
- * @return user
- */
- protected UserModel getUser(HttpServletRequest httpRequest) {
- UserModel user = authenticationManager.authenticate(httpRequest, requiresClientCertificate());
- return user;
- }
-
- /**
- * Taken from Jetty's LoginAuthenticator.renewSessionOnAuthentication()
- */
- protected void newSession(HttpServletRequest request, HttpServletResponse response) {
- HttpSession oldSession = request.getSession(false);
- if (oldSession != null && oldSession.getAttribute(SESSION_SECURED) == null) {
- synchronized (this) {
- Map<String, Object> attributes = new HashMap<String, Object>();
- Enumeration<String> e = oldSession.getAttributeNames();
- while (e.hasMoreElements()) {
- String name = e.nextElement();
- attributes.put(name, oldSession.getAttribute(name));
- oldSession.removeAttribute(name);
- }
- oldSession.invalidate();
-
- HttpSession newSession = request.getSession(true);
- newSession.setAttribute(SESSION_SECURED, Boolean.TRUE);
- for (Map.Entry<String, Object> entry : attributes.entrySet()) {
- newSession.setAttribute(entry.getKey(), entry.getValue());
- }
- }
- }
- }
-
- /**
- * Wraps a standard HttpServletRequest and overrides user principal methods.
- */
- public static class AuthenticatedRequest extends HttpServletRequestWrapper {
-
- private UserModel user;
-
- public AuthenticatedRequest(HttpServletRequest req) {
- super(req);
- user = DeepCopier.copy(UserModel.ANONYMOUS);
- }
-
- UserModel getUser() {
- return user;
- }
-
- void setUser(UserModel user) {
- this.user = user;
- }
-
- @Override
- public String getRemoteUser() {
- return user.username;
- }
-
- @Override
- public boolean isUserInRole(String role) {
- if (role.equals(Constants.ADMIN_ROLE)) {
- return user.canAdmin();
- }
- // Gitblit does not currently use actual roles in the traditional
- // servlet container sense. That is the reason this is marked
- // deprecated, but I may want to revisit this.
- return user.hasRepositoryPermission(role);
- }
-
- @Override
- public Principal getUserPrincipal() {
- return user;
- }
- }
-}
+/* + * 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.servlet; + +import java.io.IOException; +import java.security.Principal; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.gitblit.Constants; +import com.gitblit.Constants.Role; +import com.gitblit.manager.IAuthenticationManager; +import com.gitblit.models.UserModel; +import com.gitblit.utils.DeepCopier; +import com.gitblit.utils.StringUtils; + +/** + * The AuthenticationFilter is a servlet filter that preprocesses requests that + * match its url pattern definition in the web.xml file. + * + * http://en.wikipedia.org/wiki/Basic_access_authentication + * + * @author James Moger + * + */ +public abstract class AuthenticationFilter implements Filter { + + protected static final String CHALLENGE = "Basic realm=\"" + Constants.NAME + "\""; + + protected static final String SESSION_SECURED = "com.gitblit.secured"; + + protected transient Logger logger = LoggerFactory.getLogger(getClass()); + + protected IAuthenticationManager authenticationManager; + + protected AuthenticationFilter(IAuthenticationManager authenticationManager) { + this.authenticationManager = authenticationManager; + } + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + } + + @Override + public void destroy() { + } + + /** + * doFilter does the actual work of preprocessing the request to ensure that + * the user may proceed. + * + * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, + * javax.servlet.ServletResponse, javax.servlet.FilterChain) + */ + @Override + public abstract void doFilter(final ServletRequest request, final ServletResponse response, + final FilterChain chain) throws IOException, ServletException; + + /** + * Allow the filter to require a client certificate to continue processing. + * + * @return true, if a client certificate is required + */ + protected boolean requiresClientCertificate() { + return false; + } + + /** + * Returns the full relative url of the request. + * + * @param httpRequest + * @return url + */ + protected String getFullUrl(HttpServletRequest httpRequest) { + String servletUrl = httpRequest.getContextPath() + httpRequest.getServletPath(); + String url = httpRequest.getRequestURI().substring(servletUrl.length()); + String params = httpRequest.getQueryString(); + if (url.length() > 0 && url.charAt(0) == '/') { + url = url.substring(1); + } + String fullUrl = url + (StringUtils.isEmpty(params) ? "" : ("?" + params)); + return fullUrl; + } + + /** + * Returns the user making the request, if the user has authenticated. + * + * @param httpRequest + * @return user + */ + protected UserModel getUser(HttpServletRequest httpRequest) { + UserModel user = authenticationManager.authenticate(httpRequest, requiresClientCertificate()); + return user; + } + + /** + * Taken from Jetty's LoginAuthenticator.renewSessionOnAuthentication() + */ + protected void newSession(HttpServletRequest request, HttpServletResponse response) { + HttpSession oldSession = request.getSession(false); + if (oldSession != null && oldSession.getAttribute(SESSION_SECURED) == null) { + synchronized (this) { + Map<String, Object> attributes = new HashMap<String, Object>(); + Enumeration<String> e = oldSession.getAttributeNames(); + while (e.hasMoreElements()) { + String name = e.nextElement(); + attributes.put(name, oldSession.getAttribute(name)); + oldSession.removeAttribute(name); + } + oldSession.invalidate(); + + HttpSession newSession = request.getSession(true); + newSession.setAttribute(SESSION_SECURED, Boolean.TRUE); + for (Map.Entry<String, Object> entry : attributes.entrySet()) { + newSession.setAttribute(entry.getKey(), entry.getValue()); + } + } + } + } + + /** + * Wraps a standard HttpServletRequest and overrides user principal methods. + */ + public static class AuthenticatedRequest extends HttpServletRequestWrapper { + + private UserModel user; + + public AuthenticatedRequest(HttpServletRequest req) { + super(req); + user = DeepCopier.copy(UserModel.ANONYMOUS); + } + + UserModel getUser() { + return user; + } + + void setUser(UserModel user) { + this.user = user; + } + + @Override + public String getRemoteUser() { + return user.username; + } + + @Override + public boolean isUserInRole(String role) { + if (role.equals(Role.ADMIN.getRole())) { + return user.canAdmin(); + } + // Gitblit does not currently use actual roles in the traditional + // servlet container sense. That is the reason this is marked + // deprecated, but I may want to revisit this. + return user.hasRepositoryPermission(role); + } + + @Override + public Principal getUserPrincipal() { + return user; + } + } +} diff --git a/src/main/java/com/gitblit/servlet/BranchGraphServlet.java b/src/main/java/com/gitblit/servlet/BranchGraphServlet.java index fa2152c6..85fbb745 100644 --- a/src/main/java/com/gitblit/servlet/BranchGraphServlet.java +++ b/src/main/java/com/gitblit/servlet/BranchGraphServlet.java @@ -36,7 +36,10 @@ import java.util.Set; import java.util.TreeSet;
import javax.imageio.ImageIO;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -55,20 +58,18 @@ import org.slf4j.LoggerFactory; import com.gitblit.Constants;
import com.gitblit.IStoredSettings;
import com.gitblit.Keys;
-import com.gitblit.dagger.DaggerServlet;
import com.gitblit.manager.IRepositoryManager;
import com.gitblit.utils.JGitUtils;
import com.gitblit.utils.StringUtils;
-import dagger.ObjectGraph;
-
/**
* Handles requests for branch graphs
*
* @author James Moger
*
*/
-public class BranchGraphServlet extends DaggerServlet {
+@Singleton
+public class BranchGraphServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@@ -87,20 +88,20 @@ public class BranchGraphServlet extends DaggerServlet { private IRepositoryManager repositoryManager;
- public BranchGraphServlet() {
- super();
+ @Inject
+ public BranchGraphServlet(
+ IStoredSettings settings,
+ IRepositoryManager repositoryManager) {
+
+ this.settings = settings;
+ this.repositoryManager = repositoryManager;
+
strokeCache = new Stroke[4];
for (int i = 1; i < strokeCache.length; i++) {
strokeCache[i] = new BasicStroke(i);
}
}
- @Override
- protected void inject(ObjectGraph dagger) {
- this.settings = dagger.get(IStoredSettings.class);
- this.repositoryManager = dagger.get(IRepositoryManager.class);
- }
-
/**
* Returns an url to this servlet for the specified parameters.
*
diff --git a/src/main/java/com/gitblit/servlet/DownloadZipFilter.java b/src/main/java/com/gitblit/servlet/DownloadZipFilter.java index 42257a23..de471482 100644 --- a/src/main/java/com/gitblit/servlet/DownloadZipFilter.java +++ b/src/main/java/com/gitblit/servlet/DownloadZipFilter.java @@ -15,7 +15,13 @@ */
package com.gitblit.servlet;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
import com.gitblit.Constants.AccessRestrictionType;
+import com.gitblit.manager.IAuthenticationManager;
+import com.gitblit.manager.IRepositoryManager;
+import com.gitblit.manager.IRuntimeManager;
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.UserModel;
@@ -27,8 +33,18 @@ import com.gitblit.models.UserModel; * @author James Moger
*
*/
+@Singleton
public class DownloadZipFilter extends AccessRestrictionFilter {
+ @Inject
+ public DownloadZipFilter(
+ IRuntimeManager runtimeManager,
+ IAuthenticationManager authenticationManager,
+ IRepositoryManager repositoryManager) {
+
+ super(runtimeManager, authenticationManager, repositoryManager);
+ }
+
/**
* Extract the repository name from the url.
*
diff --git a/src/main/java/com/gitblit/servlet/DownloadZipServlet.java b/src/main/java/com/gitblit/servlet/DownloadZipServlet.java index 6a64778c..07562561 100644 --- a/src/main/java/com/gitblit/servlet/DownloadZipServlet.java +++ b/src/main/java/com/gitblit/servlet/DownloadZipServlet.java @@ -20,7 +20,10 @@ import java.text.MessageFormat; import java.text.ParseException;
import java.util.Date;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jgit.lib.Repository;
@@ -31,15 +34,12 @@ import org.slf4j.LoggerFactory; import com.gitblit.Constants;
import com.gitblit.IStoredSettings;
import com.gitblit.Keys;
-import com.gitblit.dagger.DaggerServlet;
import com.gitblit.manager.IRepositoryManager;
import com.gitblit.utils.CompressionUtils;
import com.gitblit.utils.JGitUtils;
import com.gitblit.utils.MarkdownUtils;
import com.gitblit.utils.StringUtils;
-import dagger.ObjectGraph;
-
/**
* Streams out a zip file from the specified repository for any tree path at any
* revision.
@@ -47,7 +47,8 @@ import dagger.ObjectGraph; * @author James Moger
*
*/
-public class DownloadZipServlet extends DaggerServlet {
+@Singleton
+public class DownloadZipServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@@ -76,10 +77,10 @@ public class DownloadZipServlet extends DaggerServlet { }
}
- @Override
- protected void inject(ObjectGraph dagger) {
- this.settings = dagger.get(IStoredSettings.class);
- this.repositoryManager = dagger.get(IRepositoryManager.class);
+ @Inject
+ public DownloadZipServlet(IStoredSettings settings, IRepositoryManager repositoryManager) {
+ this.settings = settings;
+ this.repositoryManager = repositoryManager;
}
/**
diff --git a/src/main/java/com/gitblit/servlet/EnforceAuthenticationFilter.java b/src/main/java/com/gitblit/servlet/EnforceAuthenticationFilter.java index c015021d..8a3f782a 100644 --- a/src/main/java/com/gitblit/servlet/EnforceAuthenticationFilter.java +++ b/src/main/java/com/gitblit/servlet/EnforceAuthenticationFilter.java @@ -18,6 +18,9 @@ import java.io.IOException; import java.text.MessageFormat; +import com.google.inject.Inject; +import com.google.inject.Singleton; +import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; @@ -31,12 +34,9 @@ import org.slf4j.LoggerFactory; import com.gitblit.IStoredSettings; import com.gitblit.Keys; -import com.gitblit.dagger.DaggerFilter; import com.gitblit.manager.IAuthenticationManager; import com.gitblit.models.UserModel; -import dagger.ObjectGraph; - /** * This filter enforces authentication via HTTP Basic Authentication, if the settings indicate so. * It looks at the settings "web.authenticateViewPages" and "web.enforceHttpBasicAuthentication"; if @@ -45,7 +45,8 @@ import dagger.ObjectGraph; * @author Laurens Vrijnsen * */ -public class EnforceAuthenticationFilter extends DaggerFilter { +@Singleton +public class EnforceAuthenticationFilter implements Filter { protected transient Logger logger = LoggerFactory.getLogger(getClass()); @@ -53,10 +54,21 @@ public class EnforceAuthenticationFilter extends DaggerFilter { private IAuthenticationManager authenticationManager; + @Inject + public EnforceAuthenticationFilter( + IStoredSettings settings, + IAuthenticationManager authenticationManager) { + + this.settings = settings; + this.authenticationManager = authenticationManager; + } + @Override - protected void inject(ObjectGraph dagger, FilterConfig filterConfig) { - this.settings = dagger.get(IStoredSettings.class); - this.authenticationManager = dagger.get(IAuthenticationManager.class); + public void init(FilterConfig config) { + } + + @Override + public void destroy() { } /* @@ -87,12 +99,4 @@ public class EnforceAuthenticationFilter extends DaggerFilter { chain.doFilter(request, response); } } - - - /* - * @see javax.servlet.Filter#destroy() - */ - @Override - public void destroy() { - } } diff --git a/src/main/java/com/gitblit/servlet/FederationServlet.java b/src/main/java/com/gitblit/servlet/FederationServlet.java index acbc0021..78709c9c 100644 --- a/src/main/java/com/gitblit/servlet/FederationServlet.java +++ b/src/main/java/com/gitblit/servlet/FederationServlet.java @@ -25,6 +25,8 @@ import java.util.List; import java.util.Map;
import java.util.Set;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
import javax.servlet.http.HttpServletResponse;
import com.gitblit.Constants.FederationRequest;
@@ -43,14 +45,13 @@ import com.gitblit.utils.HttpUtils; import com.gitblit.utils.StringUtils;
import com.gitblit.utils.TimeUtils;
-import dagger.ObjectGraph;
-
/**
* Handles federation requests.
*
* @author James Moger
*
*/
+@Singleton
public class FederationServlet extends JsonServlet {
private static final long serialVersionUID = 1L;
@@ -63,12 +64,17 @@ public class FederationServlet extends JsonServlet { private IFederationManager federationManager;
- @Override
- protected void inject(ObjectGraph dagger) {
- this.settings = dagger.get(IStoredSettings.class);
- this.userManager = dagger.get(IUserManager.class);
- this.repositoryManager = dagger.get(IRepositoryManager.class);
- this.federationManager = dagger.get(IFederationManager.class);
+ @Inject
+ public FederationServlet(
+ IStoredSettings settings,
+ IUserManager userManager,
+ IRepositoryManager repositoryManager,
+ IFederationManager federationManager) {
+
+ this.settings = settings;
+ this.userManager = userManager;
+ this.repositoryManager = repositoryManager;
+ this.federationManager = federationManager;
}
/**
diff --git a/src/main/java/com/gitblit/servlet/GitFilter.java b/src/main/java/com/gitblit/servlet/GitFilter.java index bb3d3216..b29fdb6a 100644 --- a/src/main/java/com/gitblit/servlet/GitFilter.java +++ b/src/main/java/com/gitblit/servlet/GitFilter.java @@ -17,7 +17,8 @@ package com.gitblit.servlet; import java.text.MessageFormat;
-import javax.servlet.FilterConfig;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
import javax.servlet.http.HttpServletRequest;
import com.gitblit.Constants.AccessRestrictionType;
@@ -25,13 +26,14 @@ import com.gitblit.Constants.AuthorizationControl; import com.gitblit.GitBlitException;
import com.gitblit.IStoredSettings;
import com.gitblit.Keys;
+import com.gitblit.manager.IAuthenticationManager;
import com.gitblit.manager.IFederationManager;
+import com.gitblit.manager.IRepositoryManager;
+import com.gitblit.manager.IRuntimeManager;
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.UserModel;
import com.gitblit.utils.StringUtils;
-import dagger.ObjectGraph;
-
/**
* The GitFilter is an AccessRestrictionFilter which ensures that Git client
* requests for push, clone, or view restricted repositories are authenticated
@@ -40,6 +42,7 @@ import dagger.ObjectGraph; * @author James Moger
*
*/
+@Singleton
public class GitFilter extends AccessRestrictionFilter {
protected static final String gitReceivePack = "/git-receive-pack";
@@ -53,11 +56,18 @@ public class GitFilter extends AccessRestrictionFilter { private IFederationManager federationManager;
- @Override
- protected void inject(ObjectGraph dagger, FilterConfig filterConfig) {
- super.inject(dagger, filterConfig);
- this.settings = dagger.get(IStoredSettings.class);
- this.federationManager = dagger.get(IFederationManager.class);
+ @Inject
+ public GitFilter(
+ IStoredSettings settings,
+ IRuntimeManager runtimeManager,
+ IAuthenticationManager authenticationManager,
+ IRepositoryManager repositoryManager,
+ IFederationManager federationManager) {
+
+ super(runtimeManager, authenticationManager, repositoryManager);
+
+ this.settings = settings;
+ this.federationManager = federationManager;
}
/**
diff --git a/src/main/java/com/gitblit/servlet/GitServlet.java b/src/main/java/com/gitblit/servlet/GitServlet.java index 93fe31d0..941b4c5e 100644 --- a/src/main/java/com/gitblit/servlet/GitServlet.java +++ b/src/main/java/com/gitblit/servlet/GitServlet.java @@ -20,6 +20,8 @@ import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND; import java.io.IOException;
import java.util.Enumeration;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletConfig;
@@ -33,14 +35,11 @@ import javax.servlet.http.HttpServletResponse; import org.eclipse.jgit.http.server.GitFilter;
-import com.gitblit.dagger.DaggerContext;
import com.gitblit.git.GitblitReceivePackFactory;
import com.gitblit.git.GitblitUploadPackFactory;
import com.gitblit.git.RepositoryResolver;
import com.gitblit.manager.IGitblit;
-import dagger.ObjectGraph;
-
/**
* The GitServlet provides http/https access to Git repositories.
* Access to this servlet is protected by the GitFilter.
@@ -48,24 +47,23 @@ import dagger.ObjectGraph; * @author James Moger
*
*/
+@Singleton
public class GitServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private final GitFilter gitFilter;
- public GitServlet() {
+ @Inject
+ public GitServlet(IGitblit gitblit) {
gitFilter = new GitFilter();
+ gitFilter.setRepositoryResolver(new RepositoryResolver<HttpServletRequest>(gitblit));
+ gitFilter.setUploadPackFactory(new GitblitUploadPackFactory<HttpServletRequest>(gitblit));
+ gitFilter.setReceivePackFactory(new GitblitReceivePackFactory<HttpServletRequest>(gitblit));
}
@Override
public void init(final ServletConfig config) throws ServletException {
- ServletContext context = config.getServletContext();
- ObjectGraph dagger = (ObjectGraph) context.getAttribute(DaggerContext.INJECTOR_NAME);
- IGitblit gitblit = dagger.get(IGitblit.class);
- gitFilter.setRepositoryResolver(new RepositoryResolver<HttpServletRequest>(gitblit));
- gitFilter.setUploadPackFactory(new GitblitUploadPackFactory<HttpServletRequest>(gitblit));
- gitFilter.setReceivePackFactory(new GitblitReceivePackFactory<HttpServletRequest>(gitblit));
gitFilter.init(new FilterConfig() {
@Override
diff --git a/src/main/java/com/gitblit/servlet/GitblitContext.java b/src/main/java/com/gitblit/servlet/GitblitContext.java index e5c59bd0..077624c2 100644 --- a/src/main/java/com/gitblit/servlet/GitblitContext.java +++ b/src/main/java/com/gitblit/servlet/GitblitContext.java @@ -31,14 +31,17 @@ import javax.naming.NamingException; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.gitblit.Constants; -import com.gitblit.DaggerModule; import com.gitblit.FileSettings; import com.gitblit.IStoredSettings; import com.gitblit.Keys; import com.gitblit.WebXmlSettings; -import com.gitblit.dagger.DaggerContext; import com.gitblit.extensions.LifeCycleListener; +import com.gitblit.guice.CoreModule; +import com.gitblit.guice.WebModule; import com.gitblit.manager.IAuthenticationManager; import com.gitblit.manager.IFederationManager; import com.gitblit.manager.IGitblit; @@ -48,28 +51,34 @@ import com.gitblit.manager.IPluginManager; import com.gitblit.manager.IProjectManager; import com.gitblit.manager.IRepositoryManager; import com.gitblit.manager.IRuntimeManager; +import com.gitblit.manager.IServicesManager; import com.gitblit.manager.IUserManager; +import com.gitblit.tickets.ITicketService; import com.gitblit.transport.ssh.IPublicKeyManager; import com.gitblit.utils.ContainerUtils; import com.gitblit.utils.StringUtils; - -import dagger.ObjectGraph; +import com.google.inject.AbstractModule; +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.google.inject.servlet.GuiceServletContextListener; /** * This class is the main entry point for the entire webapp. It is a singleton * created manually by Gitblit GO or dynamically by the WAR/Express servlet - * container. This class instantiates and starts all managers. Servlets and - * filters are instantiated defined in web.xml and instantiated by the servlet - * container, but those servlets and filters use Dagger to manually inject their - * dependencies. + * container. This class instantiates and starts all managers. + * + * Servlets and filters are injected which allows Gitblit to be completely + * code-driven. * * @author James Moger * */ -public class GitblitContext extends DaggerContext { +public class GitblitContext extends GuiceServletContextListener { private static GitblitContext gitblit; + protected final Logger logger = LoggerFactory.getLogger(getClass()); + private final List<IManager> managers = new ArrayList<IManager>(); private final IStoredSettings goSettings; @@ -111,12 +120,16 @@ public class GitblitContext extends DaggerContext { return null; } + @Override + protected Injector getInjector() { + return Guice.createInjector(getModules()); + } + /** - * Returns Gitblit's Dagger injection modules. + * Returns Gitblit's Guice injection modules. */ - @Override - protected Object [] getModules() { - return new Object [] { new DaggerModule() }; + protected AbstractModule [] getModules() { + return new AbstractModule [] { new CoreModule(), new WebModule() }; } /** @@ -127,18 +140,20 @@ public class GitblitContext extends DaggerContext { */ @Override public final void contextInitialized(ServletContextEvent contextEvent) { + super.contextInitialized(contextEvent); + ServletContext context = contextEvent.getServletContext(); - configureContext(context); + startCore(context); } /** * Prepare runtime settings and start all manager instances. */ - protected void configureContext(ServletContext context) { - ObjectGraph injector = getInjector(context); + protected void startCore(ServletContext context) { + Injector injector = (Injector) context.getAttribute(Injector.class.getName()); // create the runtime settings object - IStoredSettings runtimeSettings = injector.get(IStoredSettings.class); + IStoredSettings runtimeSettings = injector.getInstance(IStoredSettings.class); final File baseFolder; if (goSettings != null) { @@ -168,7 +183,7 @@ public class GitblitContext extends DaggerContext { // Manually configure IRuntimeManager logManager(IRuntimeManager.class); - IRuntimeManager runtime = injector.get(IRuntimeManager.class); + IRuntimeManager runtime = injector.getInstance(IRuntimeManager.class); runtime.setBaseFolder(baseFolder); runtime.getStatus().isGO = goSettings != null; runtime.getStatus().servletContainer = context.getServerInfo(); @@ -186,7 +201,9 @@ public class GitblitContext extends DaggerContext { startManager(injector, IRepositoryManager.class); startManager(injector, IProjectManager.class); startManager(injector, IFederationManager.class); + startManager(injector, ITicketService.class); startManager(injector, IGitblit.class); + startManager(injector, IServicesManager.class); // start the plugin manager last so that plugins can depend on // deterministic access to all other managers in their start() methods @@ -196,7 +213,7 @@ public class GitblitContext extends DaggerContext { logger.info("All managers started."); logger.info(""); - IPluginManager pluginManager = injector.get(IPluginManager.class); + IPluginManager pluginManager = injector.getInstance(IPluginManager.class); for (LifeCycleListener listener : pluginManager.getExtensions(LifeCycleListener.class)) { try { listener.onStartup(); @@ -236,17 +253,21 @@ public class GitblitContext extends DaggerContext { return defaultBaseFolder; } - protected <X extends IManager> X loadManager(ObjectGraph injector, Class<X> clazz) { - X x = injector.get(clazz); + protected <X extends IManager> X loadManager(Injector injector, Class<X> clazz) { + X x = injector.getInstance(clazz); return x; } - protected <X extends IManager> X startManager(ObjectGraph injector, Class<X> clazz) { + protected <X extends IManager> X startManager(Injector injector, Class<X> clazz) { X x = loadManager(injector, clazz); logManager(clazz); - x.start(); - managers.add(x); - return x; + return startManager(x); + } + + protected <X extends IManager> X startManager(X x) { + x.start(); + managers.add(x); + return x; } protected void logManager(Class<? extends IManager> clazz) { @@ -254,11 +275,17 @@ public class GitblitContext extends DaggerContext { logger.info("----[{}]----", clazz.getName()); } + @Override + public final void contextDestroyed(ServletContextEvent contextEvent) { + super.contextDestroyed(contextEvent); + ServletContext context = contextEvent.getServletContext(); + destroyContext(context); + } + /** * Gitblit is being shutdown either because the servlet container is * shutting down or because the servlet container is re-deploying Gitblit. */ - @Override protected void destroyContext(ServletContext context) { logger.info("Gitblit context destroyed by servlet container."); @@ -341,12 +368,10 @@ public class GitblitContext extends DaggerContext { baseFolder.mkdirs(); // try to extract the data folder resource to the baseFolder - File localSettings = new File(baseFolder, "gitblit.properties"); - if (!localSettings.exists()) { - extractResources(context, "/WEB-INF/data/", baseFolder); - } + extractResources(context, "/WEB-INF/data/", baseFolder); // delegate all config to baseFolder/gitblit.properties file + File localSettings = new File(baseFolder, "gitblit.properties"); FileSettings fileSettings = new FileSettings(localSettings.getAbsolutePath()); // merge the stored settings into the runtime settings diff --git a/src/main/java/com/gitblit/servlet/JsonServlet.java b/src/main/java/com/gitblit/servlet/JsonServlet.java index 4378c8a6..abc0f292 100644 --- a/src/main/java/com/gitblit/servlet/JsonServlet.java +++ b/src/main/java/com/gitblit/servlet/JsonServlet.java @@ -21,6 +21,7 @@ import java.lang.reflect.Type; import java.text.MessageFormat;
import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -28,7 +29,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory;
import com.gitblit.Constants;
-import com.gitblit.dagger.DaggerServlet;
import com.gitblit.utils.JsonUtils;
import com.gitblit.utils.StringUtils;
@@ -38,7 +38,7 @@ import com.gitblit.utils.StringUtils; * @author James Moger
*
*/
-public abstract class JsonServlet extends DaggerServlet {
+public abstract class JsonServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
diff --git a/src/main/java/com/gitblit/servlet/LogoServlet.java b/src/main/java/com/gitblit/servlet/LogoServlet.java index 96f34afd..d5d298b1 100644 --- a/src/main/java/com/gitblit/servlet/LogoServlet.java +++ b/src/main/java/com/gitblit/servlet/LogoServlet.java @@ -21,24 +21,25 @@ import java.io.IOException; import java.io.InputStream;
import java.io.OutputStream;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.gitblit.Keys;
-import com.gitblit.dagger.DaggerServlet;
import com.gitblit.manager.IRuntimeManager;
-import dagger.ObjectGraph;
-
/**
* Handles requests for logo.png
*
* @author James Moger
*
*/
-public class LogoServlet extends DaggerServlet {
+@Singleton
+public class LogoServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@@ -46,9 +47,9 @@ public class LogoServlet extends DaggerServlet { private IRuntimeManager runtimeManager;
- @Override
- protected void inject(ObjectGraph dagger) {
- this.runtimeManager = dagger.get(IRuntimeManager.class);
+ @Inject
+ public LogoServlet(IRuntimeManager runtimeManager) {
+ this.runtimeManager = runtimeManager;
}
@Override
diff --git a/src/main/java/com/gitblit/servlet/PagesFilter.java b/src/main/java/com/gitblit/servlet/PagesFilter.java index e07d9b3b..1d6c3db9 100644 --- a/src/main/java/com/gitblit/servlet/PagesFilter.java +++ b/src/main/java/com/gitblit/servlet/PagesFilter.java @@ -15,6 +15,13 @@ */
package com.gitblit.servlet;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+import com.gitblit.manager.IAuthenticationManager;
+import com.gitblit.manager.IRepositoryManager;
+import com.gitblit.manager.IRuntimeManager;
+
/**
* The PagesFilter is an AccessRestrictionFilter which ensures the gh-pages
@@ -23,7 +30,17 @@ package com.gitblit.servlet; * @author James Moger
*
*/
+
+@Singleton
public class PagesFilter extends RawFilter {
+ @Inject
+ public PagesFilter(
+ IRuntimeManager runtimeManager,
+ IAuthenticationManager authenticationManager,
+ IRepositoryManager repositoryManager) {
+
+ super(runtimeManager, authenticationManager, repositoryManager);
+ }
}
diff --git a/src/main/java/com/gitblit/servlet/PagesServlet.java b/src/main/java/com/gitblit/servlet/PagesServlet.java index defd9e63..1473e966 100644 --- a/src/main/java/com/gitblit/servlet/PagesServlet.java +++ b/src/main/java/com/gitblit/servlet/PagesServlet.java @@ -1,103 +1,116 @@ -/*
- * Copyright 2012 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.servlet;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Date;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevCommit;
-
-import com.gitblit.Constants;
-import com.gitblit.utils.JGitUtils;
-
-/**
- * Serves the content of a gh-pages branch.
- *
- * @author James Moger
- *
- */
-public class PagesServlet extends RawServlet {
-
- private static final long serialVersionUID = 1L;
-
-
- /**
- * Returns an url to this servlet for the specified parameters.
- *
- * @param baseURL
- * @param repository
- * @param path
- * @return an url
- */
- public static String asLink(String baseURL, String repository, String path) {
- if (baseURL.length() > 0 && baseURL.charAt(baseURL.length() - 1) == '/') {
- baseURL = baseURL.substring(0, baseURL.length() - 1);
- }
- return baseURL + Constants.PAGES + repository + "/" + (path == null ? "" : ("/" + path));
- }
-
- @Override
- protected String getBranch(String repository, HttpServletRequest request) {
- return "gh-pages";
- }
-
- @Override
- protected String getPath(String repository, String branch, HttpServletRequest request) {
- String pi = request.getPathInfo().substring(1);
- if (pi.equals(repository)) {
- return "";
- }
- String path = pi.substring(pi.indexOf(repository) + repository.length() + 1);
- if (path.endsWith("/")) {
- path = path.substring(0, path.length() - 1);
- }
- return path;
- }
-
- @Override
- protected boolean renderIndex() {
- return true;
- }
-
- @Override
- protected void setContentType(HttpServletResponse response, String contentType) {
- response.setContentType(contentType);;
- }
-
- @Override
- protected boolean streamFromRepo(HttpServletRequest request, HttpServletResponse response, Repository repository,
- RevCommit commit, String requestedPath) throws IOException {
-
- response.setDateHeader("Last-Modified", JGitUtils.getCommitDate(commit).getTime());
- response.setHeader("Cache-Control", "public, max-age=3600, must-revalidate");
-
- return super.streamFromRepo(request, response, repository, commit, requestedPath);
- }
-
- @Override
- protected void sendContent(HttpServletResponse response, Date date, InputStream is) throws ServletException, IOException {
- response.setDateHeader("Last-Modified", date.getTime());
- response.setHeader("Cache-Control", "public, max-age=3600, must-revalidate");
-
- super.sendContent(response, date, is);
- }
-}
+/* + * Copyright 2012 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.servlet; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Date; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.revwalk.RevCommit; + +import com.gitblit.Constants; +import com.gitblit.manager.IRepositoryManager; +import com.gitblit.manager.IRuntimeManager; +import com.gitblit.utils.JGitUtils; +import com.google.inject.Inject; +import com.google.inject.Singleton; + +/** + * Serves the content of a gh-pages branch. + * + * @author James Moger + * + */ +@Singleton +public class PagesServlet extends RawServlet { + + private static final long serialVersionUID = 1L; + + + /** + * Returns an url to this servlet for the specified parameters. + * + * @param baseURL + * @param repository + * @param path + * @return an url + */ + public static String asLink(String baseURL, String repository, String path) { + if (baseURL.length() > 0 && baseURL.charAt(baseURL.length() - 1) == '/') { + baseURL = baseURL.substring(0, baseURL.length() - 1); + } + return baseURL + Constants.PAGES + repository + "/" + (path == null ? "" : ("/" + path)); + } + + @Inject + public PagesServlet( + IRuntimeManager runtimeManager, + IRepositoryManager repositoryManager) { + + super(runtimeManager, repositoryManager); + } + + @Override + protected String getBranch(String repository, HttpServletRequest request) { + return "gh-pages"; + } + + @Override + protected String getPath(String repository, String branch, HttpServletRequest request) { + String pi = request.getPathInfo().substring(1); + if (pi.equals(repository)) { + return ""; + } + String path = pi.substring(pi.indexOf(repository) + repository.length() + 1); + if (path.endsWith("/")) { + path = path.substring(0, path.length() - 1); + } + return path; + } + + @Override + protected boolean renderIndex() { + return true; + } + + @Override + protected void setContentType(HttpServletResponse response, String contentType) { + response.setContentType(contentType);; + } + + @Override + protected boolean streamFromRepo(HttpServletRequest request, HttpServletResponse response, Repository repository, + RevCommit commit, String requestedPath) throws IOException { + + response.setDateHeader("Last-Modified", JGitUtils.getCommitDate(commit).getTime()); + response.setHeader("Cache-Control", "public, max-age=3600, must-revalidate"); + + return super.streamFromRepo(request, response, repository, commit, requestedPath); + } + + @Override + protected void sendContent(HttpServletResponse response, Date date, InputStream is) throws ServletException, IOException { + response.setDateHeader("Last-Modified", date.getTime()); + response.setHeader("Cache-Control", "public, max-age=3600, must-revalidate"); + + super.sendContent(response, date, is); + } +} diff --git a/src/main/java/com/gitblit/servlet/ProxyFilter.java b/src/main/java/com/gitblit/servlet/ProxyFilter.java index 46f59de9..d7f096ac 100644 --- a/src/main/java/com/gitblit/servlet/ProxyFilter.java +++ b/src/main/java/com/gitblit/servlet/ProxyFilter.java @@ -16,9 +16,13 @@ package com.gitblit.servlet; import java.io.IOException; +import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import com.google.inject.Inject; +import com.google.inject.Singleton; +import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; @@ -27,13 +31,10 @@ import javax.servlet.ServletResponse; import ro.fortsoft.pf4j.PluginWrapper; -import com.gitblit.dagger.DaggerFilter; import com.gitblit.extensions.HttpRequestFilter; import com.gitblit.manager.IPluginManager; import com.gitblit.manager.IRuntimeManager; -import dagger.ObjectGraph; - /** * A request filter than allows registered extension request filters to access * request data. The intended purpose is for server monitoring plugins. @@ -41,15 +42,29 @@ import dagger.ObjectGraph; * @author David Ostrovsky * @since 1.6.0 */ -public class ProxyFilter extends DaggerFilter { - private List<HttpRequestFilter> filters; +@Singleton +public class ProxyFilter implements Filter { + private final IRuntimeManager runtimeManager; + + private final IPluginManager pluginManager; + + private final List<HttpRequestFilter> filters; + + @Inject + public ProxyFilter( + IRuntimeManager runtimeManager, + IPluginManager pluginManager) { + + this.runtimeManager = runtimeManager; + this.pluginManager = pluginManager; + this.filters = new ArrayList<>(); + + } @Override - protected void inject(ObjectGraph dagger, FilterConfig filterConfig) throws ServletException { - IRuntimeManager runtimeManager = dagger.get(IRuntimeManager.class); - IPluginManager pluginManager = dagger.get(IPluginManager.class); + public void init(FilterConfig filterConfig) throws ServletException { - filters = pluginManager.getExtensions(HttpRequestFilter.class); + filters.addAll(pluginManager.getExtensions(HttpRequestFilter.class)); for (HttpRequestFilter f : filters) { // wrap the filter config for Gitblit settings retrieval PluginWrapper pluginWrapper = pluginManager.whichPlugin(f.getClass()); diff --git a/src/main/java/com/gitblit/servlet/PtServlet.java b/src/main/java/com/gitblit/servlet/PtServlet.java index f69b444d..5f577f85 100644 --- a/src/main/java/com/gitblit/servlet/PtServlet.java +++ b/src/main/java/com/gitblit/servlet/PtServlet.java @@ -22,7 +22,10 @@ import java.io.IOException; import java.io.InputStream;
import java.io.OutputStream;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -34,11 +37,8 @@ import org.apache.commons.compress.compressors.CompressorOutputStream; import org.apache.commons.compress.compressors.CompressorStreamFactory;
import org.eclipse.jgit.lib.FileMode;
-import com.gitblit.dagger.DaggerServlet;
import com.gitblit.manager.IRuntimeManager;
-import dagger.ObjectGraph;
-
/**
* Handles requests for the Barnum pt (patchset tool).
*
@@ -47,7 +47,8 @@ import dagger.ObjectGraph; * @author James Moger
*
*/
-public class PtServlet extends DaggerServlet {
+@Singleton
+public class PtServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@@ -55,9 +56,9 @@ public class PtServlet extends DaggerServlet { private IRuntimeManager runtimeManager;
- @Override
- protected void inject(ObjectGraph dagger) {
- this.runtimeManager = dagger.get(IRuntimeManager.class);
+ @Inject
+ public PtServlet(IRuntimeManager runtimeManager) {
+ this.runtimeManager = runtimeManager;
}
@Override
diff --git a/src/main/java/com/gitblit/servlet/RawFilter.java b/src/main/java/com/gitblit/servlet/RawFilter.java index 34989c98..fe4af040 100644 --- a/src/main/java/com/gitblit/servlet/RawFilter.java +++ b/src/main/java/com/gitblit/servlet/RawFilter.java @@ -15,9 +15,15 @@ */
package com.gitblit.servlet;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
import org.eclipse.jgit.lib.Repository;
import com.gitblit.Constants.AccessRestrictionType;
+import com.gitblit.manager.IAuthenticationManager;
+import com.gitblit.manager.IRepositoryManager;
+import com.gitblit.manager.IRuntimeManager;
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.UserModel;
@@ -28,8 +34,18 @@ import com.gitblit.models.UserModel; * @author James Moger
*
*/
+@Singleton
public class RawFilter extends AccessRestrictionFilter {
+ @Inject
+ public RawFilter(
+ IRuntimeManager runtimeManager,
+ IAuthenticationManager authenticationManager,
+ IRepositoryManager repositoryManager) {
+
+ super(runtimeManager, authenticationManager, repositoryManager);
+ }
+
/**
* Extract the repository name from the url.
*
diff --git a/src/main/java/com/gitblit/servlet/RawServlet.java b/src/main/java/com/gitblit/servlet/RawServlet.java index ca41d0ab..16d1a977 100644 --- a/src/main/java/com/gitblit/servlet/RawServlet.java +++ b/src/main/java/com/gitblit/servlet/RawServlet.java @@ -30,6 +30,7 @@ import java.util.TreeMap; import javax.servlet.ServletContext; import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -48,7 +49,6 @@ import org.slf4j.LoggerFactory; import com.gitblit.Constants; import com.gitblit.Keys; -import com.gitblit.dagger.DaggerServlet; import com.gitblit.manager.IRepositoryManager; import com.gitblit.manager.IRuntimeManager; import com.gitblit.models.PathModel; @@ -56,8 +56,8 @@ import com.gitblit.utils.ByteFormat; import com.gitblit.utils.JGitUtils; import com.gitblit.utils.MarkdownUtils; import com.gitblit.utils.StringUtils; - -import dagger.ObjectGraph; +import com.google.inject.Inject; +import com.google.inject.Singleton; /** * Serves the content of a branch. @@ -65,20 +65,24 @@ import dagger.ObjectGraph; * @author James Moger * */ -public class RawServlet extends DaggerServlet { +@Singleton +public class RawServlet extends HttpServlet { private static final long serialVersionUID = 1L; private transient Logger logger = LoggerFactory.getLogger(RawServlet.class); - private IRuntimeManager runtimeManager; + private final IRuntimeManager runtimeManager; - private IRepositoryManager repositoryManager; + private final IRepositoryManager repositoryManager; - @Override - protected void inject(ObjectGraph dagger) { - this.runtimeManager = dagger.get(IRuntimeManager.class); - this.repositoryManager = dagger.get(IRepositoryManager.class); + @Inject + public RawServlet( + IRuntimeManager runtimeManager, + IRepositoryManager repositoryManager) { + + this.runtimeManager = runtimeManager; + this.repositoryManager = repositoryManager; } /** @@ -230,9 +234,18 @@ public class RawServlet extends DaggerServlet { // requested a specific resource String file = StringUtils.getLastPathElement(requestedPath); try { - // query Tika for the content type - Tika tika = new Tika(); - String contentType = tika.detect(file); + String contentType; + + List<String> exts = runtimeManager.getSettings().getStrings(Keys.web.prettyPrintExtensions); + String ext = StringUtils.getFileExtension(file).toLowerCase(); + if (exts.contains(ext)) { + // extension is a registered text type for pretty printing + contentType = "text/plain"; + } else { + // query Tika for the content type + Tika tika = new Tika(); + contentType = tika.detect(file); + } if (contentType == null) { // ask the container for the content type @@ -244,7 +257,7 @@ public class RawServlet extends DaggerServlet { } } - if (isTextType(contentType)) { + if (isTextType(contentType) || isTextDataType(contentType)) { // load, interpret, and serve text content as UTF-8 String [] encodings = runtimeManager.getSettings().getStrings(Keys.web.blobEncodings).toArray(new String[0]); @@ -378,6 +391,13 @@ public class RawServlet extends DaggerServlet { return false; } + protected boolean isTextDataType(String contentType) { + if ("image/svg+xml".equals(contentType)) { + return true; + } + return false; + } + /** * Override all text types to be plain text. * diff --git a/src/main/java/com/gitblit/servlet/RobotsTxtServlet.java b/src/main/java/com/gitblit/servlet/RobotsTxtServlet.java index 9bd3b3c3..4e58d4de 100644 --- a/src/main/java/com/gitblit/servlet/RobotsTxtServlet.java +++ b/src/main/java/com/gitblit/servlet/RobotsTxtServlet.java @@ -18,32 +18,33 @@ package com.gitblit.servlet; import java.io.File;
import java.io.IOException;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.gitblit.Keys;
-import com.gitblit.dagger.DaggerServlet;
import com.gitblit.manager.IRuntimeManager;
import com.gitblit.utils.FileUtils;
-import dagger.ObjectGraph;
-
/**
* Handles requests for robots.txt
*
* @author James Moger
*
*/
-public class RobotsTxtServlet extends DaggerServlet {
+@Singleton
+public class RobotsTxtServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private IRuntimeManager runtimeManager;
- @Override
- protected void inject(ObjectGraph dagger) {
- this.runtimeManager = dagger.get(IRuntimeManager.class);
+ @Inject
+ public RobotsTxtServlet(IRuntimeManager runtimeManager) {
+ this.runtimeManager = runtimeManager;
}
@Override
diff --git a/src/main/java/com/gitblit/servlet/RpcFilter.java b/src/main/java/com/gitblit/servlet/RpcFilter.java index 23bf956e..34474d55 100644 --- a/src/main/java/com/gitblit/servlet/RpcFilter.java +++ b/src/main/java/com/gitblit/servlet/RpcFilter.java @@ -18,8 +18,9 @@ package com.gitblit.servlet; import java.io.IOException;
import java.text.MessageFormat;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
@@ -29,11 +30,10 @@ import javax.servlet.http.HttpServletResponse; import com.gitblit.Constants.RpcRequest;
import com.gitblit.IStoredSettings;
import com.gitblit.Keys;
+import com.gitblit.manager.IAuthenticationManager;
import com.gitblit.manager.IRuntimeManager;
import com.gitblit.models.UserModel;
-import dagger.ObjectGraph;
-
/**
* The RpcFilter is a servlet filter that secures the RpcServlet.
*
@@ -47,17 +47,23 @@ import dagger.ObjectGraph; * @author James Moger
*
*/
+@Singleton
public class RpcFilter extends AuthenticationFilter {
private IStoredSettings settings;
private IRuntimeManager runtimeManager;
- @Override
- protected void inject(ObjectGraph dagger, FilterConfig filterConfig) {
- super.inject(dagger, filterConfig);
- this.settings = dagger.get(IStoredSettings.class);
- this.runtimeManager = dagger.get(IRuntimeManager.class);
+ @Inject
+ public RpcFilter(
+ IStoredSettings settings,
+ IRuntimeManager runtimeManager,
+ IAuthenticationManager authenticationManager) {
+
+ super(authenticationManager);
+
+ this.settings = settings;
+ this.runtimeManager = runtimeManager;
}
/**
diff --git a/src/main/java/com/gitblit/servlet/RpcServlet.java b/src/main/java/com/gitblit/servlet/RpcServlet.java index b8cdfb04..9809a252 100644 --- a/src/main/java/com/gitblit/servlet/RpcServlet.java +++ b/src/main/java/com/gitblit/servlet/RpcServlet.java @@ -23,6 +23,8 @@ import java.util.HashMap; import java.util.List;
import java.util.Map;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -47,13 +49,12 @@ import com.gitblit.utils.JGitUtils; import com.gitblit.utils.RpcUtils;
import com.gitblit.utils.StringUtils;
-import dagger.ObjectGraph;
-
/**
* Handles remote procedure calls.
*
* @author James Moger
*/
+@Singleton
public class RpcServlet extends JsonServlet {
private static final long serialVersionUID = 1L;
@@ -64,10 +65,10 @@ public class RpcServlet extends JsonServlet { private IGitblit gitblit;
- @Override
- protected void inject(ObjectGraph dagger) {
- this.settings = dagger.get(IStoredSettings.class);
- this.gitblit = dagger.get(IGitblit.class);
+ @Inject
+ public RpcServlet(IStoredSettings settings, IGitblit gitblit) {
+ this.settings = settings;
+ this.gitblit = gitblit;
}
/**
diff --git a/src/main/java/com/gitblit/servlet/SparkleShareInviteServlet.java b/src/main/java/com/gitblit/servlet/SparkleShareInviteServlet.java index 150dd68a..69c254cb 100644 --- a/src/main/java/com/gitblit/servlet/SparkleShareInviteServlet.java +++ b/src/main/java/com/gitblit/servlet/SparkleShareInviteServlet.java @@ -19,13 +19,15 @@ import java.io.IOException; import java.net.URL;
import java.text.MessageFormat;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.gitblit.IStoredSettings;
import com.gitblit.Keys;
-import com.gitblit.dagger.DaggerServlet;
import com.gitblit.manager.IAuthenticationManager;
import com.gitblit.manager.IRepositoryManager;
import com.gitblit.manager.IUserManager;
@@ -33,15 +35,14 @@ import com.gitblit.models.RepositoryModel; import com.gitblit.models.UserModel;
import com.gitblit.utils.StringUtils;
-import dagger.ObjectGraph;
-
/**
* Handles requests for Sparkleshare Invites
*
* @author James Moger
*
*/
-public class SparkleShareInviteServlet extends DaggerServlet {
+@Singleton
+public class SparkleShareInviteServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@@ -53,12 +54,17 @@ public class SparkleShareInviteServlet extends DaggerServlet { private IRepositoryManager repositoryManager;
- @Override
- protected void inject(ObjectGraph dagger) {
- this.settings = dagger.get(IStoredSettings.class);
- this.userManager = dagger.get(IUserManager.class);
- this.authenticationManager = dagger.get(IAuthenticationManager.class);
- this.repositoryManager = dagger.get(IRepositoryManager.class);
+ @Inject
+ public SparkleShareInviteServlet(
+ IStoredSettings settings,
+ IUserManager userManager,
+ IAuthenticationManager authenticationManager,
+ IRepositoryManager repositoryManager) {
+
+ this.settings = settings;
+ this.userManager = userManager;
+ this.authenticationManager = authenticationManager;
+ this.repositoryManager = repositoryManager;
}
@Override
diff --git a/src/main/java/com/gitblit/servlet/SyndicationFilter.java b/src/main/java/com/gitblit/servlet/SyndicationFilter.java index 78da47e9..49348d0d 100644 --- a/src/main/java/com/gitblit/servlet/SyndicationFilter.java +++ b/src/main/java/com/gitblit/servlet/SyndicationFilter.java @@ -18,8 +18,9 @@ package com.gitblit.servlet; import java.io.IOException;
import java.text.MessageFormat;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
@@ -27,6 +28,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;
import com.gitblit.Constants.AccessRestrictionType;
+import com.gitblit.manager.IAuthenticationManager;
import com.gitblit.manager.IProjectManager;
import com.gitblit.manager.IRepositoryManager;
import com.gitblit.manager.IRuntimeManager;
@@ -34,8 +36,6 @@ import com.gitblit.models.ProjectModel; import com.gitblit.models.RepositoryModel;
import com.gitblit.models.UserModel;
-import dagger.ObjectGraph;
-
/**
* The SyndicationFilter is an AuthenticationFilter which ensures that feed
* requests for projects or view-restricted repositories have proper authentication
@@ -44,18 +44,24 @@ import dagger.ObjectGraph; * @author James Moger
*
*/
+@Singleton
public class SyndicationFilter extends AuthenticationFilter {
private IRuntimeManager runtimeManager;
private IRepositoryManager repositoryManager;
private IProjectManager projectManager;
- @Override
- protected void inject(ObjectGraph dagger, FilterConfig filterConfig) {
- super.inject(dagger, filterConfig);
- this.runtimeManager = dagger.get(IRuntimeManager.class);
- this.repositoryManager = dagger.get(IRepositoryManager.class);
- this.projectManager = dagger.get(IProjectManager.class);
+ @Inject
+ public SyndicationFilter(
+ IRuntimeManager runtimeManager,
+ IAuthenticationManager authenticationManager,
+ IRepositoryManager repositoryManager,
+ IProjectManager projectManager) {
+ super(authenticationManager);
+
+ this.runtimeManager = runtimeManager;
+ this.repositoryManager = repositoryManager;
+ this.projectManager = projectManager;
}
/**
diff --git a/src/main/java/com/gitblit/servlet/SyndicationServlet.java b/src/main/java/com/gitblit/servlet/SyndicationServlet.java index e3c25967..c6343d9f 100644 --- a/src/main/java/com/gitblit/servlet/SyndicationServlet.java +++ b/src/main/java/com/gitblit/servlet/SyndicationServlet.java @@ -22,6 +22,10 @@ import java.util.Collections; import java.util.List;
import java.util.Map;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import javax.servlet.http.HttpServlet;
+
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
@@ -31,7 +35,6 @@ import org.slf4j.LoggerFactory; import com.gitblit.Constants;
import com.gitblit.IStoredSettings;
import com.gitblit.Keys;
-import com.gitblit.dagger.DaggerServlet;
import com.gitblit.manager.IProjectManager;
import com.gitblit.manager.IRepositoryManager;
import com.gitblit.models.FeedEntryModel;
@@ -46,8 +49,6 @@ import com.gitblit.utils.JGitUtils; import com.gitblit.utils.StringUtils;
import com.gitblit.utils.SyndicationUtils;
-import dagger.ObjectGraph;
-
/**
* SyndicationServlet generates RSS 2.0 feeds and feed links.
*
@@ -56,7 +57,8 @@ import dagger.ObjectGraph; * @author James Moger
*
*/
-public class SyndicationServlet extends DaggerServlet {
+@Singleton
+public class SyndicationServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@@ -68,11 +70,15 @@ public class SyndicationServlet extends DaggerServlet { private IProjectManager projectManager;
- @Override
- protected void inject(ObjectGraph dagger) {
- this.settings = dagger.get(IStoredSettings.class);
- this.repositoryManager = dagger.get(IRepositoryManager.class);
- this.projectManager = dagger.get(IProjectManager.class);
+ @Inject
+ public SyndicationServlet(
+ IStoredSettings settings,
+ IRepositoryManager repositoryManager,
+ IProjectManager projectManager) {
+
+ this.settings = settings;
+ this.repositoryManager = repositoryManager;
+ this.projectManager = projectManager;
}
/**
diff --git a/src/main/java/com/gitblit/tickets/BranchTicketService.java b/src/main/java/com/gitblit/tickets/BranchTicketService.java index 5a42c6a7..42189bfd 100644 --- a/src/main/java/com/gitblit/tickets/BranchTicketService.java +++ b/src/main/java/com/gitblit/tickets/BranchTicketService.java @@ -72,6 +72,8 @@ import com.gitblit.models.TicketModel.Change; import com.gitblit.utils.ArrayUtils; import com.gitblit.utils.JGitUtils; import com.gitblit.utils.StringUtils; +import com.google.inject.Inject; +import com.google.inject.Singleton; /** * Implementation of a ticket service based on an orphan branch. All tickets @@ -81,6 +83,7 @@ import com.gitblit.utils.StringUtils; * @author James Moger * */ +@Singleton public class BranchTicketService extends ITicketService implements RefsChangedListener { public static final String BRANCH = "refs/meta/gitblit/tickets"; @@ -91,6 +94,7 @@ public class BranchTicketService extends ITicketService implements RefsChangedLi private final Map<String, AtomicLong> lastAssignedId; + @Inject public BranchTicketService( IRuntimeManager runtimeManager, IPluginManager pluginManager, @@ -112,6 +116,7 @@ public class BranchTicketService extends ITicketService implements RefsChangedLi @Override public BranchTicketService start() { + log.info("{} started", getClass().getSimpleName()); return this; } diff --git a/src/main/java/com/gitblit/tickets/FileTicketService.java b/src/main/java/com/gitblit/tickets/FileTicketService.java index b3d8838e..1e82f0de 100644 --- a/src/main/java/com/gitblit/tickets/FileTicketService.java +++ b/src/main/java/com/gitblit/tickets/FileTicketService.java @@ -42,6 +42,8 @@ import com.gitblit.models.TicketModel.Change; import com.gitblit.utils.ArrayUtils; import com.gitblit.utils.FileUtils; import com.gitblit.utils.StringUtils; +import com.google.inject.Inject; +import com.google.inject.Singleton; /** * Implementation of a ticket service based on a directory within the repository. @@ -51,6 +53,7 @@ import com.gitblit.utils.StringUtils; * @author James Moger * */ +@Singleton public class FileTicketService extends ITicketService { private static final String JOURNAL = "journal.json"; @@ -59,6 +62,7 @@ public class FileTicketService extends ITicketService { private final Map<String, AtomicLong> lastAssignedId; + @Inject public FileTicketService( IRuntimeManager runtimeManager, IPluginManager pluginManager, @@ -77,6 +81,7 @@ public class FileTicketService extends ITicketService { @Override public FileTicketService start() { + log.info("{} started", getClass().getSimpleName()); return this; } @@ -488,6 +493,10 @@ public class FileTicketService extends ITicketService { @Override protected boolean deleteAllImpl(RepositoryModel repository) { Repository db = repositoryManager.getRepository(repository.name); + if (db == null) { + // the tickets no longer exist because the db no longer exists + return true; + } try { File dir = new File(db.getDirectory(), TICKETS_PATH); return FileUtils.delete(dir); diff --git a/src/main/java/com/gitblit/tickets/ITicketService.java b/src/main/java/com/gitblit/tickets/ITicketService.java index 4cf099ff..5e3e372a 100644 --- a/src/main/java/com/gitblit/tickets/ITicketService.java +++ b/src/main/java/com/gitblit/tickets/ITicketService.java @@ -36,6 +36,7 @@ import org.slf4j.LoggerFactory; import com.gitblit.IStoredSettings; import com.gitblit.Keys; import com.gitblit.extensions.TicketHook; +import com.gitblit.manager.IManager; import com.gitblit.manager.INotificationManager; import com.gitblit.manager.IPluginManager; import com.gitblit.manager.IRepositoryManager; @@ -63,7 +64,7 @@ import com.google.common.cache.CacheBuilder; * @author James Moger * */ -public abstract class ITicketService { +public abstract class ITicketService implements IManager { public static final String SETTING_UPDATE_DIFFSTATS = "migration.updateDiffstats"; @@ -176,12 +177,14 @@ public abstract class ITicketService { * Start the service. * @since 1.4.0 */ + @Override public abstract ITicketService start(); /** * Stop the service. * @since 1.4.0 */ + @Override public final ITicketService stop() { indexer.close(); ticketsCache.invalidateAll(); diff --git a/src/main/java/com/gitblit/tickets/NullTicketService.java b/src/main/java/com/gitblit/tickets/NullTicketService.java index d410cdd0..3947b945 100644 --- a/src/main/java/com/gitblit/tickets/NullTicketService.java +++ b/src/main/java/com/gitblit/tickets/NullTicketService.java @@ -28,6 +28,8 @@ import com.gitblit.models.RepositoryModel; import com.gitblit.models.TicketModel; import com.gitblit.models.TicketModel.Attachment; import com.gitblit.models.TicketModel.Change; +import com.google.inject.Inject; +import com.google.inject.Singleton; /** * Implementation of a ticket service that rejects everything. @@ -35,8 +37,10 @@ import com.gitblit.models.TicketModel.Change; * @author James Moger * */ +@Singleton public class NullTicketService extends ITicketService { + @Inject public NullTicketService( IRuntimeManager runtimeManager, IPluginManager pluginManager, @@ -58,6 +62,7 @@ public class NullTicketService extends ITicketService { @Override public NullTicketService start() { + log.info("{} started", getClass().getSimpleName()); return this; } diff --git a/src/main/java/com/gitblit/tickets/QueryResult.java b/src/main/java/com/gitblit/tickets/QueryResult.java index 7a2b1abe..f8d6d123 100644 --- a/src/main/java/com/gitblit/tickets/QueryResult.java +++ b/src/main/java/com/gitblit/tickets/QueryResult.java @@ -24,6 +24,8 @@ import java.util.List; import com.gitblit.models.TicketModel.Patchset; import com.gitblit.models.TicketModel.Status; import com.gitblit.models.TicketModel.Type; +import com.gitblit.models.TicketModel.Priority; +import com.gitblit.models.TicketModel.Severity; import com.gitblit.utils.StringUtils; /** @@ -62,6 +64,8 @@ public class QueryResult implements Serializable { public int commentsCount; public int votesCount; public int approvalsCount; + public Priority priority; + public Severity severity; public int docId; public int totalResults; diff --git a/src/main/java/com/gitblit/tickets/RedisTicketService.java b/src/main/java/com/gitblit/tickets/RedisTicketService.java index d773b0bd..0f9ad174 100644 --- a/src/main/java/com/gitblit/tickets/RedisTicketService.java +++ b/src/main/java/com/gitblit/tickets/RedisTicketService.java @@ -43,6 +43,8 @@ import com.gitblit.models.TicketModel.Attachment; import com.gitblit.models.TicketModel.Change; import com.gitblit.utils.ArrayUtils; import com.gitblit.utils.StringUtils; +import com.google.inject.Inject; +import com.google.inject.Singleton; /** * Implementation of a ticket service based on a Redis key-value store. All @@ -53,6 +55,7 @@ import com.gitblit.utils.StringUtils; * @author James Moger * */ +@Singleton public class RedisTicketService extends ITicketService { private final JedisPool pool; @@ -61,6 +64,7 @@ public class RedisTicketService extends ITicketService { journal, ticket, counter } + @Inject public RedisTicketService( IRuntimeManager runtimeManager, IPluginManager pluginManager, @@ -80,6 +84,10 @@ public class RedisTicketService extends ITicketService { @Override public RedisTicketService start() { + log.info("{} started", getClass().getSimpleName()); + if (!isReady()) { + log.warn("{} is not ready!", getClass().getSimpleName()); + } return this; } diff --git a/src/main/java/com/gitblit/tickets/TicketIndexer.java b/src/main/java/com/gitblit/tickets/TicketIndexer.java index 11ea3a73..e2d53af7 100644 --- a/src/main/java/com/gitblit/tickets/TicketIndexer.java +++ b/src/main/java/com/gitblit/tickets/TicketIndexer.java @@ -103,7 +103,10 @@ public class TicketIndexer { mergesha(Type.STRING), mergeto(Type.STRING), patchsets(Type.INT), - votes(Type.INT); + votes(Type.INT), + //NOTE: Indexing on the underlying value to allow flexibility on naming + priority(Type.INT), + severity(Type.INT); final Type fieldType; @@ -519,6 +522,8 @@ public class TicketIndexer { toDocField(doc, Lucene.watchedby, StringUtils.flattenStrings(ticket.getWatchers(), ";").toLowerCase()); toDocField(doc, Lucene.mentions, StringUtils.flattenStrings(ticket.getMentions(), ";").toLowerCase()); toDocField(doc, Lucene.votes, ticket.getVoters().size()); + toDocField(doc, Lucene.priority, ticket.priority.getValue()); + toDocField(doc, Lucene.severity, ticket.severity.getValue()); List<String> attachments = new ArrayList<String>(); for (Attachment attachment : ticket.getAttachments()) { @@ -600,6 +605,8 @@ public class TicketIndexer { result.participants = unpackStrings(doc, Lucene.participants); result.watchedby = unpackStrings(doc, Lucene.watchedby); result.mentions = unpackStrings(doc, Lucene.mentions); + result.priority = TicketModel.Priority.fromObject(unpackInt(doc, Lucene.priority), TicketModel.Priority.defaultPriority); + result.severity = TicketModel.Severity.fromObject(unpackInt(doc, Lucene.severity), TicketModel.Severity.defaultSeverity); if (!StringUtils.isEmpty(doc.get(Lucene.patchset.name()))) { // unpack most recent patchset diff --git a/src/main/java/com/gitblit/tickets/TicketNotifier.java b/src/main/java/com/gitblit/tickets/TicketNotifier.java index d6217b35..5979cf26 100644 --- a/src/main/java/com/gitblit/tickets/TicketNotifier.java +++ b/src/main/java/com/gitblit/tickets/TicketNotifier.java @@ -135,6 +135,7 @@ public class TicketNotifier { StringBuilder html = new StringBuilder(); html.append("<head>"); html.append(readStyle()); + html.append(readViewTicketAction(ticket)); html.append("</head>"); html.append("<body>"); html.append(MarkdownUtils.transformGFM(settings, markdown, ticket.repository)); @@ -613,6 +614,12 @@ public class TicketNotifier { return sb.toString(); } + protected String readViewTicketAction(TicketModel ticket) { + String action = readResource("viewTicket.html"); + action = action.replace("${url}", ticketService.getTicketUrl(ticket)); + return action; + } + protected String readResource(String resource) { StringBuilder sb = new StringBuilder(); InputStream is = null; diff --git a/src/main/java/com/gitblit/tickets/viewTicket.html b/src/main/java/com/gitblit/tickets/viewTicket.html new file mode 100644 index 00000000..54e091ca --- /dev/null +++ b/src/main/java/com/gitblit/tickets/viewTicket.html @@ -0,0 +1,12 @@ +<script type="application/ld+json"> +{ + "@context": "http://schema.org", + "@type": "EmailMessage", + "description": "View this Ticket in Gitblit", + "action": { + "@type": "ViewAction", + "url": "${url}", + "name": "View Ticket" + } +} +</script> diff --git a/src/main/java/com/gitblit/transport/ssh/FileKeyManager.java b/src/main/java/com/gitblit/transport/ssh/FileKeyManager.java index a063dc7d..1a2cd682 100644 --- a/src/main/java/com/gitblit/transport/ssh/FileKeyManager.java +++ b/src/main/java/com/gitblit/transport/ssh/FileKeyManager.java @@ -29,6 +29,7 @@ import com.gitblit.manager.IRuntimeManager; import com.google.common.base.Charsets; import com.google.common.base.Joiner; import com.google.common.io.Files; +import com.google.inject.Inject; /** * Manages public keys on the filesystem. @@ -42,6 +43,7 @@ public class FileKeyManager extends IPublicKeyManager { protected final Map<File, Long> lastModifieds; + @Inject public FileKeyManager(IRuntimeManager runtimeManager) { this.runtimeManager = runtimeManager; this.lastModifieds = new ConcurrentHashMap<File, Long>(); diff --git a/src/main/java/com/gitblit/transport/ssh/MemoryKeyManager.java b/src/main/java/com/gitblit/transport/ssh/MemoryKeyManager.java index 357b34a2..bf783786 100644 --- a/src/main/java/com/gitblit/transport/ssh/MemoryKeyManager.java +++ b/src/main/java/com/gitblit/transport/ssh/MemoryKeyManager.java @@ -20,6 +20,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import com.google.inject.Inject; + /** * Memory public key manager. * @@ -30,6 +32,7 @@ public class MemoryKeyManager extends IPublicKeyManager { final Map<String, List<SshKey>> keys; + @Inject public MemoryKeyManager() { keys = new HashMap<String, List<SshKey>>(); } diff --git a/src/main/java/com/gitblit/transport/ssh/NullKeyManager.java b/src/main/java/com/gitblit/transport/ssh/NullKeyManager.java index 0761d842..fcd3e197 100644 --- a/src/main/java/com/gitblit/transport/ssh/NullKeyManager.java +++ b/src/main/java/com/gitblit/transport/ssh/NullKeyManager.java @@ -17,6 +17,8 @@ package com.gitblit.transport.ssh; import java.util.List; +import com.google.inject.Inject; + /** * Rejects all public key management requests. * @@ -25,6 +27,7 @@ import java.util.List; */ public class NullKeyManager extends IPublicKeyManager { + @Inject public NullKeyManager() { } diff --git a/src/main/java/com/gitblit/transport/ssh/SshDaemon.java b/src/main/java/com/gitblit/transport/ssh/SshDaemon.java index 6bcc0390..9667154f 100644 --- a/src/main/java/com/gitblit/transport/ssh/SshDaemon.java +++ b/src/main/java/com/gitblit/transport/ssh/SshDaemon.java @@ -31,6 +31,7 @@ import org.apache.sshd.common.io.mina.MinaServiceFactoryFactory; import org.apache.sshd.common.io.nio2.Nio2ServiceFactoryFactory; import org.apache.sshd.common.keyprovider.FileKeyPairProvider; import org.apache.sshd.common.util.SecurityUtils; +import org.apache.sshd.server.auth.CachingPublicKeyAuthenticator; import org.bouncycastle.openssl.PEMWriter; import org.eclipse.jgit.internal.JGitText; import org.slf4j.Logger; @@ -98,8 +99,8 @@ public class SshDaemon { hostKeyPairProvider.setFiles(new String [] { rsaKeyStore.getPath(), dsaKeyStore.getPath(), dsaKeyStore.getPath() }); // Client public key authenticator - CachingPublicKeyAuthenticator keyAuthenticator = - new CachingPublicKeyAuthenticator(gitblit.getPublicKeyManager(), gitblit); + SshKeyAuthenticator keyAuthenticator = + new SshKeyAuthenticator(gitblit.getPublicKeyManager(), gitblit); // Configure the preferred SSHD backend String sshBackendStr = settings.getString(Keys.git.sshBackend, @@ -125,7 +126,7 @@ public class SshDaemon { sshd.setPort(addr.getPort()); sshd.setHost(addr.getHostName()); sshd.setKeyPairProvider(hostKeyPairProvider); - sshd.setPublickeyAuthenticator(keyAuthenticator); + sshd.setPublickeyAuthenticator(new CachingPublicKeyAuthenticator(keyAuthenticator)); sshd.setPasswordAuthenticator(new UsernamePasswordAuthenticator(gitblit)); sshd.setSessionFactory(new SshServerSessionFactory()); sshd.setFileSystemFactory(new DisabledFilesystemFactory()); diff --git a/src/main/java/com/gitblit/transport/ssh/CachingPublicKeyAuthenticator.java b/src/main/java/com/gitblit/transport/ssh/SshKeyAuthenticator.java index e804a0da..c28a2ed6 100644 --- a/src/main/java/com/gitblit/transport/ssh/CachingPublicKeyAuthenticator.java +++ b/src/main/java/com/gitblit/transport/ssh/SshKeyAuthenticator.java @@ -16,14 +16,9 @@ package com.gitblit.transport.ssh; import java.security.PublicKey; -import java.util.HashMap; import java.util.List; import java.util.Locale; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import org.apache.sshd.common.Session; -import org.apache.sshd.common.SessionListener; import org.apache.sshd.server.PublickeyAuthenticator; import org.apache.sshd.server.session.ServerSession; import org.slf4j.Logger; @@ -37,7 +32,7 @@ import com.google.common.base.Preconditions; * Authenticates an SSH session against a public key. * */ -public class CachingPublicKeyAuthenticator implements PublickeyAuthenticator, SessionListener { +public class SshKeyAuthenticator implements PublickeyAuthenticator { protected final Logger log = LoggerFactory.getLogger(getClass()); @@ -45,30 +40,13 @@ public class CachingPublicKeyAuthenticator implements PublickeyAuthenticator, Se protected final IAuthenticationManager authManager; - private final Map<ServerSession, Map<PublicKey, Boolean>> cache = new ConcurrentHashMap<ServerSession, Map<PublicKey, Boolean>>(); - - public CachingPublicKeyAuthenticator(IPublicKeyManager keyManager, IAuthenticationManager authManager) { + public SshKeyAuthenticator(IPublicKeyManager keyManager, IAuthenticationManager authManager) { this.keyManager = keyManager; this.authManager = authManager; } @Override - public boolean authenticate(String username, PublicKey key, ServerSession session) { - Map<PublicKey, Boolean> map = cache.get(session); - if (map == null) { - map = new HashMap<PublicKey, Boolean>(); - cache.put(session, map); - session.addListener(this); - } - if (map.containsKey(key)) { - return map.get(key); - } - boolean result = doAuthenticate(username, key, session); - map.put(key, result); - return result; - } - - private boolean doAuthenticate(String username, PublicKey suppliedKey, ServerSession session) { + public boolean authenticate(String username, PublicKey suppliedKey, ServerSession session) { SshDaemonClient client = session.getAttribute(SshDaemonClient.KEY); Preconditions.checkState(client.getUser() == null); username = username.toLowerCase(Locale.US); @@ -96,17 +74,4 @@ public class CachingPublicKeyAuthenticator implements PublickeyAuthenticator, Se log.warn("could not authenticate {} for SSH using the supplied public key", username); return false; } - - @Override - public void sessionCreated(Session session) { - } - - @Override - public void sessionEvent(Session sesssion, Event event) { - } - - @Override - public void sessionClosed(Session session) { - cache.remove(session); - } } diff --git a/src/main/java/com/gitblit/utils/DiffUtils.java b/src/main/java/com/gitblit/utils/DiffUtils.java index dd2a7807..b30a203d 100644 --- a/src/main/java/com/gitblit/utils/DiffUtils.java +++ b/src/main/java/com/gitblit/utils/DiffUtils.java @@ -52,6 +52,27 @@ public class DiffUtils { private static final Logger LOGGER = LoggerFactory.getLogger(DiffUtils.class);
/**
+ * Callback interface for binary diffs. All the getDiff methods here take an optional handler;
+ * if given and the {@link DiffOutputType} is {@link DiffOutputType#HTML HTML}, it is responsible
+ * for displaying a binary diff.
+ */
+ public interface BinaryDiffHandler {
+
+ /**
+ * Renders a binary diff. The result must be valid HTML, it will be inserted into an HTML table cell.
+ * May return {@code null} if the default behavior (which is typically just a textual note "Bnary
+ * files differ") is desired.
+ *
+ * @param diffEntry
+ * current diff entry
+ *
+ * @return the rendered diff as HTML, or {@code null} if the default is desired.
+ */
+ public String renderBinaryDiff(final DiffEntry diffEntry);
+
+ }
+
+ /**
* Enumeration for the diff output types.
*/
public static enum DiffOutputType {
@@ -181,6 +202,23 @@ public class DiffUtils { }
/**
+ * Returns the complete diff of the specified commit compared to its primary parent.
+ *
+ * @param repository
+ * @param commit
+ * @param outputType
+ * @param handler
+ * to use for rendering binary diffs if {@code outputType} is {@link DiffOutputType#HTML HTML}.
+ * May be {@code null}, resulting in the default behavior.
+ * @return the diff
+ */
+ public static DiffOutput getCommitDiff(Repository repository, RevCommit commit,
+ DiffOutputType outputType, BinaryDiffHandler handler) {
+ return getDiff(repository, null, commit, null, outputType, handler);
+ }
+
+
+ /**
* Returns the diff for the specified file or folder from the specified
* commit compared to its primary parent.
*
@@ -196,6 +234,24 @@ public class DiffUtils { }
/**
+ * Returns the diff for the specified file or folder from the specified
+ * commit compared to its primary parent.
+ *
+ * @param repository
+ * @param commit
+ * @param path
+ * @param outputType
+ * @param handler
+ * to use for rendering binary diffs if {@code outputType} is {@link DiffOutputType#HTML HTML}.
+ * May be {@code null}, resulting in the default behavior.
+ * @return the diff
+ */
+ public static DiffOutput getDiff(Repository repository, RevCommit commit, String path,
+ DiffOutputType outputType, BinaryDiffHandler handler) {
+ return getDiff(repository, null, commit, path, outputType, handler);
+ }
+
+ /**
* Returns the complete diff between the two specified commits.
*
* @param repository
@@ -210,6 +266,23 @@ public class DiffUtils { }
/**
+ * Returns the complete diff between the two specified commits.
+ *
+ * @param repository
+ * @param baseCommit
+ * @param commit
+ * @param outputType
+ * @param handler
+ * to use for rendering binary diffs if {@code outputType} is {@link DiffOutputType#HTML HTML}.
+ * May be {@code null}, resulting in the default behavior.
+ * @return the diff
+ */
+ public static DiffOutput getDiff(Repository repository, RevCommit baseCommit, RevCommit commit,
+ DiffOutputType outputType, BinaryDiffHandler handler) {
+ return getDiff(repository, baseCommit, commit, null, outputType, handler);
+ }
+
+ /**
* Returns the diff between two commits for the specified file.
*
* @param repository
@@ -225,18 +298,41 @@ public class DiffUtils { */
public static DiffOutput getDiff(Repository repository, RevCommit baseCommit, RevCommit commit,
String path, DiffOutputType outputType) {
+ return getDiff(repository, baseCommit, commit, path, outputType, null);
+ }
+
+ /**
+ * Returns the diff between two commits for the specified file.
+ *
+ * @param repository
+ * @param baseCommit
+ * if base commit is null the diff is to the primary parent of
+ * the commit.
+ * @param commit
+ * @param path
+ * if the path is specified, the diff is restricted to that file
+ * or folder. if unspecified, the diff is for the entire commit.
+ * @param outputType
+ * @param handler
+ * to use for rendering binary diffs if {@code outputType} is {@link DiffOutputType#HTML HTML}.
+ * May be {@code null}, resulting in the default behavior.
+ * @return the diff
+ */
+ public static DiffOutput getDiff(Repository repository, RevCommit baseCommit, RevCommit commit, String path, DiffOutputType outputType,
+ final BinaryDiffHandler handler) {
DiffStat stat = null;
String diff = null;
try {
- final ByteArrayOutputStream os = new ByteArrayOutputStream();
+ ByteArrayOutputStream os = null;
RawTextComparator cmp = RawTextComparator.DEFAULT;
DiffFormatter df;
switch (outputType) {
case HTML:
- df = new GitBlitDiffFormatter(os, commit.getName());
+ df = new GitBlitDiffFormatter(commit.getName(), path, handler);
break;
case PLAIN:
default:
+ os = new ByteArrayOutputStream();
df = new DiffFormatter(os);
break;
}
@@ -271,6 +367,7 @@ public class DiffUtils { } else {
df.format(diffEntries);
}
+ df.flush();
if (df instanceof GitBlitDiffFormatter) {
// workaround for complex private methods in DiffFormatter
diff = ((GitBlitDiffFormatter) df).getHtml();
@@ -278,7 +375,6 @@ public class DiffUtils { } else {
diff = os.toString();
}
- df.flush();
} catch (Throwable t) {
LOGGER.error("failed to generate commit diff!", t);
}
diff --git a/src/main/java/com/gitblit/utils/GitBlitDiffFormatter.java b/src/main/java/com/gitblit/utils/GitBlitDiffFormatter.java index 47ff143a..3c65267b 100644 --- a/src/main/java/com/gitblit/utils/GitBlitDiffFormatter.java +++ b/src/main/java/com/gitblit/utils/GitBlitDiffFormatter.java @@ -1,236 +1,529 @@ -/*
- * 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.utils;
-
-import static org.eclipse.jgit.lib.Constants.encode;
-import static org.eclipse.jgit.lib.Constants.encodeASCII;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.text.MessageFormat;
-
-import org.eclipse.jgit.diff.DiffEntry;
-import org.eclipse.jgit.diff.DiffFormatter;
-import org.eclipse.jgit.diff.RawText;
-import org.eclipse.jgit.util.RawParseUtils;
-
-import com.gitblit.models.PathModel.PathChangeModel;
-import com.gitblit.utils.DiffUtils.DiffStat;
-
-/**
- * Generates an html snippet of a diff in Gitblit's style, tracks changed paths,
- * and calculates diff stats.
- *
- * @author James Moger
- *
- */
-public class GitBlitDiffFormatter extends DiffFormatter {
-
- private final OutputStream os;
-
- private final DiffStat diffStat;
-
- private PathChangeModel currentPath;
-
- private int left, right;
-
- public GitBlitDiffFormatter(OutputStream os, String commitId) {
- super(os);
- this.os = os;
- this.diffStat = new DiffStat(commitId);
- }
-
- @Override
- public void format(DiffEntry ent) throws IOException {
- currentPath = diffStat.addPath(ent);
- super.format(ent);
- }
-
- /**
- * Output a hunk header
- *
- * @param aStartLine
- * within first source
- * @param aEndLine
- * within first source
- * @param bStartLine
- * within second source
- * @param bEndLine
- * within second source
- * @throws IOException
- */
- @Override
- protected void writeHunkHeader(int aStartLine, int aEndLine, int bStartLine, int bEndLine)
- throws IOException {
- os.write("<tr><th>..</th><th>..</th><td class='hunk_header'>".getBytes());
- os.write('@');
- os.write('@');
- writeRange('-', aStartLine + 1, aEndLine - aStartLine);
- writeRange('+', bStartLine + 1, bEndLine - bStartLine);
- os.write(' ');
- os.write('@');
- os.write('@');
- os.write("</td></tr>\n".getBytes());
- left = aStartLine + 1;
- right = bStartLine + 1;
- }
-
- protected void writeRange(final char prefix, final int begin, final int cnt) throws IOException {
- os.write(' ');
- os.write(prefix);
- switch (cnt) {
- case 0:
- // If the range is empty, its beginning number must
- // be the
- // line just before the range, or 0 if the range is
- // at the
- // start of the file stream. Here, begin is always 1
- // based,
- // so an empty file would produce "0,0".
- //
- os.write(encodeASCII(begin - 1));
- os.write(',');
- os.write('0');
- break;
-
- case 1:
- // If the range is exactly one line, produce only
- // the number.
- //
- os.write(encodeASCII(begin));
- break;
-
- default:
- os.write(encodeASCII(begin));
- os.write(',');
- os.write(encodeASCII(cnt));
- break;
- }
- }
-
- @Override
- protected void writeLine(final char prefix, final RawText text, final int cur)
- throws IOException {
- // update entry diffstat
- currentPath.update(prefix);
-
- // output diff
- os.write("<tr>".getBytes());
- switch (prefix) {
- case '+':
- os.write(("<th></th><th>" + (right++) + "</th>").getBytes());
- os.write("<td><div class=\"diff add2\">".getBytes());
- break;
- case '-':
- os.write(("<th>" + (left++) + "</th><th></th>").getBytes());
- os.write("<td><div class=\"diff remove2\">".getBytes());
- break;
- default:
- os.write(("<th>" + (left++) + "</th><th>" + (right++) + "</th>").getBytes());
- os.write("<td>".getBytes());
- break;
- }
- os.write(prefix);
- String line = text.getString(cur);
- line = StringUtils.escapeForHtml(line, false);
- os.write(encode(line));
- switch (prefix) {
- case '+':
- case '-':
- os.write("</div>".getBytes());
- break;
- default:
- os.write("</td>".getBytes());
- }
- os.write("</tr>\n".getBytes());
- }
-
- /**
- * Workaround function for complex private methods in DiffFormatter. This
- * sets the html for the diff headers.
- *
- * @return
- */
- public String getHtml() {
- ByteArrayOutputStream bos = (ByteArrayOutputStream) os;
- String html = RawParseUtils.decode(bos.toByteArray());
- String[] lines = html.split("\n");
- StringBuilder sb = new StringBuilder();
- boolean inFile = false;
- String oldnull = "a/dev/null";
- for (String line : lines) {
- if (line.startsWith("index")) {
- // skip index lines
- } else if (line.startsWith("new file")) {
- // skip new file lines
- } else if (line.startsWith("\\ No newline")) {
- // skip no new line
- } else if (line.startsWith("---") || line.startsWith("+++")) {
- // skip --- +++ lines
- } else if (line.startsWith("diff")) {
- line = StringUtils.convertOctal(line);
- if (line.indexOf(oldnull) > -1) {
- // a is null, use b
- line = line.substring(("diff --git " + oldnull).length()).trim();
- // trim b/
- line = line.substring(2).trim();
- } else {
- // use a
- line = line.substring("diff --git ".length()).trim();
- line = line.substring(line.startsWith("\"a/") ? 3 : 2);
- line = line.substring(0, line.indexOf(" b/") > -1 ? line.indexOf(" b/") : line.indexOf("\"b/")).trim();
- }
-
- if (line.charAt(0) == '"') {
- line = line.substring(1);
- }
- if (line.charAt(line.length() - 1) == '"') {
- line = line.substring(0, line.length() - 1);
- }
- if (inFile) {
- sb.append("</tbody></table></div>\n");
- inFile = false;
- }
-
- sb.append(MessageFormat.format("<div class='header'><div class=\"diffHeader\" id=\"{0}\"><i class=\"icon-file\"></i> ", line)).append(line).append("</div></div>");
- sb.append("<div class=\"diff\">");
- sb.append("<table><tbody>");
- inFile = true;
- } else {
- boolean gitLinkDiff = line.length() > 0 && line.substring(1).startsWith("Subproject commit");
- if (gitLinkDiff) {
- sb.append("<tr><th></th><th></th>");
- if (line.charAt(0) == '+') {
- sb.append("<td><div class=\"diff add2\">");
- } else {
- sb.append("<td><div class=\"diff remove2\">");
- }
- }
- sb.append(line);
- if (gitLinkDiff) {
- sb.append("</div></td></tr>");
- }
- }
- }
- sb.append("</table></div>");
- return sb.toString();
- }
-
- public DiffStat getDiffStat() {
- return diffStat;
- }
-}
+/* + * 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.utils; + +import static org.eclipse.jgit.lib.Constants.encode; +import static org.eclipse.jgit.lib.Constants.encodeASCII; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.wicket.Application; +import org.apache.wicket.Localizer; +import org.eclipse.jgit.diff.DiffEntry; +import org.eclipse.jgit.diff.DiffEntry.ChangeType; +import org.eclipse.jgit.diff.DiffFormatter; +import org.eclipse.jgit.diff.RawText; +import org.eclipse.jgit.util.RawParseUtils; + +import com.gitblit.models.PathModel.PathChangeModel; +import com.gitblit.utils.DiffUtils.BinaryDiffHandler; +import com.gitblit.utils.DiffUtils.DiffStat; +import com.gitblit.wicket.GitBlitWebApp; + +/** + * Generates an html snippet of a diff in Gitblit's style, tracks changed paths, and calculates diff stats. + * + * @author James Moger + * @author Tom <tw201207@gmail.com> + * + */ +public class GitBlitDiffFormatter extends DiffFormatter { + + /** Regex pattern identifying trailing whitespace. */ + private static final Pattern trailingWhitespace = Pattern.compile("(\\s+?)\r?\n?$"); + + /** + * gitblit.properties key for the per-file limit on the number of diff lines. + */ + private static final String DIFF_LIMIT_PER_FILE_KEY = "web.maxDiffLinesPerFile"; + + /** + * gitblit.properties key for the global limit on the number of diff lines in a commitdiff. + */ + private static final String GLOBAL_DIFF_LIMIT_KEY = "web.maxDiffLines"; + + /** + * Diffs with more lines are not shown in commitdiffs. (Similar to what GitHub does.) Can be reduced + * (but not increased) through gitblit.properties key {@link #DIFF_LIMIT_PER_FILE_KEY}. + */ + private static final int DIFF_LIMIT_PER_FILE = 4000; + + /** + * Global diff limit. Commitdiffs with more lines are truncated. Can be reduced (but not increased) + * through gitblit.properties key {@link #GLOBAL_DIFF_LIMIT_KEY}. + */ + private static final int GLOBAL_DIFF_LIMIT = 20000; + + private final DiffOutputStream os; + + private final DiffStat diffStat; + + private PathChangeModel currentPath; + + private int left, right; + + /** + * If a single file diff in a commitdiff produces more than this number of lines, we don't display + * the diff. First, it's too taxing on the browser: it'll spend an awful lot of time applying the + * CSS rules (despite my having optimized them). And second, no human can read a diff with thousands + * of lines and make sense of it. + * <p> + * Set to {@link #DIFF_LIMIT_PER_FILE} for commitdiffs, and to -1 (switches off the limit) for + * single-file diffs. + * </p> + */ + private final int maxDiffLinesPerFile; + + /** + * Global limit on the number of diff lines. Set to {@link #GLOBAL_DIFF_LIMIT} for commitdiffs, and + * to -1 (switched off the limit) for single-file diffs. + */ + private final int globalDiffLimit; + + /** Number of lines for the current file diff. Set to zero when a new DiffEntry is started. */ + private int nofLinesCurrent; + /** + * Position in the stream when we try to write the first line. Used to rewind when we detect that + * the diff is too large. + */ + private int startCurrent; + /** Flag set to true when we rewind. Reset to false when we start a new DiffEntry. */ + private boolean isOff; + /** The current diff entry. */ + private DiffEntry entry; + + // Global limit stuff. + + /** Total number of lines written before the current diff entry. */ + private int totalNofLinesPrevious; + /** Running total of the number of diff lines written. Updated until we exceed the global limit. */ + private int totalNofLinesCurrent; + /** Stream position to reset to if we decided to truncate the commitdiff. */ + private int truncateTo; + /** Whether we decided to truncate the commitdiff. */ + private boolean truncated; + /** If {@link #truncated}, contains all entries skipped. */ + private final List<DiffEntry> skipped = new ArrayList<DiffEntry>(); + + /** + * A {@link ResettableByteArrayOutputStream} that intercept the "Binary files differ" message produced + * by the super implementation. Unfortunately the super implementation has far too many things private; + * otherwise we'd just have re-implemented {@link GitBlitDiffFormatter#format(DiffEntry) format(DiffEntry)} + * completely without ever calling the super implementation. + */ + private static class DiffOutputStream extends ResettableByteArrayOutputStream { + + private static final String BINARY_DIFFERENCE = "Binary files differ\n"; + + private GitBlitDiffFormatter formatter; + private BinaryDiffHandler binaryDiffHandler; + + public void setFormatter(GitBlitDiffFormatter formatter, BinaryDiffHandler handler) { + this.formatter = formatter; + this.binaryDiffHandler = handler; + } + + @Override + public void write(byte[] b, int offset, int length) { + if (binaryDiffHandler != null + && RawParseUtils.decode(Arrays.copyOfRange(b, offset, offset + length)).contains(BINARY_DIFFERENCE)) + { + String binaryDiff = binaryDiffHandler.renderBinaryDiff(formatter.entry); + if (binaryDiff != null) { + byte[] bb = ("<tr><td colspan='4' align='center'>" + binaryDiff + "</td></tr>").getBytes(StandardCharsets.UTF_8); + super.write(bb, 0, bb.length); + return; + } + } + super.write(b, offset, length); + } + + } + + public GitBlitDiffFormatter(String commitId, String path, BinaryDiffHandler handler) { + super(new DiffOutputStream()); + this.os = (DiffOutputStream) getOutputStream(); + this.os.setFormatter(this, handler); + this.diffStat = new DiffStat(commitId); + // If we have a full commitdiff, install maxima to avoid generating a super-long diff listing that + // will only tax the browser too much. + maxDiffLinesPerFile = path != null ? -1 : getLimit(DIFF_LIMIT_PER_FILE_KEY, 500, DIFF_LIMIT_PER_FILE); + globalDiffLimit = path != null ? -1 : getLimit(GLOBAL_DIFF_LIMIT_KEY, 1000, GLOBAL_DIFF_LIMIT); + } + + /** + * Determines a limit to use for HTML diff output. + * + * @param key + * to use to read the value from the GitBlit settings, if available. + * @param minimum + * minimum value to enforce + * @param maximum + * maximum (and default) value to enforce + * @return the limit + */ + private int getLimit(String key, int minimum, int maximum) { + if (Application.exists()) { + Application application = Application.get(); + if (application instanceof GitBlitWebApp) { + GitBlitWebApp webApp = (GitBlitWebApp) application; + int configValue = webApp.settings().getInteger(key, maximum); + if (configValue < minimum) { + return minimum; + } else if (configValue < maximum) { + return configValue; + } + } + } + return maximum; + } + + /** + * Returns a localized message string, if there is a localization; otherwise the given default value. + * + * @param key + * message key for the message + * @param defaultValue + * to use if no localization for the message can be found + * @return the possibly localized message + */ + private String getMsg(String key, String defaultValue) { + if (Application.exists()) { + Localizer localizer = Application.get().getResourceSettings().getLocalizer(); + if (localizer != null) { + // Use getStringIgnoreSettings because we don't want exceptions here if the key is missing! + return localizer.getStringIgnoreSettings(key, null, null, defaultValue); + } + } + return defaultValue; + } + + @Override + public void format(DiffEntry ent) throws IOException { + currentPath = diffStat.addPath(ent); + nofLinesCurrent = 0; + isOff = false; + entry = ent; + if (!truncated) { + totalNofLinesPrevious = totalNofLinesCurrent; + if (globalDiffLimit > 0 && totalNofLinesPrevious > globalDiffLimit) { + truncated = true; + isOff = true; + } + truncateTo = os.size(); + } else { + isOff = true; + } + if (truncated) { + skipped.add(ent); + } else { + // Produce a header here and now + String path; + String id; + if (ChangeType.DELETE.equals(ent.getChangeType())) { + path = ent.getOldPath(); + id = ent.getOldId().name(); + } else { + path = ent.getNewPath(); + id = ent.getNewId().name(); + } + StringBuilder sb = new StringBuilder(MessageFormat.format("<div class='header'><div class=\"diffHeader\" id=\"n{0}\"><i class=\"icon-file\"></i> ", id)); + sb.append(StringUtils.escapeForHtml(path, false)).append("</div></div>"); + sb.append("<div class=\"diff\"><table cellpadding='0'><tbody>\n"); + os.write(sb.toString().getBytes()); + } + // Keep formatting, but if off, don't produce anything anymore. We just keep on counting. + super.format(ent); + if (!truncated) { + // Close the table + os.write("</tbody></table></div>\n".getBytes()); + } + } + + @Override + public void flush() throws IOException { + if (truncated) { + os.resetTo(truncateTo); + } + super.flush(); + } + + /** + * Rewind and issue a message that the diff is too large. + */ + private void reset() { + if (!isOff) { + os.resetTo(startCurrent); + writeFullWidthLine(getMsg("gb.diffFileDiffTooLarge", "Diff too large")); + totalNofLinesCurrent = totalNofLinesPrevious; + isOff = true; + } + } + + /** + * Writes an initial table row containing information about added/removed/renamed/copied files. In case + * of a deletion, we also suppress generating the diff; it's not interesting. (All lines removed.) + */ + private void handleChange() { + // XXX Would be nice if we could generate blob links for the cases handled here. Alas, we lack the repo + // name, and cannot reliably determine it here. We could get the .git directory of a Repository, if we + // passed in the repo, and then take the name of the parent directory, but that'd fail for repos nested + // in GitBlit projects. And we don't know if the repo is inside a project or is a top-level repo. + // + // That's certainly solvable (just pass along more information), but would require a larger rewrite than + // I'm prepared to do now. + String message; + switch (entry.getChangeType()) { + case ADD: + message = getMsg("gb.diffNewFile", "New file"); + break; + case DELETE: + message = getMsg("gb.diffDeletedFile", "File was deleted"); + isOff = true; + break; + case RENAME: + message = MessageFormat.format(getMsg("gb.diffRenamedFile", "File was renamed from {0}"), entry.getOldPath()); + break; + case COPY: + message = MessageFormat.format(getMsg("gb.diffCopiedFile", "File was copied from {0}"), entry.getOldPath()); + break; + default: + return; + } + writeFullWidthLine(message); + } + + /** + * Output a hunk header + * + * @param aStartLine + * within first source + * @param aEndLine + * within first source + * @param bStartLine + * within second source + * @param bEndLine + * within second source + * @throws IOException + */ + @Override + protected void writeHunkHeader(int aStartLine, int aEndLine, int bStartLine, int bEndLine) throws IOException { + if (nofLinesCurrent++ == 0) { + handleChange(); + startCurrent = os.size(); + } + if (!isOff) { + totalNofLinesCurrent++; + if (nofLinesCurrent > maxDiffLinesPerFile && maxDiffLinesPerFile > 0) { + reset(); + } else { + os.write("<tr><th class='diff-line' data-lineno='..'></th><th class='diff-line' data-lineno='..'></th><th class='diff-state'></th><td class='hunk_header'>" + .getBytes()); + os.write('@'); + os.write('@'); + writeRange('-', aStartLine + 1, aEndLine - aStartLine); + writeRange('+', bStartLine + 1, bEndLine - bStartLine); + os.write(' '); + os.write('@'); + os.write('@'); + os.write("</td></tr>\n".getBytes()); + } + } + left = aStartLine + 1; + right = bStartLine + 1; + } + + protected void writeRange(final char prefix, final int begin, final int cnt) throws IOException { + os.write(' '); + os.write(prefix); + switch (cnt) { + case 0: + // If the range is empty, its beginning number must be the + // line just before the range, or 0 if the range is at the + // start of the file stream. Here, begin is always 1 based, + // so an empty file would produce "0,0". + // + os.write(encodeASCII(begin - 1)); + os.write(','); + os.write('0'); + break; + + case 1: + // If the range is exactly one line, produce only the number. + // + os.write(encodeASCII(begin)); + break; + + default: + os.write(encodeASCII(begin)); + os.write(','); + os.write(encodeASCII(cnt)); + break; + } + } + + /** + * Writes a line spanning the full width of the code view, including the gutter. + * + * @param text + * to put on that line; will be HTML-escaped. + */ + private void writeFullWidthLine(String text) { + try { + os.write("<tr><td class='diff-cell' colspan='4'>".getBytes()); + os.write(StringUtils.escapeForHtml(text, false).getBytes()); + os.write("</td></tr>\n".getBytes()); + } catch (IOException ex) { + // Cannot happen with a ByteArrayOutputStream + } + } + + @Override + protected void writeLine(final char prefix, final RawText text, final int cur) throws IOException { + if (nofLinesCurrent++ == 0) { + handleChange(); + startCurrent = os.size(); + } + // update entry diffstat + currentPath.update(prefix); + if (isOff) { + return; + } + totalNofLinesCurrent++; + if (nofLinesCurrent > maxDiffLinesPerFile && maxDiffLinesPerFile > 0) { + reset(); + } else { + // output diff + os.write("<tr>".getBytes()); + switch (prefix) { + case '+': + os.write(("<th class='diff-line'></th><th class='diff-line' data-lineno='" + (right++) + "'></th>").getBytes()); + os.write("<th class='diff-state diff-state-add'></th>".getBytes()); + os.write("<td class='diff-cell add2'>".getBytes()); + break; + case '-': + os.write(("<th class='diff-line' data-lineno='" + (left++) + "'></th><th class='diff-line'></th>").getBytes()); + os.write("<th class='diff-state diff-state-sub'></th>".getBytes()); + os.write("<td class='diff-cell remove2'>".getBytes()); + break; + default: + os.write(("<th class='diff-line' data-lineno='" + (left++) + "'></th><th class='diff-line' data-lineno='" + (right++) + "'></th>").getBytes()); + os.write("<th class='diff-state'></th>".getBytes()); + os.write("<td class='diff-cell context2'>".getBytes()); + break; + } + os.write(encode(codeLineToHtml(prefix, text.getString(cur)))); + os.write("</td></tr>\n".getBytes()); + } + } + + /** + * Convert the given code line to HTML. + * + * @param prefix + * the diff prefix (+/-) indicating whether the line was added or removed. + * @param line + * the line to format as HTML + * @return the HTML-formatted line, safe for inserting as is into HTML. + */ + private String codeLineToHtml(final char prefix, final String line) { + if ((prefix == '+' || prefix == '-')) { + // Highlight trailing whitespace on deleted/added lines. + Matcher matcher = trailingWhitespace.matcher(line); + if (matcher.find()) { + StringBuilder result = new StringBuilder(StringUtils.escapeForHtml(line.substring(0, matcher.start()), false)); + result.append("<span class='trailingws-").append(prefix == '+' ? "add" : "sub").append("'>"); + result.append(StringUtils.escapeForHtml(matcher.group(1), false)); + result.append("</span>"); + return result.toString(); + } + } + return StringUtils.escapeForHtml(line, false); + } + + /** + * Workaround function for complex private methods in DiffFormatter. This sets the html for the diff headers. + * + * @return + */ + public String getHtml() { + String html = RawParseUtils.decode(os.toByteArray()); + String[] lines = html.split("\n"); + StringBuilder sb = new StringBuilder(); + for (String line : lines) { + if (line.startsWith("index")) { + // skip index lines + } else if (line.startsWith("new file") || line.startsWith("deleted file")) { + // skip new file lines + } else if (line.startsWith("\\ No newline")) { + // skip no new line + } else if (line.startsWith("---") || line.startsWith("+++")) { + // skip --- +++ lines + } else if (line.startsWith("diff")) { + // skip diff lines + } else { + boolean gitLinkDiff = line.length() > 0 && line.substring(1).startsWith("Subproject commit"); + if (gitLinkDiff) { + sb.append("<tr><th class='diff-line'></th><th class='diff-line'></th>"); + if (line.charAt(0) == '+') { + sb.append("<th class='diff-state diff-state-add'></th><td class=\"diff-cell add2\">"); + } else { + sb.append("<th class='diff-state diff-state-sub'></th><td class=\"diff-cell remove2\">"); + } + line = StringUtils.escapeForHtml(line.substring(1), false); + } + sb.append(line); + if (gitLinkDiff) { + sb.append("</td></tr>"); + } + sb.append('\n'); + } + } + if (truncated) { + sb.append(MessageFormat.format("<div class='header'><div class='diffHeader'>{0}</div></div>", + StringUtils.escapeForHtml(getMsg("gb.diffTruncated", "Diff truncated after the above file"), false))); + // List all files not shown. We can be sure we do have at least one path in skipped. + sb.append("<div class='diff'><table cellpadding='0'><tbody><tr><td class='diff-cell' colspan='4'>"); + String deletedSuffix = StringUtils.escapeForHtml(getMsg("gb.diffDeletedFileSkipped", "(deleted)"), false); + boolean first = true; + for (DiffEntry entry : skipped) { + if (!first) { + sb.append('\n'); + } + if (ChangeType.DELETE.equals(entry.getChangeType())) { + sb.append("<span id=\"n" + entry.getOldId().name() + "\">" + StringUtils.escapeForHtml(entry.getOldPath(), false) + ' ' + deletedSuffix + "</span>"); + } else { + sb.append("<span id=\"n" + entry.getNewId().name() + "\">" + StringUtils.escapeForHtml(entry.getNewPath(), false) + "</span>"); + } + first = false; + } + skipped.clear(); + sb.append("</td></tr></tbody></table></div>"); + } + return sb.toString(); + } + + public DiffStat getDiffStat() { + return diffStat; + } +} diff --git a/src/main/java/com/gitblit/utils/HtmlBuilder.java b/src/main/java/com/gitblit/utils/HtmlBuilder.java new file mode 100644 index 00000000..6208ea82 --- /dev/null +++ b/src/main/java/com/gitblit/utils/HtmlBuilder.java @@ -0,0 +1,96 @@ +/* + * Copyright 2014 Tom <tw201207@gmail.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.utils; + +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.jsoup.parser.Tag; + +/** + * Simple helper class to hide some common setup needed to use JSoup as an HTML generator. + * A {@link HtmlBuilder} has a root element that can be manipulated in all the usual JSoup + * ways (but not added to some {@link Document}); to generate HTML for that root element, + * the builder's {@link #toString()} method can be used. (Or one can invoke toString() + * directly on the root element.) By default, a HTML builder does not pretty-print the HTML. + * + * @author Tom <tw201207@gmail.com> + */ +public class HtmlBuilder { + + private final Document shell; + private final Element root; + + /** + * Creates a new HTML builder with a root element with the given {@code tagName}. + * + * @param tagName + * of the {@link Element} to set as the root element. + */ + public HtmlBuilder(String tagName) { + this(new Element(Tag.valueOf(tagName), "")); + } + + /** + * Creates a new HTML builder for the given element. + * + * @param element + * to set as the root element of this HTML builder. + */ + public HtmlBuilder(Element element) { + shell = Document.createShell(""); + shell.outputSettings().prettyPrint(false); + shell.body().appendChild(element); + root = element; + } + + /** @return the root element of this HTML builder */ + public Element getRoot() { + return root; + } + + /** @return the root element of this HTML builder */ + public Element root() { + return root; + } + + /** @return whether this HTML builder will pretty-print the HTML generated by {@link #toString()} */ + public boolean prettyPrint() { + return shell.outputSettings().prettyPrint(); + } + + /** + * Sets whether this HTML builder will produce pretty-printed HTML in its {@link #toString()} method. + * + * @param pretty + * whether to pretty-print + * @return the HTML builder itself + */ + public HtmlBuilder prettyPrint(boolean pretty) { + shell.outputSettings().prettyPrint(pretty); + return this; + } + + /** @return the HTML for the root element. */ + @Override + public String toString() { + return root.toString(); + } + + /** @return the {@link Document} used as generation shell. */ + protected Document getShell() { + return shell; + } +} diff --git a/src/main/java/com/gitblit/utils/JGitUtils.java b/src/main/java/com/gitblit/utils/JGitUtils.java index da51ea98..69084ca1 100644 --- a/src/main/java/com/gitblit/utils/JGitUtils.java +++ b/src/main/java/com/gitblit/utils/JGitUtils.java @@ -30,6 +30,7 @@ import java.util.Map; import java.util.Map.Entry;
import java.util.regex.Pattern;
+import com.google.common.base.Strings;
import org.apache.commons.io.filefilter.TrueFileFilter;
import org.eclipse.jgit.api.CloneCommand;
import org.eclipse.jgit.api.FetchCommand;
@@ -891,6 +892,63 @@ public class JGitUtils { }
/**
+ * Returns the list of files in the specified folder at the specified
+ * commit. If the repository does not exist or is empty, an empty list is
+ * returned.
+ *
+ * This is modified version that implements path compression feature.
+ *
+ * @param repository
+ * @param path
+ * if unspecified, root folder is assumed.
+ * @param commit
+ * if null, HEAD is assumed.
+ * @return list of files in specified path
+ */
+ public static List<PathModel> getFilesInPath2(Repository repository, String path, RevCommit commit) {
+
+ List<PathModel> list = new ArrayList<PathModel>();
+ if (!hasCommits(repository)) {
+ return list;
+ }
+ if (commit == null) {
+ commit = getCommit(repository, null);
+ }
+ final TreeWalk tw = new TreeWalk(repository);
+ try {
+
+ tw.addTree(commit.getTree());
+ final boolean isPathEmpty = Strings.isNullOrEmpty(path);
+
+ if (!isPathEmpty) {
+ PathFilter f = PathFilter.create(path);
+ tw.setFilter(f);
+ }
+
+ tw.setRecursive(true);
+ List<String> paths = new ArrayList<>();
+
+ while (tw.next()) {
+ String child = isPathEmpty ? tw.getPathString()
+ : tw.getPathString().replaceFirst(String.format("%s/", path), "");
+ paths.add(child);
+ }
+
+ for(String p: PathUtils.compressPaths(paths)) {
+ String pathString = isPathEmpty ? p : String.format("%s/%s", path, p);
+ list.add(getPathModel(repository, pathString, path, commit));
+ }
+
+ } catch (IOException e) {
+ error(e, repository, "{0} failed to get files for commit {1}", commit.getName());
+ } finally {
+ tw.release();
+ }
+ Collections.sort(list);
+ return list;
+ }
+
+ /**
* Returns the list of files changed in a specified commit. If the
* repository does not exist or is empty, an empty list is returned.
*
@@ -1124,6 +1182,46 @@ public class JGitUtils { }
/**
+ * Returns a path model by path string
+ *
+ * @param repo
+ * @param path
+ * @param filter
+ * @param commit
+ * @return a path model of the specified object
+ */
+ private static PathModel getPathModel(Repository repo, String path, String filter, RevCommit commit)
+ throws IOException {
+
+ long size = 0;
+ TreeWalk tw = TreeWalk.forPath(repo, path, commit.getTree());
+ String pathString = path;
+
+ if (!tw.isSubtree() && (tw.getFileMode(0) != FileMode.GITLINK)) {
+ size = tw.getObjectReader().getObjectSize(tw.getObjectId(0), Constants.OBJ_BLOB);
+ pathString = PathUtils.getLastPathComponent(pathString);
+
+ } else if (tw.isSubtree()) {
+
+ // do not display dirs that are behind in the path
+ if (!Strings.isNullOrEmpty(filter)) {
+ pathString = path.replaceFirst(filter + "/", "");
+ }
+
+ // remove the last slash from path in displayed link
+ if (pathString != null && pathString.charAt(pathString.length()-1) == '/') {
+ pathString = pathString.substring(0, pathString.length()-1);
+ }
+ }
+
+ return new PathModel(pathString, tw.getPathString(), size, tw.getFileMode(0).getBits(),
+ tw.getObjectId(0).getName(), commit.getName());
+
+
+ }
+
+
+ /**
* Returns a permissions representation of the mode bits.
*
* @param mode
@@ -2194,212 +2292,222 @@ public class JGitUtils { }
return false;
}
- - /** - * Returns true if the commit identified by commitId is an ancestor or the - * the commit identified by tipId. - * - * @param repository - * @param commitId - * @param tipId - * @return true if there is the commit is an ancestor of the tip - */ - public static boolean isMergedInto(Repository repository, String commitId, String tipId) { - try { - return isMergedInto(repository, repository.resolve(commitId), repository.resolve(tipId)); - } catch (Exception e) { - LOGGER.error("Failed to determine isMergedInto", e); - } - return false; - } - - /** - * Returns true if the commit identified by commitId is an ancestor or the - * the commit identified by tipId. - * - * @param repository - * @param commitId - * @param tipId - * @return true if there is the commit is an ancestor of the tip - */ - public static boolean isMergedInto(Repository repository, ObjectId commitId, ObjectId tipCommitId) { - // traverse the revlog looking for a commit chain between the endpoints - RevWalk rw = new RevWalk(repository); - try { - // must re-lookup RevCommits to workaround undocumented RevWalk bug - RevCommit tip = rw.lookupCommit(tipCommitId); - RevCommit commit = rw.lookupCommit(commitId); - return rw.isMergedInto(commit, tip); - } catch (Exception e) { - LOGGER.error("Failed to determine isMergedInto", e); - } finally { - rw.dispose(); - } - return false; - } - - /** - * Returns the merge base of two commits or null if there is no common - * ancestry. - * - * @param repository - * @param commitIdA - * @param commitIdB - * @return the commit id of the merge base or null if there is no common base - */ - public static String getMergeBase(Repository repository, ObjectId commitIdA, ObjectId commitIdB) { - RevWalk rw = new RevWalk(repository); - try { - RevCommit a = rw.lookupCommit(commitIdA); - RevCommit b = rw.lookupCommit(commitIdB); - - rw.setRevFilter(RevFilter.MERGE_BASE); - rw.markStart(a); - rw.markStart(b); - RevCommit mergeBase = rw.next(); - if (mergeBase == null) { - return null; - } - return mergeBase.getName(); - } catch (Exception e) { - LOGGER.error("Failed to determine merge base", e); - } finally { - rw.dispose(); - } - return null; - } - - public static enum MergeStatus { - NOT_MERGEABLE, FAILED, ALREADY_MERGED, MERGEABLE, MERGED; - } - - /** - * Determines if we can cleanly merge one branch into another. Returns true - * if we can merge without conflict, otherwise returns false. - * - * @param repository - * @param src - * @param toBranch - * @return true if we can merge without conflict - */ - public static MergeStatus canMerge(Repository repository, String src, String toBranch) { - RevWalk revWalk = null; - try { - revWalk = new RevWalk(repository); - RevCommit branchTip = revWalk.lookupCommit(repository.resolve(toBranch)); - RevCommit srcTip = revWalk.lookupCommit(repository.resolve(src)); - if (revWalk.isMergedInto(srcTip, branchTip)) { - // already merged - return MergeStatus.ALREADY_MERGED; - } else if (revWalk.isMergedInto(branchTip, srcTip)) { - // fast-forward - return MergeStatus.MERGEABLE; - } - RecursiveMerger merger = (RecursiveMerger) MergeStrategy.RECURSIVE.newMerger(repository, true); - boolean canMerge = merger.merge(branchTip, srcTip); - if (canMerge) { - return MergeStatus.MERGEABLE; - } - } catch (IOException e) { - LOGGER.error("Failed to determine canMerge", e); +
+ /**
+ * Returns true if the commit identified by commitId is an ancestor or the
+ * the commit identified by tipId.
+ *
+ * @param repository
+ * @param commitId
+ * @param tipId
+ * @return true if there is the commit is an ancestor of the tip
+ */
+ public static boolean isMergedInto(Repository repository, String commitId, String tipId) {
+ try {
+ return isMergedInto(repository, repository.resolve(commitId), repository.resolve(tipId));
+ } catch (Exception e) {
+ LOGGER.error("Failed to determine isMergedInto", e);
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if the commit identified by commitId is an ancestor or the
+ * the commit identified by tipId.
+ *
+ * @param repository
+ * @param commitId
+ * @param tipId
+ * @return true if there is the commit is an ancestor of the tip
+ */
+ public static boolean isMergedInto(Repository repository, ObjectId commitId, ObjectId tipCommitId) {
+ // traverse the revlog looking for a commit chain between the endpoints
+ RevWalk rw = new RevWalk(repository);
+ try {
+ // must re-lookup RevCommits to workaround undocumented RevWalk bug
+ RevCommit tip = rw.lookupCommit(tipCommitId);
+ RevCommit commit = rw.lookupCommit(commitId);
+ return rw.isMergedInto(commit, tip);
+ } catch (Exception e) {
+ LOGGER.error("Failed to determine isMergedInto", e);
+ } finally {
+ rw.dispose();
+ }
+ return false;
+ }
+
+ /**
+ * Returns the merge base of two commits or null if there is no common
+ * ancestry.
+ *
+ * @param repository
+ * @param commitIdA
+ * @param commitIdB
+ * @return the commit id of the merge base or null if there is no common base
+ */
+ public static String getMergeBase(Repository repository, ObjectId commitIdA, ObjectId commitIdB) {
+ RevWalk rw = new RevWalk(repository);
+ try {
+ RevCommit a = rw.lookupCommit(commitIdA);
+ RevCommit b = rw.lookupCommit(commitIdB);
+
+ rw.setRevFilter(RevFilter.MERGE_BASE);
+ rw.markStart(a);
+ rw.markStart(b);
+ RevCommit mergeBase = rw.next();
+ if (mergeBase == null) {
+ return null;
+ }
+ return mergeBase.getName();
+ } catch (Exception e) {
+ LOGGER.error("Failed to determine merge base", e);
+ } finally {
+ rw.dispose();
+ }
+ return null;
+ }
+
+ public static enum MergeStatus {
+ MISSING_INTEGRATION_BRANCH, MISSING_SRC_BRANCH, NOT_MERGEABLE, FAILED, ALREADY_MERGED, MERGEABLE, MERGED;
+ }
+
+ /**
+ * Determines if we can cleanly merge one branch into another. Returns true
+ * if we can merge without conflict, otherwise returns false.
+ *
+ * @param repository
+ * @param src
+ * @param toBranch
+ * @return true if we can merge without conflict
+ */
+ public static MergeStatus canMerge(Repository repository, String src, String toBranch) {
+ RevWalk revWalk = null;
+ try {
+ revWalk = new RevWalk(repository);
+ ObjectId branchId = repository.resolve(toBranch);
+ if (branchId == null) {
+ return MergeStatus.MISSING_INTEGRATION_BRANCH;
+ }
+ ObjectId srcId = repository.resolve(src);
+ if (srcId == null) {
+ return MergeStatus.MISSING_SRC_BRANCH;
+ }
+ RevCommit branchTip = revWalk.lookupCommit(branchId);
+ RevCommit srcTip = revWalk.lookupCommit(srcId);
+ if (revWalk.isMergedInto(srcTip, branchTip)) {
+ // already merged
+ return MergeStatus.ALREADY_MERGED;
+ } else if (revWalk.isMergedInto(branchTip, srcTip)) {
+ // fast-forward
+ return MergeStatus.MERGEABLE;
+ }
+ RecursiveMerger merger = (RecursiveMerger) MergeStrategy.RECURSIVE.newMerger(repository, true);
+ boolean canMerge = merger.merge(branchTip, srcTip);
+ if (canMerge) {
+ return MergeStatus.MERGEABLE;
+ }
+ } catch (NullPointerException e) {
+ LOGGER.error("Failed to determine canMerge", e);
+ } catch (IOException e) {
+ LOGGER.error("Failed to determine canMerge", e);
} finally {
- if (revWalk != null) { + if (revWalk != null) {
revWalk.release();
- } - } - return MergeStatus.NOT_MERGEABLE; - } - - - public static class MergeResult { - public final MergeStatus status; - public final String sha; - - MergeResult(MergeStatus status, String sha) { - this.status = status; - this.sha = sha; - } - } - - /** - * Tries to merge a commit into a branch. If there are conflicts, the merge - * will fail. - * - * @param repository - * @param src - * @param toBranch - * @param committer - * @param message - * @return the merge result - */ - public static MergeResult merge(Repository repository, String src, String toBranch, - PersonIdent committer, String message) { - - if (!toBranch.startsWith(Constants.R_REFS)) { - // branch ref doesn't start with ref, assume this is a branch head - toBranch = Constants.R_HEADS + toBranch; - } - - RevWalk revWalk = null; - try { - revWalk = new RevWalk(repository); - RevCommit branchTip = revWalk.lookupCommit(repository.resolve(toBranch)); - RevCommit srcTip = revWalk.lookupCommit(repository.resolve(src)); - if (revWalk.isMergedInto(srcTip, branchTip)) { - // already merged - return new MergeResult(MergeStatus.ALREADY_MERGED, null); - } - RecursiveMerger merger = (RecursiveMerger) MergeStrategy.RECURSIVE.newMerger(repository, true); - boolean merged = merger.merge(branchTip, srcTip); - if (merged) { - // create a merge commit and a reference to track the merge commit - ObjectId treeId = merger.getResultTreeId(); - ObjectInserter odi = repository.newObjectInserter(); - try { - // Create a commit object - CommitBuilder commitBuilder = new CommitBuilder(); - commitBuilder.setCommitter(committer); - commitBuilder.setAuthor(committer); - commitBuilder.setEncoding(Constants.CHARSET); - if (StringUtils.isEmpty(message)) { - message = MessageFormat.format("merge {0} into {1}", srcTip.getName(), branchTip.getName()); - } - commitBuilder.setMessage(message); - commitBuilder.setParentIds(branchTip.getId(), srcTip.getId()); - commitBuilder.setTreeId(treeId); - - // Insert the merge commit into the repository - ObjectId mergeCommitId = odi.insert(commitBuilder); - odi.flush(); - - // set the merge ref to the merge commit - RevCommit mergeCommit = revWalk.parseCommit(mergeCommitId); - RefUpdate mergeRefUpdate = repository.updateRef(toBranch); - mergeRefUpdate.setNewObjectId(mergeCommitId); - mergeRefUpdate.setRefLogMessage("commit: " + mergeCommit.getShortMessage(), false); - RefUpdate.Result rc = mergeRefUpdate.update(); - switch (rc) { - case FAST_FORWARD: - // successful, clean merge + }
+ }
+ return MergeStatus.NOT_MERGEABLE;
+ }
+
+
+ public static class MergeResult {
+ public final MergeStatus status;
+ public final String sha;
+
+ MergeResult(MergeStatus status, String sha) {
+ this.status = status;
+ this.sha = sha;
+ }
+ }
+
+ /**
+ * Tries to merge a commit into a branch. If there are conflicts, the merge
+ * will fail.
+ *
+ * @param repository
+ * @param src
+ * @param toBranch
+ * @param committer
+ * @param message
+ * @return the merge result
+ */
+ public static MergeResult merge(Repository repository, String src, String toBranch,
+ PersonIdent committer, String message) {
+
+ if (!toBranch.startsWith(Constants.R_REFS)) {
+ // branch ref doesn't start with ref, assume this is a branch head
+ toBranch = Constants.R_HEADS + toBranch;
+ }
+
+ RevWalk revWalk = null;
+ try {
+ revWalk = new RevWalk(repository);
+ RevCommit branchTip = revWalk.lookupCommit(repository.resolve(toBranch));
+ RevCommit srcTip = revWalk.lookupCommit(repository.resolve(src));
+ if (revWalk.isMergedInto(srcTip, branchTip)) {
+ // already merged
+ return new MergeResult(MergeStatus.ALREADY_MERGED, null);
+ }
+ RecursiveMerger merger = (RecursiveMerger) MergeStrategy.RECURSIVE.newMerger(repository, true);
+ boolean merged = merger.merge(branchTip, srcTip);
+ if (merged) {
+ // create a merge commit and a reference to track the merge commit
+ ObjectId treeId = merger.getResultTreeId();
+ ObjectInserter odi = repository.newObjectInserter();
+ try {
+ // Create a commit object
+ CommitBuilder commitBuilder = new CommitBuilder();
+ commitBuilder.setCommitter(committer);
+ commitBuilder.setAuthor(committer);
+ commitBuilder.setEncoding(Constants.CHARSET);
+ if (StringUtils.isEmpty(message)) {
+ message = MessageFormat.format("merge {0} into {1}", srcTip.getName(), branchTip.getName());
+ }
+ commitBuilder.setMessage(message);
+ commitBuilder.setParentIds(branchTip.getId(), srcTip.getId());
+ commitBuilder.setTreeId(treeId);
+
+ // Insert the merge commit into the repository
+ ObjectId mergeCommitId = odi.insert(commitBuilder);
+ odi.flush();
+
+ // set the merge ref to the merge commit
+ RevCommit mergeCommit = revWalk.parseCommit(mergeCommitId);
+ RefUpdate mergeRefUpdate = repository.updateRef(toBranch);
+ mergeRefUpdate.setNewObjectId(mergeCommitId);
+ mergeRefUpdate.setRefLogMessage("commit: " + mergeCommit.getShortMessage(), false);
+ RefUpdate.Result rc = mergeRefUpdate.update();
+ switch (rc) {
+ case FAST_FORWARD:
+ // successful, clean merge
break;
- default: - throw new GitBlitException(MessageFormat.format("Unexpected result \"{0}\" when merging commit {1} into {2} in {3}", - rc.name(), srcTip.getName(), branchTip.getName(), repository.getDirectory())); - } - - // return the merge commit id - return new MergeResult(MergeStatus.MERGED, mergeCommitId.getName()); - } finally { - odi.release(); - } - } - } catch (IOException e) { - LOGGER.error("Failed to merge", e); + default:
+ throw new GitBlitException(MessageFormat.format("Unexpected result \"{0}\" when merging commit {1} into {2} in {3}",
+ rc.name(), srcTip.getName(), branchTip.getName(), repository.getDirectory()));
+ }
+
+ // return the merge commit id
+ return new MergeResult(MergeStatus.MERGED, mergeCommitId.getName());
+ } finally {
+ odi.release();
+ }
+ }
+ } catch (IOException e) {
+ LOGGER.error("Failed to merge", e);
} finally {
- if (revWalk != null) { + if (revWalk != null) {
revWalk.release();
- } - } - return new MergeResult(MergeStatus.FAILED, null); - } + }
+ }
+ return new MergeResult(MergeStatus.FAILED, null);
+ }
}
diff --git a/src/main/java/com/gitblit/utils/JSoupXssFilter.java b/src/main/java/com/gitblit/utils/JSoupXssFilter.java index 5ab7953a..aec22411 100644 --- a/src/main/java/com/gitblit/utils/JSoupXssFilter.java +++ b/src/main/java/com/gitblit/utils/JSoupXssFilter.java @@ -20,18 +20,23 @@ import org.jsoup.nodes.Document; import org.jsoup.safety.Cleaner; import org.jsoup.safety.Whitelist; +import com.google.inject.Inject; +import com.google.inject.Singleton; + /** * Implementation of an XSS filter based on JSoup. * * @author James Moger * */ +@Singleton public class JSoupXssFilter implements XssFilter { private final Cleaner none; private final Cleaner relaxed; + @Inject public JSoupXssFilter() { none = new Cleaner(Whitelist.none()); relaxed = new Cleaner(getRelaxedWhiteList()); diff --git a/src/main/java/com/gitblit/utils/PathUtils.java b/src/main/java/com/gitblit/utils/PathUtils.java new file mode 100644 index 00000000..a3c7d8d6 --- /dev/null +++ b/src/main/java/com/gitblit/utils/PathUtils.java @@ -0,0 +1,92 @@ +package com.gitblit.utils; + +import com.google.common.base.Joiner; +import com.google.common.base.Splitter; +import com.google.common.collect.Iterables; + +import java.util.*; + +/** + * Utils for handling path strings + * + */ +public class PathUtils { + + private PathUtils() {} + + /** + * Compress paths containing no files + * + * @param paths lines from `git ls-tree -r --name-only ${branch}` + * @return compressed paths + */ + public static List<String> compressPaths(final Iterable<String> paths) { + + ArrayList<String> pathList = new ArrayList<>(); + Map<String, List<String[]>> folderRoots = new LinkedHashMap<>(); + + for (String s: paths) { + String[] components = s.split("/"); + + // File in current directory + if (components.length == 1) { + pathList.add(components[0]); + + // Directory path + } else { + List<String[]> rootedPaths = folderRoots.get(components[0]); + if (rootedPaths == null) { + rootedPaths = new ArrayList<>(); + } + rootedPaths.add(components); + folderRoots.put(components[0], rootedPaths); + } + } + + for (String folder: folderRoots.keySet()) { + List<String[]> matchingPaths = folderRoots.get(folder); + + if (matchingPaths.size() == 1) { + pathList.add(toStringPath(matchingPaths.get(0))); + } else { + pathList.add(longestCommonSequence(matchingPaths)); + } + } + return pathList; + } + + /** + * Get last path component + * + * + * @param path string path separated by slashes + * @return rightmost entry + */ + public static String getLastPathComponent(final String path) { + return Iterables.getLast(Splitter.on("/").omitEmptyStrings().split(path), path); + } + + private static String toStringPath(final String[] pathComponents) { + List<String> tmp = Arrays.asList(pathComponents); + return Joiner.on('/').join(tmp.subList(0,tmp.size()-1)) + '/'; + } + + + private static String longestCommonSequence(final List<String[]> paths) { + + StringBuilder path = new StringBuilder(); + + for (int i = 0; i < paths.get(0).length; i++) { + String current = paths.get(0)[i]; + for (int j = 1; j < paths.size(); j++) { + if (!current.equals(paths.get(j)[i])) { + return path.toString(); + } + } + path.append(current); + path.append('/'); + } + return path.toString(); + } + +} diff --git a/src/main/java/com/gitblit/utils/ResettableByteArrayOutputStream.java b/src/main/java/com/gitblit/utils/ResettableByteArrayOutputStream.java new file mode 100644 index 00000000..7df0693f --- /dev/null +++ b/src/main/java/com/gitblit/utils/ResettableByteArrayOutputStream.java @@ -0,0 +1,42 @@ +// Copyright (C) 2014 Tom <tw201207@gmail.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.utils; + +import java.io.ByteArrayOutputStream; + +/** + * A {@link ByteArrayOutputStream} that can be reset to a specified position. + * + * @author Tom <tw201207@gmail.com> + */ +public class ResettableByteArrayOutputStream extends ByteArrayOutputStream { + + /** + * Reset the stream to the given position. If {@code mark} is <= 0, see {@link #reset()}. + * A no-op if the stream contains less than {@code mark} bytes. Otherwise, resets the + * current writing position to {@code mark}. Previously allocated buffer space will be + * reused in subsequent writes. + * + * @param mark + * to set the current writing position to. + */ + public synchronized void resetTo(int mark) { + if (mark <= 0) { + reset(); + } else if (mark < count) { + count = mark; + } + } + +} diff --git a/src/main/java/com/gitblit/wicket/GitBlitWebApp.java b/src/main/java/com/gitblit/wicket/GitBlitWebApp.java index d19630c2..359040b5 100644 --- a/src/main/java/com/gitblit/wicket/GitBlitWebApp.java +++ b/src/main/java/com/gitblit/wicket/GitBlitWebApp.java @@ -43,6 +43,7 @@ import com.gitblit.manager.IPluginManager; import com.gitblit.manager.IProjectManager; import com.gitblit.manager.IRepositoryManager; import com.gitblit.manager.IRuntimeManager; +import com.gitblit.manager.IServicesManager; import com.gitblit.manager.IUserManager; import com.gitblit.tickets.ITicketService; import com.gitblit.transport.ssh.IPublicKeyManager; @@ -90,7 +91,11 @@ import com.gitblit.wicket.pages.TicketsPage; import com.gitblit.wicket.pages.TreePage; import com.gitblit.wicket.pages.UserPage; import com.gitblit.wicket.pages.UsersPage; +import com.google.inject.Inject; +import com.google.inject.Provider; +import com.google.inject.Singleton; +@Singleton public class GitBlitWebApp extends WebApplication implements GitblitWicketApp { private final Class<? extends WebPage> homePageClass = MyDashboardPage.class; @@ -99,6 +104,10 @@ public class GitBlitWebApp extends WebApplication implements GitblitWicketApp { private final Map<String, CacheControl> cacheablePages = new HashMap<String, CacheControl>(); + private final Provider<IPublicKeyManager> publicKeyManagerProvider; + + private final Provider<ITicketService> ticketServiceProvider; + private final IStoredSettings settings; private final XssFilter xssFilter; @@ -113,8 +122,6 @@ public class GitBlitWebApp extends WebApplication implements GitblitWicketApp { private final IAuthenticationManager authenticationManager; - private final IPublicKeyManager publicKeyManager; - private final IRepositoryManager repositoryManager; private final IProjectManager projectManager; @@ -123,19 +130,26 @@ public class GitBlitWebApp extends WebApplication implements GitblitWicketApp { private final IGitblit gitblit; + private final IServicesManager services; + + @Inject public GitBlitWebApp( + Provider<IPublicKeyManager> publicKeyManagerProvider, + Provider<ITicketService> ticketServiceProvider, IRuntimeManager runtimeManager, IPluginManager pluginManager, INotificationManager notificationManager, IUserManager userManager, IAuthenticationManager authenticationManager, - IPublicKeyManager publicKeyManager, IRepositoryManager repositoryManager, IProjectManager projectManager, IFederationManager federationManager, - IGitblit gitblit) { + IGitblit gitblit, + IServicesManager services) { super(); + this.publicKeyManagerProvider = publicKeyManagerProvider; + this.ticketServiceProvider = ticketServiceProvider; this.settings = runtimeManager.getSettings(); this.xssFilter = runtimeManager.getXssFilter(); this.runtimeManager = runtimeManager; @@ -143,11 +157,11 @@ public class GitBlitWebApp extends WebApplication implements GitblitWicketApp { this.notificationManager = notificationManager; this.userManager = userManager; this.authenticationManager = authenticationManager; - this.publicKeyManager = publicKeyManager; this.repositoryManager = repositoryManager; this.projectManager = projectManager; this.federationManager = federationManager; this.gitblit = gitblit; + this.services = services; } @Override @@ -392,7 +406,7 @@ public class GitBlitWebApp extends WebApplication implements GitblitWicketApp { */ @Override public IPublicKeyManager keys() { - return publicKeyManager; + return publicKeyManagerProvider.get(); } /* (non-Javadoc) @@ -428,11 +442,19 @@ public class GitBlitWebApp extends WebApplication implements GitblitWicketApp { } /* (non-Javadoc) + * @see com.gitblit.wicket.Webapp#services() + */ + @Override + public IServicesManager services() { + return services; + } + + /* (non-Javadoc) * @see com.gitblit.wicket.Webapp#tickets() */ @Override public ITicketService tickets() { - return gitblit.getTicketService(); + return ticketServiceProvider.get(); } /* (non-Javadoc) diff --git a/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties b/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties index eb92e2d2..648ac2a5 100644 --- a/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties +++ b/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties @@ -561,6 +561,7 @@ gb.bugTickets = bugs gb.enhancementTickets = enhancements gb.taskTickets = tasks gb.questionTickets = questions +gb.maintenanceTickets = maintenance gb.requestTickets = enhancements & tasks gb.yourCreatedTickets = created by you gb.yourWatchedTickets = watched by you @@ -741,4 +742,21 @@ gb.sshKeyCommentDescription = Enter an optional comment. If blank, the comment w gb.permission = Permission gb.sshKeyPermissionDescription = Specify the access permission for the SSH key gb.transportPreference = Transport Preference -gb.transportPreferenceDescription = Set the transport that you prefer to use for cloning
\ No newline at end of file +gb.transportPreferenceDescription = Set the transport that you prefer to use for cloning +gb.priority = priority +gb.severity = severity +gb.sortHighestPriority = highest priority +gb.sortLowestPriority = lowest priority +gb.sortHighestSeverity = highest severity +gb.sortLowestSeverity = lowest severity +gb.missingIntegrationBranchMore = The target integration branch does not exist in the repository! +gb.diffDeletedFileSkipped = (deleted) +gb.diffFileDiffTooLarge = Diff too large +gb.diffNewFile = New file +gb.diffDeletedFile = File was deleted +gb.diffRenamedFile = File was renamed from {0} +gb.diffCopiedFile = File was copied from {0} +gb.diffTruncated = Diff truncated after the above file +gb.opacityAdjust = Adjust opacity +gb.blinkComparator = Blink comparator +gb.imgdiffSubtract = Subtract (black = identical)
\ No newline at end of file diff --git a/src/main/java/com/gitblit/wicket/GitBlitWebApp_de.properties b/src/main/java/com/gitblit/wicket/GitBlitWebApp_de.properties index 3ec330b7..eca3fd2a 100644 --- a/src/main/java/com/gitblit/wicket/GitBlitWebApp_de.properties +++ b/src/main/java/com/gitblit/wicket/GitBlitWebApp_de.properties @@ -743,3 +743,13 @@ gb.permission = Berechtigung gb.sshKeyPermissionDescription = Geben Sie die Zugriffberechtigung f\u00fcr den SSH Key an gb.transportPreference = \u00dcbertragungseinstellungen gb.transportPreferenceDescription = Geben Sie die \u00dcbertragungsart an, die Sie f\u00fcr das Klonen bevorzugen +gb.diffDeletedFileSkipped = (gel\u00f6scht) +gb.diffFileDiffTooLarge = Zu viele \u00c4nderungen; Diff wird nicht angezeigt +gb.diffNewFile = Neue Datei +gb.diffDeletedFile = Datei wurde gel\u00f6scht +gb.diffRenamedFile = Datei umbenannt von {0} +gb.diffCopiedFile = Datei kopiert von {0} +gb.diffTruncated = Diff nach obiger Datei abgeschnitten +gb.opacityAdjust = Transparenz +gb.blinkComparator = Blinkkomparator +gb.imgdiffSubtract = Pixeldifferenz (schwarz = identisch)
\ No newline at end of file diff --git a/src/main/java/com/gitblit/wicket/GitBlitWebApp_fr.properties b/src/main/java/com/gitblit/wicket/GitBlitWebApp_fr.properties index b888beee..d479b3d6 100644 --- a/src/main/java/com/gitblit/wicket/GitBlitWebApp_fr.properties +++ b/src/main/java/com/gitblit/wicket/GitBlitWebApp_fr.properties @@ -672,3 +672,13 @@ gb.ticketIsClosed = Ce ticket est clos. gb.mergeToDescription = branche d'int\u00e9gration par d\u00e9faut pour fusionner les correctifs li\u00e9s aux tickets gb.myTickets = mes tickets gb.yourAssignedTickets = dont vous \u00eates responsable +gb.diffDeletedFileSkipped = (effac\u00e9) +gb.diffFileDiffTooLarge = Trop de diff\u00e9rences, affichage supprim\u00e9e +gb.diffNewFile = Nouveau fichier +gb.diffDeletedFile = Fichier a \u00e9t\u00e9 effac\u00e9 +gb.diffRenamedFile = Fichier renomm\u00e9 de {0} +gb.diffCopiedFile = Fichier copi\u00e9 de {0} +gb.diffTruncated = Affichage de diff\u00e9rences supprim\u00e9e apr\u00e8s le fichier ci-dessus +gb.opacityAdjust = ajuster l'opacit\u00e9 +gb.blinkComparator = Comparateur \u00e0 clignotement +gb.imgdiffSubtract = Diff\u00e9rence (noir = identique)
\ No newline at end of file diff --git a/src/main/java/com/gitblit/wicket/GitblitWicketApp.java b/src/main/java/com/gitblit/wicket/GitblitWicketApp.java index 8d3d598d..3041c5da 100644 --- a/src/main/java/com/gitblit/wicket/GitblitWicketApp.java +++ b/src/main/java/com/gitblit/wicket/GitblitWicketApp.java @@ -14,6 +14,7 @@ import com.gitblit.manager.IPluginManager; import com.gitblit.manager.IProjectManager; import com.gitblit.manager.IRepositoryManager; import com.gitblit.manager.IRuntimeManager; +import com.gitblit.manager.IServicesManager; import com.gitblit.manager.IUserManager; import com.gitblit.tickets.ITicketService; import com.gitblit.transport.ssh.IPublicKeyManager; @@ -68,6 +69,8 @@ public interface GitblitWicketApp { public abstract IGitblit gitblit(); + public abstract IServicesManager services(); + public abstract ITicketService tickets(); public abstract TimeZone getTimezone(); diff --git a/src/main/java/com/gitblit/wicket/GitblitWicketFilter.java b/src/main/java/com/gitblit/wicket/GitblitWicketFilter.java index 7865fb3b..68ad84a5 100644 --- a/src/main/java/com/gitblit/wicket/GitblitWicketFilter.java +++ b/src/main/java/com/gitblit/wicket/GitblitWicketFilter.java @@ -17,6 +17,8 @@ package com.gitblit.wicket; import java.util.Date;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
import javax.servlet.http.HttpServletRequest;
import org.apache.wicket.protocol.http.IWebApplicationFactory;
@@ -28,7 +30,6 @@ import org.eclipse.jgit.revwalk.RevCommit; import com.gitblit.IStoredSettings;
import com.gitblit.Keys;
-import com.gitblit.dagger.DaggerWicketFilter;
import com.gitblit.manager.IProjectManager;
import com.gitblit.manager.IRepositoryManager;
import com.gitblit.manager.IRuntimeManager;
@@ -37,8 +38,6 @@ import com.gitblit.models.RepositoryModel; import com.gitblit.utils.JGitUtils;
import com.gitblit.utils.StringUtils;
-import dagger.ObjectGraph;
-
/**
*
* Customization of the WicketFilter to allow smart browser-side caching of
@@ -47,7 +46,8 @@ import dagger.ObjectGraph; * @author James Moger
*
*/
-public class GitblitWicketFilter extends DaggerWicketFilter {
+@Singleton
+public class GitblitWicketFilter extends WicketFilter {
private IStoredSettings settings;
@@ -59,13 +59,19 @@ public class GitblitWicketFilter extends DaggerWicketFilter { private GitBlitWebApp webapp;
- @Override
- protected void inject(ObjectGraph dagger) {
- this.settings = dagger.get(IStoredSettings.class);
- this.runtimeManager = dagger.get(IRuntimeManager.class);
- this.repositoryManager = dagger.get(IRepositoryManager.class);
- this.projectManager = dagger.get(IProjectManager.class);
- this.webapp = dagger.get(GitBlitWebApp.class);
+ @Inject
+ public GitblitWicketFilter(
+ IStoredSettings settings,
+ IRuntimeManager runtimeManager,
+ IRepositoryManager repositoryManager,
+ IProjectManager projectManager,
+ GitBlitWebApp webapp) {
+
+ this.settings = settings;
+ this.runtimeManager = runtimeManager;
+ this.repositoryManager = repositoryManager;
+ this.projectManager = projectManager;
+ this.webapp = webapp;
}
@Override
diff --git a/src/main/java/com/gitblit/wicket/TicketsUI.java b/src/main/java/com/gitblit/wicket/TicketsUI.java index 0eff4cff..8d599523 100644 --- a/src/main/java/com/gitblit/wicket/TicketsUI.java +++ b/src/main/java/com/gitblit/wicket/TicketsUI.java @@ -21,6 +21,8 @@ import java.text.MessageFormat; import org.apache.wicket.markup.html.basic.Label; import com.gitblit.models.TicketModel; +import com.gitblit.models.TicketModel.Priority; +import com.gitblit.models.TicketModel.Severity; import com.gitblit.models.TicketModel.Status; import com.gitblit.models.TicketModel.Type; import com.gitblit.utils.StringUtils; @@ -38,37 +40,74 @@ public class TicketsUI { public static final String [] closedStatii = new String [] { "!" + Status.New.name().toLowerCase(), "!" + Status.Open.name().toLowerCase() }; public static Label getStateIcon(String wicketId, TicketModel ticket) { - return getStateIcon(wicketId, ticket.type, ticket.status); + return getStateIcon(wicketId, ticket.type, ticket.status, ticket.severity); } - public static Label getStateIcon(String wicketId, Type type, Status state) { + public static Label getStateIcon(String wicketId, Type type, Status state, Severity severity) { Label label = new Label(wicketId); if (type == null) { type = Type.defaultType; } switch (type) { case Proposal: - WicketUtils.setCssClass(label, "fa fa-code-fork"); + WicketUtils.setCssClass(label, "fa fa-code-fork fa-fw"); break; case Bug: - WicketUtils.setCssClass(label, "fa fa-bug"); + WicketUtils.setCssClass(label, "fa fa-bug fa-fw"); break; case Enhancement: - WicketUtils.setCssClass(label, "fa fa-magic"); + WicketUtils.setCssClass(label, "fa fa-magic fa-fw"); break; case Question: - WicketUtils.setCssClass(label, "fa fa-question"); + WicketUtils.setCssClass(label, "fa fa-question fa-fw"); + break; + case Maintenance: + WicketUtils.setCssClass(label, "fa fa-cogs fa-fw"); break; default: // standard ticket - WicketUtils.setCssClass(label, "fa fa-ticket"); + WicketUtils.setCssClass(label, "fa fa-ticket fa-fw"); + } + WicketUtils.setHtmlTooltip(label, getTypeState(type, state, severity)); + + return label; + } + + public static Label getPriorityIcon(String wicketId, Priority priority) { + Label label = new Label(wicketId); + if (priority == null) { + priority = Priority.defaultPriority; + } + switch (priority) { + case Urgent: + WicketUtils.setCssClass(label, "fa fa-step-forward fa-rotate-270"); + break; + case High: + WicketUtils.setCssClass(label, "fa fa-caret-up fa-lg"); + break; + case Low: + WicketUtils.setCssClass(label, "fa fa-caret-down fa-lg"); + break; + default: } - WicketUtils.setHtmlTooltip(label, getTypeState(type, state)); + WicketUtils.setHtmlTooltip(label, priority.toString()); + return label; } - public static String getTypeState(Type type, Status state) { - return state.toString() + " " + type.toString(); + public static String getPriorityClass(Priority priority) { + return String.format("priority-%s", priority); + } + + public static String getSeverityClass(Severity severity) { + return String.format("severity-%s", severity); + } + + public static String getTypeState(Type type, Status state, Severity severity) { + if (Severity.Unrated == severity) { + return state.toString() + " " + type.toString(); + } + return state.toString() + " " + type.toString() + ", " + severity.toString(); } public static String getLozengeClass(Status status, boolean subtle) { diff --git a/src/main/java/com/gitblit/wicket/WicketUtils.java b/src/main/java/com/gitblit/wicket/WicketUtils.java index d47390d4..9a40931d 100644 --- a/src/main/java/com/gitblit/wicket/WicketUtils.java +++ b/src/main/java/com/gitblit/wicket/WicketUtils.java @@ -29,12 +29,14 @@ import javax.servlet.http.HttpServletRequest; import org.apache.wicket.Component;
import org.apache.wicket.PageParameters;
import org.apache.wicket.Request;
+import org.apache.wicket.behavior.AttributeAppender;
import org.apache.wicket.behavior.HeaderContributor;
import org.apache.wicket.behavior.SimpleAttributeModifier;
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.image.ContextImage;
+import org.apache.wicket.model.Model;
import org.apache.wicket.protocol.http.WebRequest;
import org.apache.wicket.resource.ContextRelativeResource;
import org.eclipse.jgit.diff.DiffEntry.ChangeType;
@@ -56,6 +58,10 @@ public class WicketUtils { container.add(new SimpleAttributeModifier("class", value));
}
+ public static void addCssClass(Component container, String value) {
+ container.add(new AttributeAppender("class", new Model<String>(value), " "));
+ }
+
public static void setCssStyle(Component container, String value) {
container.add(new SimpleAttributeModifier("style", value));
}
diff --git a/src/main/java/com/gitblit/wicket/freemarker/templates/FilterableRepositoryList.fm b/src/main/java/com/gitblit/wicket/freemarker/templates/FilterableRepositoryList.fm index 1512adab..43816cf7 100644 --- a/src/main/java/com/gitblit/wicket/freemarker/templates/FilterableRepositoryList.fm +++ b/src/main/java/com/gitblit/wicket/freemarker/templates/FilterableRepositoryList.fm @@ -9,7 +9,13 @@ </div>
<div ng-repeat="item in ${ngList} | filter:query" style="padding: 3px;border-top: 1px solid #ddd;">
- <b><span class="repositorySwatch" style="background-color:{{item.c}};"><span ng-show="item.wc">!</span><span ng-show="!item.wc"> </span></span></b>
+ <span style="color:{{item.c}};padding-right:2px;">
+ <span ng-show="item.y == 0" class="octicon octicon-centered octicon-repo"></span>
+ <span ng-show="item.y == 1" class="octicon octicon-centered octicon-repo-forked"></span>
+ <span ng-show="item.y == 2" class="octicon octicon-centered octicon-mirror"></span>
+ <span ng-show="item.y == 3" class="octicon octicon-centered octicon-repo-push"></span>
+ </span>
+
<a href="summary/?r={{item.r}}" title="{{item.i}}">{{item.p}}<b>{{item.n}}</b></a>
<span class="link hidden-tablet hidden-phone" style="color: #bbb;" title="{{item.d}}">{{item.t}}</span>
<span ng-show="item.s" class="pull-right">
diff --git a/src/main/java/com/gitblit/wicket/pages/BasePage.html b/src/main/java/com/gitblit/wicket/pages/BasePage.html index 89a28b80..b998428c 100644 --- a/src/main/java/com/gitblit/wicket/pages/BasePage.html +++ b/src/main/java/com/gitblit/wicket/pages/BasePage.html @@ -15,6 +15,7 @@ <link rel="stylesheet" href="bootstrap/css/bootstrap.css"/>
<link rel="stylesheet" href="bootstrap/css/iconic.css"/>
<link rel="stylesheet" href="fontawesome/css/font-awesome.min.css"/>
+ <link rel="stylesheet" href="octicons/octicons.css"/>
<link rel="stylesheet" type="text/css" href="gitblit.css"/>
</wicket:head>
@@ -50,5 +51,6 @@ <!-- Include scripts at end for faster page loading -->
<script type="text/javascript" src="bootstrap/js/jquery.js"></script>
<script type="text/javascript" src="bootstrap/js/bootstrap.js"></script>
+ <wicket:container wicket:id="bottomScripts"></wicket:container>
</body>
</html>
\ No newline at end of file diff --git a/src/main/java/com/gitblit/wicket/pages/BasePage.java b/src/main/java/com/gitblit/wicket/pages/BasePage.java index b6967003..0d99f5e5 100644 --- a/src/main/java/com/gitblit/wicket/pages/BasePage.java +++ b/src/main/java/com/gitblit/wicket/pages/BasePage.java @@ -42,6 +42,8 @@ import org.apache.wicket.markup.html.CSSPackageResource; import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.link.ExternalLink;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
+import org.apache.wicket.markup.html.resources.JavascriptResourceReference;
+import org.apache.wicket.markup.repeater.RepeatingView;
import org.apache.wicket.protocol.http.RequestUtils;
import org.apache.wicket.protocol.http.WebResponse;
import org.apache.wicket.protocol.http.servlet.ServletWebRequest;
@@ -242,7 +244,7 @@ public abstract class BasePage extends SessionPage { protected void setupPage(String repositoryName, String pageName) {
add(new Label("title", getPageTitle(repositoryName)));
-
+ getBottomScriptContainer();
String rootLinkUrl = app().settings().getString(Keys.web.rootLink, urlFor(GitBlitWebApp.get().getHomePage(), null).toString());
ExternalLink rootLink = new ExternalLink("rootLink", rootLinkUrl);
WicketUtils.setHtmlTooltip(rootLink, app().settings().getString(Keys.web.siteName, Constants.NAME));
@@ -506,4 +508,40 @@ public abstract class BasePage extends SessionPage { return sb.toString();
}
+ private RepeatingView getBottomScriptContainer() {
+ RepeatingView bottomScriptContainer = (RepeatingView) get("bottomScripts");
+ if (bottomScriptContainer == null) {
+ bottomScriptContainer = new RepeatingView("bottomScripts");
+ bottomScriptContainer.setRenderBodyOnly(true);
+ add(bottomScriptContainer);
+ }
+ return bottomScriptContainer;
+ }
+
+ /**
+ * Adds a HTML script element loading the javascript designated by the given path.
+ *
+ * @param scriptPath
+ * page-relative path to the Javascript resource; normally starts with "scripts/"
+ */
+ protected void addBottomScript(String scriptPath) {
+ RepeatingView bottomScripts = getBottomScriptContainer();
+ Label script = new Label(bottomScripts.newChildId(), "<script type='text/javascript' src='"
+ + urlFor(new JavascriptResourceReference(this.getClass(), scriptPath)) + "'></script>\n");
+ bottomScripts.add(script.setEscapeModelStrings(false).setRenderBodyOnly(true));
+ }
+
+ /**
+ * Adds a HTML script element containing the given code.
+ *
+ * @param code
+ * inline script code
+ */
+ protected void addBottomScriptInline(String code) {
+ RepeatingView bottomScripts = getBottomScriptContainer();
+ Label script = new Label(bottomScripts.newChildId(),
+ "<script type='text/javascript'>/*<![CDATA[*/\n" + code + "\n//]]>\n</script>\n");
+ bottomScripts.add(script.setEscapeModelStrings(false).setRenderBodyOnly(true));
+ }
+
}
diff --git a/src/main/java/com/gitblit/wicket/pages/BlobDiffPage.java b/src/main/java/com/gitblit/wicket/pages/BlobDiffPage.java index 9cc3eae1..ae737a53 100644 --- a/src/main/java/com/gitblit/wicket/pages/BlobDiffPage.java +++ b/src/main/java/com/gitblit/wicket/pages/BlobDiffPage.java @@ -15,12 +15,15 @@ */
package com.gitblit.wicket.pages;
+import java.util.List;
+
import org.apache.wicket.PageParameters;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.link.BookmarkablePageLink;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
+import com.gitblit.Keys;
import com.gitblit.utils.DiffUtils;
import com.gitblit.utils.DiffUtils.DiffOutputType;
import com.gitblit.utils.JGitUtils;
@@ -43,16 +46,29 @@ public class BlobDiffPage extends RepositoryPage { Repository r = getRepository();
RevCommit commit = getCommit();
+ final List<String> imageExtensions = app().settings().getStrings(Keys.web.imageExtensions);
+
String diff;
if (StringUtils.isEmpty(baseObjectId)) {
// use first parent
- diff = DiffUtils.getDiff(r, commit, blobPath, DiffOutputType.HTML).content;
+ RevCommit parent = commit.getParentCount() == 0 ? null : commit.getParent(0);
+ ImageDiffHandler handler = new ImageDiffHandler(this, repositoryName,
+ parent.getName(), commit.getName(), imageExtensions);
+ diff = DiffUtils.getDiff(r, commit, blobPath, DiffOutputType.HTML, handler).content;
+ if (handler.getImgDiffCount() > 0) {
+ addBottomScript("scripts/imgdiff.js"); // Tiny support script for image diffs
+ }
add(new BookmarkablePageLink<Void>("patchLink", PatchPage.class,
WicketUtils.newPathParameter(repositoryName, objectId, blobPath)));
} else {
// base commit specified
RevCommit baseCommit = JGitUtils.getCommit(r, baseObjectId);
- diff = DiffUtils.getDiff(r, baseCommit, commit, blobPath, DiffOutputType.HTML).content;
+ ImageDiffHandler handler = new ImageDiffHandler(this, repositoryName,
+ baseCommit.getName(), commit.getName(), imageExtensions);
+ diff = DiffUtils.getDiff(r, baseCommit, commit, blobPath, DiffOutputType.HTML, handler).content;
+ if (handler.getImgDiffCount() > 0) {
+ addBottomScript("scripts/imgdiff.js"); // Tiny support script for image diffs
+ }
add(new BookmarkablePageLink<Void>("patchLink", PatchPage.class,
WicketUtils.newBlobDiffParameter(repositoryName, baseObjectId, objectId,
blobPath)));
diff --git a/src/main/java/com/gitblit/wicket/pages/BlobPage.html b/src/main/java/com/gitblit/wicket/pages/BlobPage.html index 3f9a959a..289c1498 100644 --- a/src/main/java/com/gitblit/wicket/pages/BlobPage.html +++ b/src/main/java/com/gitblit/wicket/pages/BlobPage.html @@ -40,9 +40,8 @@ </wicket:link>
</wicket:head>
+<body>
<wicket:extend>
-<!-- need to specify body.onload -->
-<body onload="prettyPrint()">
<!-- blob nav links -->
<div class="page_nav2">
@@ -61,6 +60,6 @@ <!-- blob image -->
<img wicket:id="blobImage" style="padding-top:5px;"></img>
-</body>
</wicket:extend>
+</body>
</html>
\ No newline at end of file diff --git a/src/main/java/com/gitblit/wicket/pages/BlobPage.java b/src/main/java/com/gitblit/wicket/pages/BlobPage.java index 3c244f9a..df30e4cc 100644 --- a/src/main/java/com/gitblit/wicket/pages/BlobPage.java +++ b/src/main/java/com/gitblit/wicket/pages/BlobPage.java @@ -137,6 +137,7 @@ public class BlobPage extends RepositoryPage { table = missingBlob(blobPath, commit);
} else {
table = generateSourceView(source, extension, type == 1);
+ addBottomScriptInline("jQuery(prettyPrint);");
}
add(new Label("blobText", table).setEscapeModelStrings(false));
add(new Image("blobImage").setVisible(false));
@@ -150,6 +151,7 @@ public class BlobPage extends RepositoryPage { table = missingBlob(blobPath, commit);
} else {
table = generateSourceView(source, null, false);
+ addBottomScriptInline("jQuery(prettyPrint);");
}
add(new Label("blobText", table).setEscapeModelStrings(false));
add(new Image("blobImage").setVisible(false));
diff --git a/src/main/java/com/gitblit/wicket/pages/CommitDiffPage.java b/src/main/java/com/gitblit/wicket/pages/CommitDiffPage.java index d827c449..c838dab5 100644 --- a/src/main/java/com/gitblit/wicket/pages/CommitDiffPage.java +++ b/src/main/java/com/gitblit/wicket/pages/CommitDiffPage.java @@ -31,6 +31,7 @@ import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevCommit; import com.gitblit.Constants; +import com.gitblit.Keys; import com.gitblit.models.GitNote; import com.gitblit.models.PathModel.PathChangeModel; import com.gitblit.models.SubmoduleModel; @@ -59,8 +60,6 @@ public class CommitDiffPage extends RepositoryPage { RevCommit commit = getCommit(); - final DiffOutput diff = DiffUtils.getCommitDiff(r, commit, DiffOutputType.HTML); - List<String> parents = new ArrayList<String>(); if (commit.getParentCount() > 0) { for (RevCommit parent : commit.getParents()) { @@ -82,6 +81,14 @@ public class CommitDiffPage extends RepositoryPage { add(new CommitHeaderPanel("commitHeader", repositoryName, commit)); + final List<String> imageExtensions = app().settings().getStrings(Keys.web.imageExtensions); + final ImageDiffHandler handler = new ImageDiffHandler(this, repositoryName, + parents.isEmpty() ? null : parents.get(0), commit.getName(), imageExtensions); + final DiffOutput diff = DiffUtils.getCommitDiff(r, commit, DiffOutputType.HTML, handler); + if (handler.getImgDiffCount() > 0) { + addBottomScript("scripts/imgdiff.js"); // Tiny support script for image diffs + } + // add commit diffstat int insertions = 0; int deletions = 0; @@ -145,10 +152,10 @@ public class CommitDiffPage extends RepositoryPage { hasSubmodule = submodule.hasSubmodule; // add relative link - item.add(new LinkPanel("pathName", "list", entry.path + " @ " + getShortObjectId(submoduleId), "#" + entry.path)); + item.add(new LinkPanel("pathName", "list", entry.path + " @ " + getShortObjectId(submoduleId), "#n" + entry.objectId)); } else { // add relative link - item.add(new LinkPanel("pathName", "list", entry.path, "#" + entry.path)); + item.add(new LinkPanel("pathName", "list", entry.path, "#n" + entry.objectId)); } // quick links diff --git a/src/main/java/com/gitblit/wicket/pages/ComparePage.java b/src/main/java/com/gitblit/wicket/pages/ComparePage.java index 1ec66133..62ae7c25 100644 --- a/src/main/java/com/gitblit/wicket/pages/ComparePage.java +++ b/src/main/java/com/gitblit/wicket/pages/ComparePage.java @@ -37,6 +37,7 @@ import org.eclipse.jgit.diff.DiffEntry.ChangeType; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevCommit; +import com.gitblit.Keys; import com.gitblit.models.PathModel.PathChangeModel; import com.gitblit.models.RefModel; import com.gitblit.models.RepositoryModel; @@ -111,7 +112,14 @@ public class ComparePage extends RepositoryPage { fromCommitId.setObject(startId); toCommitId.setObject(endId); - final DiffOutput diff = DiffUtils.getDiff(r, fromCommit, toCommit, DiffOutputType.HTML); + final List<String> imageExtensions = app().settings().getStrings(Keys.web.imageExtensions); + final ImageDiffHandler handler = new ImageDiffHandler(this, repositoryName, + fromCommit.getName(), toCommit.getName(), imageExtensions); + + final DiffOutput diff = DiffUtils.getDiff(r, fromCommit, toCommit, DiffOutputType.HTML, handler); + if (handler.getImgDiffCount() > 0) { + addBottomScript("scripts/imgdiff.js"); // Tiny support script for image diffs + } // add compare diffstat int insertions = 0; @@ -160,10 +168,10 @@ public class ComparePage extends RepositoryPage { hasSubmodule = submodule.hasSubmodule; // add relative link - item.add(new LinkPanel("pathName", "list", entry.path + " @ " + getShortObjectId(submoduleId), "#" + entry.path)); + item.add(new LinkPanel("pathName", "list", entry.path + " @ " + getShortObjectId(submoduleId), "#n" + entry.objectId)); } else { // add relative link - item.add(new LinkPanel("pathName", "list", entry.path, "#" + entry.path)); + item.add(new LinkPanel("pathName", "list", entry.path, "#n" + entry.objectId)); } // quick links diff --git a/src/main/java/com/gitblit/wicket/pages/EditTeamPage.java b/src/main/java/com/gitblit/wicket/pages/EditTeamPage.java index f537f33d..a43d8db0 100644 --- a/src/main/java/com/gitblit/wicket/pages/EditTeamPage.java +++ b/src/main/java/com/gitblit/wicket/pages/EditTeamPage.java @@ -39,6 +39,7 @@ import org.apache.wicket.model.util.CollectionModel; import org.apache.wicket.model.util.ListModel;
import com.gitblit.Constants.RegistrantType;
+import com.gitblit.Constants.Role;
import com.gitblit.GitBlitException;
import com.gitblit.Keys;
import com.gitblit.models.RegistrantAccessPermission;
@@ -221,14 +222,23 @@ public class EditTeamPage extends RootSubPage { // do not let the browser pre-populate these fields
form.add(new SimpleAttributeModifier("autocomplete", "off"));
- // not all user services support manipulating team memberships
+ // not all user providers support manipulating team memberships
boolean editMemberships = app().authentication().supportsTeamMembershipChanges(teamModel);
+ // not all user providers support manipulating the admin role
+ boolean changeAdminRole = app().authentication().supportsRoleChanges(teamModel, Role.ADMIN);
+
+ // not all user providers support manipulating the create role
+ boolean changeCreateRole = app().authentication().supportsRoleChanges(teamModel, Role.CREATE);
+
+ // not all user providers support manipulating the fork role
+ boolean changeForkRole = app().authentication().supportsRoleChanges(teamModel, Role.FORK);
+
// field names reflective match TeamModel fields
form.add(new TextField<String>("name"));
- form.add(new CheckBox("canAdmin"));
- form.add(new CheckBox("canFork").setEnabled(app().settings().getBoolean(Keys.web.allowForking, true)));
- form.add(new CheckBox("canCreate"));
+ form.add(new CheckBox("canAdmin").setEnabled(changeAdminRole));
+ form.add(new CheckBox("canFork").setEnabled(app().settings().getBoolean(Keys.web.allowForking, true) && changeForkRole));
+ form.add(new CheckBox("canCreate").setEnabled(changeCreateRole));
form.add(users.setEnabled(editMemberships));
mailingLists = new Model<String>(teamModel.mailingLists == null ? ""
: StringUtils.flattenStrings(teamModel.mailingLists, " "));
diff --git a/src/main/java/com/gitblit/wicket/pages/EditTicketPage.html b/src/main/java/com/gitblit/wicket/pages/EditTicketPage.html index b5fe0ae5..b12d0c77 100644 --- a/src/main/java/com/gitblit/wicket/pages/EditTicketPage.html +++ b/src/main/java/com/gitblit/wicket/pages/EditTicketPage.html @@ -39,6 +39,8 @@ </div>
</td></tr>
<tr wicket:id="status"></tr>
+ <tr><th><wicket:message key="gb.severity"></wicket:message></th><td class="edit"><select class="input-large" wicket:id="severity"></select></td></tr>
+ <tr wicket:id="priority"></tr>
<tr wicket:id="responsible"></tr>
<tr wicket:id="milestone"></tr>
<tr wicket:id="mergeto"></tr>
@@ -71,5 +73,9 @@ <th><wicket:message key="gb.mergeTo"></wicket:message></th><td class="edit"><select class="input-large" wicket:id="mergeto"></select></td>
</wicket:fragment>
+<wicket:fragment wicket:id="priorityFragment">
+ <th><wicket:message key="gb.priority"></wicket:message></th><td class="edit"><select class="input-large" wicket:id="priority"></select></td>
+</wicket:fragment>
+
</wicket:extend>
</html>
\ No newline at end of file diff --git a/src/main/java/com/gitblit/wicket/pages/EditTicketPage.java b/src/main/java/com/gitblit/wicket/pages/EditTicketPage.java index c3d405bc..192b48ca 100644 --- a/src/main/java/com/gitblit/wicket/pages/EditTicketPage.java +++ b/src/main/java/com/gitblit/wicket/pages/EditTicketPage.java @@ -84,6 +84,10 @@ public class EditTicketPage extends RepositoryPage { private Label descriptionPreview;
+ private IModel<TicketModel.Priority> priorityModel;
+
+ private IModel<TicketModel.Severity> severityModel;
+
public EditTicketPage(PageParameters params) {
super(params);
@@ -117,6 +121,8 @@ public class EditTicketPage extends RepositoryPage { milestoneModel = Model.of();
mergeToModel = Model.of(ticket.mergeTo == null ? getRepositoryModel().mergeTo : ticket.mergeTo);
statusModel = Model.of(ticket.status);
+ priorityModel = Model.of(ticket.priority);
+ severityModel = Model.of(ticket.severity);
setStatelessHint(false);
setOutputMarkupId(true);
@@ -161,6 +167,9 @@ public class EditTicketPage extends RepositoryPage { status.add(new DropDownChoice<TicketModel.Status>("status", statusModel, statusChoices));
form.add(status);
+ List<TicketModel.Severity> severityChoices = Arrays.asList(TicketModel.Severity.choices());
+ form.add(new DropDownChoice<TicketModel.Severity>("severity", severityModel, severityChoices));
+
if (currentUser.canAdmin(ticket, getRepositoryModel())) {
// responsible
Set<String> userlist = new TreeSet<String>(ticket.getParticipants());
@@ -214,11 +223,17 @@ public class EditTicketPage extends RepositoryPage { milestones.add(new TicketMilestone(NIL));
}
+ // milestone
Fragment milestone = new Fragment("milestone", "milestoneFragment", this);
-
milestone.add(new DropDownChoice<TicketMilestone>("milestone", milestoneModel, milestones));
form.add(milestone.setVisible(!milestones.isEmpty()));
+ // priority
+ Fragment priority = new Fragment("priority", "priorityFragment", this);
+ List<TicketModel.Priority> priorityChoices = Arrays.asList(TicketModel.Priority.choices());
+ priority.add(new DropDownChoice<TicketModel.Priority>("priority", priorityModel, priorityChoices));
+ form.add(priority);
+
// mergeTo (integration branch)
List<String> branches = new ArrayList<String>();
for (String branch : getRepositoryModel().getLocalBranches()) {
@@ -238,6 +253,7 @@ public class EditTicketPage extends RepositoryPage { form.add(new Label("responsible").setVisible(false));
form.add(new Label("milestone").setVisible(false));
form.add(new Label("mergeto").setVisible(false));
+ form.add(new Label("priority").setVisible(false));
}
form.add(new AjaxButton("update") {
@@ -316,6 +332,18 @@ public class EditTicketPage extends RepositoryPage { }
}
+ TicketModel.Priority priority = priorityModel.getObject();
+ if (!ticket.priority.equals(priority))
+ {
+ change.setField(Field.priority, priority);
+ }
+
+ TicketModel.Severity severity = severityModel.getObject();
+ if (!ticket.severity.equals(severity))
+ {
+ change.setField(Field.severity, severity);
+ }
+
String mergeTo = mergeToModel.getObject();
if ((StringUtils.isEmpty(ticket.mergeTo) && !StringUtils.isEmpty(mergeTo))
|| (!StringUtils.isEmpty(mergeTo) && !mergeTo.equals(ticket.mergeTo))) {
diff --git a/src/main/java/com/gitblit/wicket/pages/EditUserPage.java b/src/main/java/com/gitblit/wicket/pages/EditUserPage.java index 454aa619..c6b5c3c7 100644 --- a/src/main/java/com/gitblit/wicket/pages/EditUserPage.java +++ b/src/main/java/com/gitblit/wicket/pages/EditUserPage.java @@ -35,6 +35,7 @@ import org.apache.wicket.model.util.CollectionModel; import org.apache.wicket.model.util.ListModel;
import com.gitblit.Constants.RegistrantType;
+import com.gitblit.Constants.Role;
import com.gitblit.GitBlitException;
import com.gitblit.Keys;
import com.gitblit.models.RegistrantAccessPermission;
@@ -177,7 +178,9 @@ public class EditUserPage extends RootSubPage { // update user permissions
for (RegistrantAccessPermission repositoryPermission : permissions) {
- userModel.setRepositoryPermission(repositoryPermission.registrant, repositoryPermission.permission);
+ if (repositoryPermission.mutable) {
+ userModel.setRepositoryPermission(repositoryPermission.registrant, repositoryPermission.permission);
+ }
}
Iterator<String> selectedTeams = teams.getSelectedChoices();
@@ -216,18 +219,27 @@ public class EditUserPage extends RootSubPage { // do not let the browser pre-populate these fields
form.add(new SimpleAttributeModifier("autocomplete", "off"));
- // not all user services support manipulating username and password
+ // not all user providers support manipulating username and password
boolean editCredentials = app().authentication().supportsCredentialChanges(userModel);
- // not all user services support manipulating display name
+ // not all user providers support manipulating display name
boolean editDisplayName = app().authentication().supportsDisplayNameChanges(userModel);
- // not all user services support manipulating email address
+ // not all user providers support manipulating email address
boolean editEmailAddress = app().authentication().supportsEmailAddressChanges(userModel);
- // not all user services support manipulating team memberships
+ // not all user providers support manipulating team memberships
boolean editTeams = app().authentication().supportsTeamMembershipChanges(userModel);
+ // not all user providers support manipulating the admin role
+ boolean changeAdminRole = app().authentication().supportsRoleChanges(userModel, Role.ADMIN);
+
+ // not all user providers support manipulating the create role
+ boolean changeCreateRole = app().authentication().supportsRoleChanges(userModel, Role.CREATE);
+
+ // not all user providers support manipulating the fork role
+ boolean changeForkRole = app().authentication().supportsRoleChanges(userModel, Role.FORK);
+
// field names reflective match UserModel fields
form.add(new TextField<String>("username").setEnabled(editCredentials));
PasswordTextField passwordField = new PasswordTextField("password");
@@ -245,7 +257,7 @@ public class EditUserPage extends RootSubPage { // display a disabled-yet-checked checkbox
form.add(new CheckBox("canAdmin", Model.of(true)).setEnabled(false));
} else {
- form.add(new CheckBox("canAdmin"));
+ form.add(new CheckBox("canAdmin").setEnabled(changeAdminRole));
}
if (userModel.canFork() && !userModel.canFork) {
@@ -254,7 +266,7 @@ public class EditUserPage extends RootSubPage { form.add(new CheckBox("canFork", Model.of(true)).setEnabled(false));
} else {
final boolean forkingAllowed = app().settings().getBoolean(Keys.web.allowForking, true);
- form.add(new CheckBox("canFork").setEnabled(forkingAllowed));
+ form.add(new CheckBox("canFork").setEnabled(forkingAllowed && changeForkRole));
}
if (userModel.canCreate() && !userModel.canCreate) {
@@ -262,7 +274,7 @@ public class EditUserPage extends RootSubPage { // display a disabled-yet-checked checkbox
form.add(new CheckBox("canCreate", Model.of(true)).setEnabled(false));
} else {
- form.add(new CheckBox("canCreate"));
+ form.add(new CheckBox("canCreate").setEnabled(changeCreateRole));
}
form.add(new CheckBox("excludeFromFederation"));
diff --git a/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage.java b/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage.java index b3c52436..72d1e1a4 100644 --- a/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage.java +++ b/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage.java @@ -55,7 +55,7 @@ public class EmptyRepositoryPage extends RepositoryPage { }
HttpServletRequest req = ((WebRequest) getRequest()).getHttpServletRequest();
- List<RepositoryUrl> repositoryUrls = app().gitblit().getRepositoryUrls(req, user, repository);
+ List<RepositoryUrl> repositoryUrls = app().services().getRepositoryUrls(req, user, repository);
RepositoryUrl primaryUrl = repositoryUrls.size() == 0 ? null : repositoryUrls.get(0);
String url = primaryUrl != null ? primaryUrl.url : "";
diff --git a/src/main/java/com/gitblit/wicket/pages/ImageDiffHandler.java b/src/main/java/com/gitblit/wicket/pages/ImageDiffHandler.java new file mode 100644 index 00000000..dc0c5ae8 --- /dev/null +++ b/src/main/java/com/gitblit/wicket/pages/ImageDiffHandler.java @@ -0,0 +1,170 @@ +/* + * Copyright 2014 Tom <tw201207@gmail.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.nio.charset.StandardCharsets; +import java.util.List; + +import org.apache.wicket.protocol.http.WebApplication; +import org.apache.wicket.protocol.http.WicketURLEncoder; +import org.eclipse.jgit.diff.DiffEntry; +import org.eclipse.jgit.diff.DiffEntry.Side; +import org.jsoup.nodes.Element; + +import com.gitblit.servlet.RawServlet; +import com.gitblit.utils.DiffUtils; +import com.gitblit.utils.HtmlBuilder; + +/** + * A {@link DiffUtils.BinaryDiffHandler BinaryDiffHandler} for images. + * + * @author Tom <tw201207@gmail.com> + */ +public class ImageDiffHandler implements DiffUtils.BinaryDiffHandler { + + private final String oldCommitId; + private final String newCommitId; + private final String repositoryName; + private final BasePage page; + private final List<String> imageExtensions; + + private int imgDiffCount = 0; + + public ImageDiffHandler(final BasePage page, final String repositoryName, final String oldCommitId, final String newCommitId, + final List<String> imageExtensions) { + this.page = page; + this.repositoryName = repositoryName; + this.oldCommitId = oldCommitId; + this.newCommitId = newCommitId; + this.imageExtensions = imageExtensions; + } + + /** {@inheritDoc} */ + @Override + public String renderBinaryDiff(DiffEntry diffEntry) { + switch (diffEntry.getChangeType()) { + case MODIFY: + case RENAME: + case COPY: + // TODO: for very small images such as icons, the slider doesn't really help. Two possible + // approaches: either upscale them for display (may show blurry upscaled images), or show + // them side by side (may still be too small to really make out the differences). + String oldUrl = getImageUrl(diffEntry, Side.OLD); + String newUrl = getImageUrl(diffEntry, Side.NEW); + if (oldUrl != null && newUrl != null) { + imgDiffCount++; + String id = "imgdiff" + imgDiffCount; + HtmlBuilder builder = new HtmlBuilder("div"); + Element wrapper = builder.root().attr("class", "imgdiff-container").attr("id", "imgdiff-" + id); + Element container = wrapper.appendElement("div").attr("class", "imgdiff-ovr-slider").appendElement("div").attr("class", "imgdiff"); + Element old = container.appendElement("div").attr("class", "imgdiff-left"); + // style='max-width:640px;' is necessary for ensuring that the browser limits large images + // to some reasonable width, and to override the "img { max-width: 100%; }" from bootstrap.css, + // which would scale the left image to the width of its resizeable container, which isn't what + // we want here. Note that the max-width must be defined directly as inline style on the element, + // otherwise browsers ignore it if the image is larger, and we end up with an image display that + // is too wide. + // XXX: Maybe add a max-height, too, to limit portrait-oriented images to some reasonable height? + // (Like a 300x10000px image...) + old.appendElement("img").attr("class", "imgdiff-old").attr("id", id).attr("style", "max-width:640px;").attr("src", oldUrl); + container.appendElement("img").attr("class", "imgdiff").attr("style", "max-width:640px;").attr("src", newUrl); + wrapper.appendElement("br"); + Element controls = wrapper.appendElement("div"); + // Opacity slider + controls.appendElement("div").attr("class", "imgdiff-opa-container").appendElement("a").attr("class", "imgdiff-opa-slider") + .attr("href", "#").attr("title", page.getString("gb.opacityAdjust")); + // Blink comparator: find Pluto! + controls.appendElement("a").attr("class", "imgdiff-link imgdiff-blink").attr("href", "#") + .attr("title", page.getString("gb.blinkComparator")) + .appendElement("img").attr("src", getStaticResourceUrl("blink32.png")).attr("width", "20"); + // Pixel subtraction, initially not displayed, will be shown by imgdiff.js depending on feature test. + // (Uses CSS mix-blend-mode, which isn't supported on all browsers yet). + controls.appendElement("a").attr("class", "imgdiff-link imgdiff-subtract").attr("href", "#") + .attr("title", page.getString("gb.imgdiffSubtract")).attr("style", "display:none;") + .appendElement("img").attr("src", getStaticResourceUrl("sub32.png")).attr("width", "20"); + return builder.toString(); + } + break; + case ADD: + String url = getImageUrl(diffEntry, Side.NEW); + if (url != null) { + return new HtmlBuilder("img").root().attr("class", "diff-img").attr("src", url).toString(); + } + break; + default: + break; + } + return null; + } + + /** Returns the number of image diffs generated so far by this {@link ImageDiffHandler}. */ + public int getImgDiffCount() { + return imgDiffCount; + } + + /** + * Constructs a URL that will fetch the designated resource in the git repository. The returned string will + * contain the URL fully URL-escaped, but note that it may still contain unescaped ampersands, so the result + * must still be run through HTML escaping if it is to be used in HTML. + * + * @return the URL to the image, if the given {@link DiffEntry} and {@link Side} refers to an image, or {@code null} otherwise. + */ + protected String getImageUrl(DiffEntry entry, Side side) { + String path = entry.getPath(side); + int i = path.lastIndexOf('.'); + if (i > 0) { + String extension = path.substring(i + 1); + for (String ext : imageExtensions) { + if (ext.equalsIgnoreCase(extension)) { + String commitId = Side.NEW.equals(side) ? newCommitId : oldCommitId; + if (commitId != null) { + return RawServlet.asLink(page.getContextUrl(), urlencode(repositoryName), commitId, urlencode(path)); + } else { + return null; + } + } + } + } + return null; + } + + /** + * Returns a URL that will fetch the designated static resource from within GitBlit. + */ + protected String getStaticResourceUrl(String contextRelativePath) { + return WebApplication.get().getRequestCycleProcessor().getRequestCodingStrategy().rewriteStaticRelativeUrl(contextRelativePath); + } + + /** + * Encode a URL component of a {@link RawServlet} URL in the special way that the servlet expects it. Note that + * the %-encoding used does not encode '&' or '<'. Slashes are not encoded in the result. + * + * @param component + * to encode using %-encoding + * @return the encoded component + */ + protected String urlencode(final String component) { + // RawServlet handles slashes itself. Note that only the PATH_INSTANCE fits the bill here: it encodes + // spaces as %20, and we just have to correct for encoded slashes. Java's standard URLEncoder would + // encode spaces as '+', and I don't know what effects that would have on other parts of GitBlit. It + // would also be wrong for path components (but fine for a query part), so we'd have to correct it, too. + // + // Actually, this should be done in RawServlet.asLink(). As it is now, this may be incorrect if that + // operation ever uses query parameters instead of paths, or if it is fixed to urlencode its path + // components. But I don't want to touch that static method in RawServlet. + return WicketURLEncoder.PATH_INSTANCE.encode(component, StandardCharsets.UTF_8.name()).replaceAll("%2[fF]", "/"); + } +} diff --git a/src/main/java/com/gitblit/wicket/pages/LogoutPage.html b/src/main/java/com/gitblit/wicket/pages/LogoutPage.html index d4077830..ab3c0dae 100644 --- a/src/main/java/com/gitblit/wicket/pages/LogoutPage.html +++ b/src/main/java/com/gitblit/wicket/pages/LogoutPage.html @@ -14,7 +14,7 @@ <span class="icon-bar"></span> </a> <a class="brand" wicket:id="rootLink"> - <img src="gitblt_25_white.png" width="79" height="25" alt="gitblit" class="logo"/> + <img src="gitblt_25_white.png" width="79" alt="gitblit" class="logo"/> </a> </div> diff --git a/src/main/java/com/gitblit/wicket/pages/MyTicketsPage.html b/src/main/java/com/gitblit/wicket/pages/MyTicketsPage.html index b0bc1949..cf94f3ca 100644 --- a/src/main/java/com/gitblit/wicket/pages/MyTicketsPage.html +++ b/src/main/java/com/gitblit/wicket/pages/MyTicketsPage.html @@ -25,18 +25,19 @@ <!-- query list --> <ul class="nav nav-list"> <li class="nav-header"><wicket:message key="gb.queries"></wicket:message></li> - <li><a wicket:id="changesQuery"><i class="fa fa-code-fork"></i> <wicket:message key="gb.proposalTickets"></wicket:message></a></li> - <li><a wicket:id="bugsQuery"><i class="fa fa-bug"></i> <wicket:message key="gb.bugTickets"></wicket:message></a></li> - <li><a wicket:id="enhancementsQuery"><i class="fa fa-magic"></i> <wicket:message key="gb.enhancementTickets"></wicket:message></a></li> - <li><a wicket:id="tasksQuery"><i class="fa fa-ticket"></i> <wicket:message key="gb.taskTickets"></wicket:message></a></li> - <li><a wicket:id="questionsQuery"><i class="fa fa-question"></i> <wicket:message key="gb.questionTickets"></wicket:message></a></li> + <li><a wicket:id="changesQuery"><i class="fa fa-code-fork fa-fw"></i> <wicket:message key="gb.proposalTickets"></wicket:message></a></li> + <li><a wicket:id="bugsQuery"><i class="fa fa-bug fa-fw"></i> <wicket:message key="gb.bugTickets"></wicket:message></a></li> + <li><a wicket:id="enhancementsQuery"><i class="fa fa-magic fa-fw"></i> <wicket:message key="gb.enhancementTickets"></wicket:message></a></li> + <li><a wicket:id="tasksQuery"><i class="fa fa-ticket fa-fw"></i> <wicket:message key="gb.taskTickets"></wicket:message></a></li> + <li><a wicket:id="questionsQuery"><i class="fa fa-question fa-fw"></i> <wicket:message key="gb.questionTickets"></wicket:message></a></li> + <li><a wicket:id="maintenanceQuery"><i class="fa fa-cogs fa-fw"></i> <wicket:message key="gb.maintenanceTickets"></wicket:message></a></li> <li wicket:id="userDivider" class="divider"></li> - <li><a wicket:id="createdQuery"><i class="fa fa-user"></i> <wicket:message key="gb.yourCreatedTickets"></wicket:message></a></li> - <li><a wicket:id="responsibleQuery"><i class="fa fa-user"></i> <wicket:message key="gb.yourAssignedTickets"></wicket:message></a></li> - <li><a wicket:id="watchedQuery"><i class="fa fa-eye"></i> <wicket:message key="gb.yourWatchedTickets"></wicket:message></a></li> - <li><a wicket:id="mentionsQuery"><i class="fa fa-comment"></i> <wicket:message key="gb.mentionsMeTickets"></wicket:message></a></li> + <li><a wicket:id="createdQuery"><i class="fa fa-user fa-fw"></i> <wicket:message key="gb.yourCreatedTickets"></wicket:message></a></li> + <li><a wicket:id="responsibleQuery"><i class="fa fa-user fa-fw"></i> <wicket:message key="gb.yourAssignedTickets"></wicket:message></a></li> + <li><a wicket:id="watchedQuery"><i class="fa fa-eye fa-fw"></i> <wicket:message key="gb.yourWatchedTickets"></wicket:message></a></li> + <li><a wicket:id="mentionsQuery"><i class="fa fa-comment fa-fw"></i> <wicket:message key="gb.mentionsMeTickets"></wicket:message></a></li> <li class="divider"></li> - <li><a wicket:id="resetQuery"><i class="fa fa-bolt"></i> <wicket:message key="gb.reset"></wicket:message></a></li> + <li><a wicket:id="resetQuery"><i class="fa fa-bolt fa-fw"></i> <wicket:message key="gb.reset"></wicket:message></a></li> </ul> </div> diff --git a/src/main/java/com/gitblit/wicket/pages/MyTicketsPage.java b/src/main/java/com/gitblit/wicket/pages/MyTicketsPage.java index c207d561..187302f5 100644 --- a/src/main/java/com/gitblit/wicket/pages/MyTicketsPage.java +++ b/src/main/java/com/gitblit/wicket/pages/MyTicketsPage.java @@ -132,6 +132,16 @@ public class MyTicketsPage extends RootPage { sortBy, desc, 1))); + + add(new BookmarkablePageLink<Void>("maintenanceQuery", MyTicketsPage.class, + queryParameters( + Lucene.type.matches(TicketModel.Type.Maintenance.name()), + milestoneParam, + statiiParam, + assignedToParam, + sortBy, + desc, + 1))); add(new BookmarkablePageLink<Void>("resetQuery", MyTicketsPage.class, queryParameters( @@ -220,6 +230,10 @@ public class MyTicketsPage extends RootPage { sortChoices.add(new TicketSort(getString("gb.sortLeastPatchsetRevisions"), Lucene.patchsets.name(), false)); sortChoices.add(new TicketSort(getString("gb.sortMostVotes"), Lucene.votes.name(), true)); sortChoices.add(new TicketSort(getString("gb.sortLeastVotes"), Lucene.votes.name(), false)); + sortChoices.add(new TicketSort(getString("gb.sortHighestPriority"), Lucene.priority.name(), true)); + sortChoices.add(new TicketSort(getString("gb.sortLowestPriority"), Lucene.priority.name(), false)); + sortChoices.add(new TicketSort(getString("gb.sortHighestSeverity"), Lucene.severity.name(), true)); + sortChoices.add(new TicketSort(getString("gb.sortLowestSeverity"), Lucene.severity.name(), false)); TicketSort currentSort = sortChoices.get(0); for (TicketSort ts : sortChoices) { diff --git a/src/main/java/com/gitblit/wicket/pages/NewTicketPage.html b/src/main/java/com/gitblit/wicket/pages/NewTicketPage.html index 447c6aa4..9b5af023 100644 --- a/src/main/java/com/gitblit/wicket/pages/NewTicketPage.html +++ b/src/main/java/com/gitblit/wicket/pages/NewTicketPage.html @@ -39,6 +39,8 @@ </div>
</td></tr>
<tr><th><wicket:message key="gb.type"></wicket:message><span style="color:red;">*</span></th><td class="edit"><select class="input-large" wicket:id="type"></select></td></tr>
+ <tr><th><wicket:message key="gb.severity"></wicket:message></th><td class="edit"><select class="input-large" wicket:id="severity"></select></td></tr>
+ <tr wicket:id="priority"></tr>
<tr wicket:id="responsible"></tr>
<tr wicket:id="milestone"></tr>
<tr wicket:id="mergeto"></tr>
@@ -67,5 +69,9 @@ <th><wicket:message key="gb.mergeTo"></wicket:message></th><td class="edit"><select class="input-large" wicket:id="mergeto"></select></td>
</wicket:fragment>
+<wicket:fragment wicket:id="priorityFragment">
+ <th><wicket:message key="gb.priority"></wicket:message></th><td class="edit"><select class="input-large" wicket:id="priority"></select></td>
+</wicket:fragment>
+
</wicket:extend>
</html>
\ No newline at end of file diff --git a/src/main/java/com/gitblit/wicket/pages/NewTicketPage.java b/src/main/java/com/gitblit/wicket/pages/NewTicketPage.java index 8f28055a..0c52505c 100644 --- a/src/main/java/com/gitblit/wicket/pages/NewTicketPage.java +++ b/src/main/java/com/gitblit/wicket/pages/NewTicketPage.java @@ -76,6 +76,10 @@ public class NewTicketPage extends RepositoryPage { private Label descriptionPreview;
+ private IModel<TicketModel.Priority> priorityModel;
+
+ private IModel<TicketModel.Severity> severityModel;
+
public NewTicketPage(PageParameters params) {
super(params);
@@ -95,6 +99,8 @@ public class NewTicketPage extends RepositoryPage { mergeToModel = Model.of(Repository.shortenRefName(getRepositoryModel().mergeTo));
responsibleModel = Model.of();
milestoneModel = Model.of();
+ severityModel = Model.of(TicketModel.Severity.defaultSeverity);
+ priorityModel = Model.of(TicketModel.Priority.defaultPriority);
setStatelessHint(false);
setOutputMarkupId(true);
@@ -105,6 +111,7 @@ public class NewTicketPage extends RepositoryPage { form.add(new DropDownChoice<TicketModel.Type>("type", typeModel, Arrays.asList(TicketModel.Type.choices())));
form.add(new TextField<String>("title", titleModel));
form.add(new TextField<String>("topic", topicModel));
+ form.add(new DropDownChoice<TicketModel.Severity>("severity", severityModel, Arrays.asList(TicketModel.Severity.choices())));
final IModel<String> markdownPreviewModel = Model.of();
descriptionPreview = new Label("descriptionPreview", markdownPreviewModel);
@@ -152,6 +159,11 @@ public class NewTicketPage extends RepositoryPage { milestone.add(new DropDownChoice<TicketMilestone>("milestone", milestoneModel, milestones));
form.add(milestone.setVisible(!milestones.isEmpty()));
+ // priority
+ Fragment priority = new Fragment("priority", "priorityFragment", this);
+ priority.add(new DropDownChoice<TicketModel.Priority>("priority", priorityModel, Arrays.asList(TicketModel.Priority.choices())));
+ form.add(priority);
+
// integration branch
List<String> branches = new ArrayList<String>();
for (String branch : getRepositoryModel().getLocalBranches()) {
@@ -171,6 +183,7 @@ public class NewTicketPage extends RepositoryPage { form.add(new Label("responsible").setVisible(false));
form.add(new Label("milestone").setVisible(false));
form.add(new Label("mergeto").setVisible(false));
+ form.add(new Label("priority").setVisible(false));
}
form.add(new AjaxButton("create") {
@@ -212,6 +225,20 @@ public class NewTicketPage extends RepositoryPage { change.setField(Field.milestone, milestone.name);
}
+ // severity
+ TicketModel.Severity severity = TicketModel.Severity.defaultSeverity;
+ if (severityModel.getObject() != null) {
+ severity = severityModel.getObject();
+ }
+ change.setField(Field.severity, severity);
+
+ // priority
+ TicketModel.Priority priority = TicketModel.Priority.defaultPriority;
+ if (priorityModel.getObject() != null) {
+ priority = priorityModel.getObject();
+ }
+ change.setField(Field.priority, priority);
+
// integration branch
String mergeTo = mergeToModel.getObject();
if (!StringUtils.isEmpty(mergeTo)) {
diff --git a/src/main/java/com/gitblit/wicket/pages/RepositoryPage.html b/src/main/java/com/gitblit/wicket/pages/RepositoryPage.html index 22544bc8..d4132421 100644 --- a/src/main/java/com/gitblit/wicket/pages/RepositoryPage.html +++ b/src/main/java/com/gitblit/wicket/pages/RepositoryPage.html @@ -24,13 +24,20 @@ </div>
<div class="span7">
- <div>
- <span class="project" wicket:id="projectTitle">[project title]</span>/<span class="repository" wicket:id="repositoryName">[repository name]</span>
- <a class="hidden-phone hidden-tablet" style="text-decoration: none;" wicket:id="syndication" wicket:message="title:gb.feed">
- <img style="border:0px;vertical-align:baseline;" src="feed_16x16.png"></img>
- </a>
+ <div style="display:inline-block;vertical-align:top;padding-right:5px;">
+ <span wicket:id="repoIcon"></span>
+ </div>
+ <div style="display:inline-block;">
+ <div>
+ <span class="project" wicket:id="projectTitle">[project title]</span>/<span class="repository" wicket:id="repositoryName">[repository name]</span>
+ <a class="hidden-phone hidden-tablet" style="text-decoration: none;" wicket:id="syndication" wicket:message="title:gb.feed">
+ <img style="border:0px;vertical-align:baseline;" src="feed_16x16.png"></img>
+ </a>
+ </div>
+ <div>
+ <span class="gray" wicket:id="originRepository">[origin repository]</span>
+ </div>
</div>
- <span wicket:id="originRepository">[origin repository]</span>
</div>
</div>
</div>
@@ -60,6 +67,22 @@ <i wicket:id="icon" style="padding-right:3px;"></i><span wicket:id="label"></span>
</wicket:fragment>
+ <wicket:fragment wicket:id="repoIconFragment">
+ <span class="gray mega-octicon octicon-repo"></span>
+ </wicket:fragment>
+
+ <wicket:fragment wicket:id="mirrorIconFragment">
+ <span class="gray mega-octicon octicon-mirror"></span>
+ </wicket:fragment>
+
+ <wicket:fragment wicket:id="forkIconFragment">
+ <span class="gray mega-octicon octicon-repo-forked"></span>
+ </wicket:fragment>
+
+ <wicket:fragment wicket:id="cloneIconFragment">
+ <span class="gray mega-octicon octicon-repo-push" wicket:message="title:gb.workingCopyWarning"></span>
+ </wicket:fragment>
+
<wicket:fragment wicket:id="originFragment">
<p class="originRepository"><wicket:message key="gb.forkedFrom">[forked from]</wicket:message> <span wicket:id="originRepository">[origin repository]</span></p>
</wicket:fragment>
diff --git a/src/main/java/com/gitblit/wicket/pages/RepositoryPage.java b/src/main/java/com/gitblit/wicket/pages/RepositoryPage.java index 134ee044..9639a0c1 100644 --- a/src/main/java/com/gitblit/wicket/pages/RepositoryPage.java +++ b/src/main/java/com/gitblit/wicket/pages/RepositoryPage.java @@ -73,6 +73,7 @@ import com.gitblit.wicket.WicketUtils; import com.gitblit.wicket.panels.LinkPanel;
import com.gitblit.wicket.panels.NavigationPanel;
import com.gitblit.wicket.panels.RefsPanel;
+import com.google.common.base.Optional;
public abstract class RepositoryPage extends RootPage {
@@ -295,25 +296,38 @@ public abstract class RepositoryPage extends RootPage { RepositoryModel model = getRepositoryModel();
if (StringUtils.isEmpty(model.originRepository)) {
if (model.isMirror) {
+ add(new Fragment("repoIcon", "mirrorIconFragment", this));
Fragment mirrorFrag = new Fragment("originRepository", "mirrorFragment", this);
Label lbl = new Label("originRepository", MessageFormat.format(getString("gb.mirrorOf"), "<b>" + model.origin + "</b>"));
mirrorFrag.add(lbl.setEscapeModelStrings(false));
add(mirrorFrag);
} else {
- add(new Label("originRepository").setVisible(false));
+ if (model.isBare) {
+ add(new Fragment("repoIcon", "repoIconFragment", this));
+ } else {
+ add(new Fragment("repoIcon", "cloneIconFragment", this));
+ }
+ add(new Label("originRepository", Optional.of(model.description).or("")));
}
} else {
RepositoryModel origin = app().repositories().getRepositoryModel(model.originRepository);
if (origin == null) {
- // no origin repository
- add(new Label("originRepository").setVisible(false));
+ // no origin repository, show description if available
+ if (model.isBare) {
+ add(new Fragment("repoIcon", "repoIconFragment", this));
+ } else {
+ add(new Fragment("repoIcon", "cloneIconFragment", this));
+ }
+ add(new Label("originRepository", Optional.of(model.description).or("")));
} else if (!user.canView(origin)) {
// show origin repository without link
+ add(new Fragment("repoIcon", "forkIconFragment", this));
Fragment forkFrag = new Fragment("originRepository", "originFragment", this);
forkFrag.add(new Label("originRepository", StringUtils.stripDotGit(model.originRepository)));
add(forkFrag);
} else {
// link to origin repository
+ add(new Fragment("repoIcon", "forkIconFragment", this));
Fragment forkFrag = new Fragment("originRepository", "originFragment", this);
forkFrag.add(new LinkPanel("originRepository", null, StringUtils.stripDotGit(model.originRepository),
SummaryPage.class, WicketUtils.newRepositoryParameter(model.originRepository)));
diff --git a/src/main/java/com/gitblit/wicket/pages/RootPage.html b/src/main/java/com/gitblit/wicket/pages/RootPage.html index 2ff305f2..c51db819 100644 --- a/src/main/java/com/gitblit/wicket/pages/RootPage.html +++ b/src/main/java/com/gitblit/wicket/pages/RootPage.html @@ -14,7 +14,7 @@ <span class="icon-bar"></span>
</a>
<a class="brand" wicket:id="rootLink">
- <img src="logo.png" height="45" width="120" class="logo"/>
+ <img src="logo.png" width="120" class="logo"/>
</a>
<div class="nav-collapse">
diff --git a/src/main/java/com/gitblit/wicket/pages/RootPage.java b/src/main/java/com/gitblit/wicket/pages/RootPage.java index c4d4dd11..37e98702 100644 --- a/src/main/java/com/gitblit/wicket/pages/RootPage.java +++ b/src/main/java/com/gitblit/wicket/pages/RootPage.java @@ -151,6 +151,7 @@ public abstract class RootPage extends BasePage { boolean authenticateAdmin = app().settings().getBoolean(Keys.web.authenticateAdminPages, true); boolean allowAdmin = app().settings().getBoolean(Keys.web.allowAdministration, true); boolean allowLucene = app().settings().getBoolean(Keys.web.allowLuceneIndexing, true); + boolean displayUserPanel = app().settings().getBoolean(Keys.web.displayUserPanel, true); boolean isLoggedIn = GitBlitWebSession.get().isLoggedIn(); if (authenticateAdmin) { @@ -168,7 +169,7 @@ public abstract class RootPage extends BasePage { } } - if (authenticateView || authenticateAdmin) { + if (displayUserPanel && (authenticateView || authenticateAdmin)) { if (isLoggedIn) { UserMenu userFragment = new UserMenu("userPanel", "userMenuFragment", RootPage.this); add(userFragment); diff --git a/src/main/java/com/gitblit/wicket/pages/TicketPage.html b/src/main/java/com/gitblit/wicket/pages/TicketPage.html index f3f38ec3..5ae005eb 100644 --- a/src/main/java/com/gitblit/wicket/pages/TicketPage.html +++ b/src/main/java/com/gitblit/wicket/pages/TicketPage.html @@ -67,6 +67,8 @@ <div style="border: 1px solid #ccc;padding: 10px;margin: 5px 0px;">
<table class="summary" style="width: 100%">
<tr><th><wicket:message key="gb.type"></wicket:message></th><td><span wicket:id="ticketType">[type]</span></td></tr>
+ <tr><th><wicket:message key="gb.priority"></wicket:message></th><td><span wicket:id="priority">[priority]</span></td></tr>
+ <tr><th><wicket:message key="gb.severity"></wicket:message></th><td><span wicket:id="severity">[severity]</span></td></tr>
<tr><th><wicket:message key="gb.topic"></wicket:message></th><td><span wicket:id="ticketTopic">[topic]</span></td></tr>
<tr><th><wicket:message key="gb.responsible"></wicket:message></th><td><span wicket:id="responsible">[responsible]</span></td></tr>
<tr><th><wicket:message key="gb.milestone"></wicket:message></th><td><span wicket:id="milestone">[milestone]</span></td></tr>
@@ -555,7 +557,6 @@ pt push</pre> </div>
</wicket:fragment>
-
<!-- VETOED PATCHSET FRAGMENT -->
<wicket:fragment wicket:id="vetoedFragment">
<div class="alert alert-error submit-info" style="padding:4px;">
diff --git a/src/main/java/com/gitblit/wicket/pages/TicketPage.java b/src/main/java/com/gitblit/wicket/pages/TicketPage.java index b690e4c0..4890874a 100644 --- a/src/main/java/com/gitblit/wicket/pages/TicketPage.java +++ b/src/main/java/com/gitblit/wicket/pages/TicketPage.java @@ -519,6 +519,10 @@ public class TicketPage extends RepositoryPage { * TICKET METADATA
*/
add(new Label("ticketType", ticket.type.toString()));
+
+ add(new Label("priority", ticket.priority.toString()));
+ add(new Label("severity", ticket.severity.toString()));
+
if (StringUtils.isEmpty(ticket.topic)) {
add(new Label("ticketTopic").setVisible(false));
} else {
@@ -529,6 +533,8 @@ public class TicketPage extends RepositoryPage { }
+
+
/*
* VOTERS
*/
@@ -1425,6 +1431,12 @@ public class TicketPage extends RepositoryPage { Fragment mergePanel = new Fragment("mergePanel", "alreadyMergedFragment", this);
mergePanel.add(new Label("mergeTitle", MessageFormat.format(getString("gb.patchsetAlreadyMerged"), ticket.mergeTo)));
return mergePanel;
+ } else if (MergeStatus.MISSING_INTEGRATION_BRANCH == mergeStatus) {
+ // target/integration branch is missing
+ Fragment mergePanel = new Fragment("mergePanel", "notMergeableFragment", this);
+ mergePanel.add(new Label("mergeTitle", MessageFormat.format(getString("gb.patchsetNotMergeable"), ticket.mergeTo)));
+ mergePanel.add(new Label("mergeMore", MessageFormat.format(getString("gb.missingIntegrationBranchMore"), ticket.mergeTo)));
+ return mergePanel;
} else {
// patchset can not be cleanly merged
Fragment mergePanel = new Fragment("mergePanel", "notMergeableFragment", this);
@@ -1503,7 +1515,7 @@ public class TicketPage extends RepositoryPage { */
protected RepositoryUrl getRepositoryUrl(UserModel user, RepositoryModel repository) {
HttpServletRequest req = ((WebRequest) getRequest()).getHttpServletRequest();
- List<RepositoryUrl> urls = app().gitblit().getRepositoryUrls(req, user, repository);
+ List<RepositoryUrl> urls = app().services().getRepositoryUrls(req, user, repository);
if (ArrayUtils.isEmpty(urls)) {
return null;
}
diff --git a/src/main/java/com/gitblit/wicket/pages/TicketsPage.html b/src/main/java/com/gitblit/wicket/pages/TicketsPage.html index 3a3d977a..c686d1ff 100644 --- a/src/main/java/com/gitblit/wicket/pages/TicketsPage.html +++ b/src/main/java/com/gitblit/wicket/pages/TicketsPage.html @@ -31,17 +31,18 @@ <div class="hidden-phone">
<ul class="nav nav-list">
<li class="nav-header"><wicket:message key="gb.queries"></wicket:message></li>
- <li><a wicket:id="changesQuery"><i class="fa fa-code-fork"></i> <wicket:message key="gb.proposalTickets"></wicket:message></a></li>
- <li><a wicket:id="bugsQuery"><i class="fa fa-bug"></i> <wicket:message key="gb.bugTickets"></wicket:message></a></li>
- <li><a wicket:id="enhancementsQuery"><i class="fa fa-magic"></i> <wicket:message key="gb.enhancementTickets"></wicket:message></a></li>
- <li><a wicket:id="tasksQuery"><i class="fa fa-ticket"></i> <wicket:message key="gb.taskTickets"></wicket:message></a></li>
- <li><a wicket:id="questionsQuery"><i class="fa fa-question"></i> <wicket:message key="gb.questionTickets"></wicket:message></a></li>
+ <li><a wicket:id="changesQuery"><i class="fa fa-code-fork fa-fw"></i> <wicket:message key="gb.proposalTickets"></wicket:message></a></li>
+ <li><a wicket:id="bugsQuery"><i class="fa fa-bug fa-fw"></i> <wicket:message key="gb.bugTickets"></wicket:message></a></li>
+ <li><a wicket:id="enhancementsQuery"><i class="fa fa-magic fa-fw"></i> <wicket:message key="gb.enhancementTickets"></wicket:message></a></li>
+ <li><a wicket:id="tasksQuery"><i class="fa fa-ticket fa-fw"></i> <wicket:message key="gb.taskTickets"></wicket:message></a></li>
+ <li><a wicket:id="questionsQuery"><i class="fa fa-question fa-fw"></i> <wicket:message key="gb.questionTickets"></wicket:message></a></li>
+ <li><a wicket:id="maintenanceQuery"><i class="fa fa-cogs fa-fw"></i> <wicket:message key="gb.maintenanceTickets"></wicket:message></a></li>
<li wicket:id="userDivider" class="divider"></li>
- <li><a wicket:id="createdQuery"><i class="fa fa-user"></i> <wicket:message key="gb.yourCreatedTickets"></wicket:message></a></li>
- <li><a wicket:id="watchedQuery"><i class="fa fa-eye"></i> <wicket:message key="gb.yourWatchedTickets"></wicket:message></a></li>
- <li><a wicket:id="mentionsQuery"><i class="fa fa-comment"></i> <wicket:message key="gb.mentionsMeTickets"></wicket:message></a></li>
+ <li><a wicket:id="createdQuery"><i class="fa fa-user fa-fw"></i> <wicket:message key="gb.yourCreatedTickets"></wicket:message></a></li>
+ <li><a wicket:id="watchedQuery"><i class="fa fa-eye fa-fw"></i> <wicket:message key="gb.yourWatchedTickets"></wicket:message></a></li>
+ <li><a wicket:id="mentionsQuery"><i class="fa fa-comment fa-fw"></i> <wicket:message key="gb.mentionsMeTickets"></wicket:message></a></li>
<li class="divider"></li>
- <li><a wicket:id="resetQuery"><i class="fa fa-bolt"></i> <wicket:message key="gb.reset"></wicket:message></a></li>
+ <li><a wicket:id="resetQuery"><i class="fa fa-bolt fa-fw"></i> <wicket:message key="gb.reset"></wicket:message></a></li>
</ul>
</div>
<div wicket:id="dynamicQueries" class="hidden-phone"></div>
diff --git a/src/main/java/com/gitblit/wicket/pages/TicketsPage.java b/src/main/java/com/gitblit/wicket/pages/TicketsPage.java index 658cddec..ecfed250 100644 --- a/src/main/java/com/gitblit/wicket/pages/TicketsPage.java +++ b/src/main/java/com/gitblit/wicket/pages/TicketsPage.java @@ -272,6 +272,16 @@ public class TicketsPage extends RepositoryPage { sortBy,
desc,
1)));
+
+ add(new BookmarkablePageLink<Void>("maintenanceQuery", TicketsPage.class,
+ queryParameters(
+ Lucene.type.matches(TicketModel.Type.Maintenance.name()),
+ milestoneParam,
+ statiiParam,
+ assignedToParam,
+ sortBy,
+ desc,
+ 1)));
add(new BookmarkablePageLink<Void>("resetQuery", TicketsPage.class,
queryParameters(
@@ -454,7 +464,11 @@ public class TicketsPage extends RepositoryPage { sortChoices.add(new TicketSort(getString("gb.sortLeastPatchsetRevisions"), Lucene.patchsets.name(), false));
sortChoices.add(new TicketSort(getString("gb.sortMostVotes"), Lucene.votes.name(), true));
sortChoices.add(new TicketSort(getString("gb.sortLeastVotes"), Lucene.votes.name(), false));
-
+ sortChoices.add(new TicketSort(getString("gb.sortHighestPriority"), Lucene.priority.name(), true));
+ sortChoices.add(new TicketSort(getString("gb.sortLowestPriority"), Lucene.priority.name(), false));
+ sortChoices.add(new TicketSort(getString("gb.sortHighestSeverity"), Lucene.severity.name(), true));
+ sortChoices.add(new TicketSort(getString("gb.sortLowestSeverity"), Lucene.severity.name(), false));
+
TicketSort currentSort = sortChoices.get(0);
for (TicketSort ts : sortChoices) {
if (ts.sortBy.equals(sortBy) && desc == ts.desc) {
diff --git a/src/main/java/com/gitblit/wicket/pages/TreePage.java b/src/main/java/com/gitblit/wicket/pages/TreePage.java index 9ddbecf6..d7899dcb 100644 --- a/src/main/java/com/gitblit/wicket/pages/TreePage.java +++ b/src/main/java/com/gitblit/wicket/pages/TreePage.java @@ -52,7 +52,7 @@ public class TreePage extends RepositoryPage { Repository r = getRepository();
RevCommit commit = getCommit();
- List<PathModel> paths = JGitUtils.getFilesInPath(r, path, commit);
+ List<PathModel> paths = JGitUtils.getFilesInPath2(r, path, commit);
// tree page links
add(new BookmarkablePageLink<Void>("historyLink", HistoryPage.class,
diff --git a/src/main/java/com/gitblit/wicket/pages/UserPage.java b/src/main/java/com/gitblit/wicket/pages/UserPage.java index 8931d5e3..6e7e7a76 100644 --- a/src/main/java/com/gitblit/wicket/pages/UserPage.java +++ b/src/main/java/com/gitblit/wicket/pages/UserPage.java @@ -104,7 +104,7 @@ public class UserPage extends RootPage { if (isMyProfile) {
addPreferences(user);
- if (app().gitblit().isServingSSH()) {
+ if (app().services().isServingSSH()) {
// show the SSH key management tab
addSshKeys(user);
} else {
@@ -248,14 +248,16 @@ public class UserPage extends RootPage { emailMeOnMyTicketChanges).setVisible(app().notifier().isSendingMail()));
List<Transport> availableTransports = new ArrayList<>();
- if (app().gitblit().isServingSSH()) {
+ if (app().services().isServingSSH()) {
availableTransports.add(Transport.SSH);
}
- if (app().gitblit().isServingHTTP()) {
- availableTransports.add(Transport.HTTPS);
+ if (app().services().isServingHTTP()) {
availableTransports.add(Transport.HTTP);
}
- if (app().gitblit().isServingGIT()) {
+ if (app().services().isServingHTTPS()) {
+ availableTransports.add(Transport.HTTPS);
+ }
+ if (app().services().isServingGIT()) {
availableTransports.add(Transport.GIT);
}
diff --git a/src/main/java/com/gitblit/wicket/pages/scripts/imgdiff.js b/src/main/java/com/gitblit/wicket/pages/scripts/imgdiff.js new file mode 100644 index 00000000..61575132 --- /dev/null +++ b/src/main/java/com/gitblit/wicket/pages/scripts/imgdiff.js @@ -0,0 +1,263 @@ +/* + * Copyright 2014 Tom <tw201207@gmail.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. + */ +(function($) { + +/** + * Sets up elem as a slider; returns an access object. Elem must be positioned! + * Note that the element may contain other elements; this is used for instance + * for the image diff overlay slider. + * + * The styling of the slider is to be done in CSS. Currently recognized options: + * - initial: <float> clipped to [0..1], default 0 + * - handleClass: <string> to assign to the handle span element created. + * If no handleClass is specified, a very plain default style is assigned. + */ +function rangeSlider(elem, options) { + options = $.extend({ initial : 0 }, options || {}); + options.initial = Math.min(1.0, Math.max(0, options.initial)); + + var $elem = $(elem); + var $handle = $('<span></span>').css({ position: 'absolute', left: 0, cursor: 'ew-resize' }); + var $root = $(document.documentElement); + var $doc = $(document); + var lastRatio = options.initial; + + /** Mousemove event handler to track the mouse and move the slider. Generates slider:pos events. */ + function track(e) { + var pos = $elem.offset().left; + var width = $elem.innerWidth(); + var handleWidth = $handle.outerWidth(false); + var range = width - handleWidth; + if (range <= 0) return; + var delta = Math.min(range, Math.max (0, e.pageX - pos - handleWidth / 2)); + lastRatio = delta / range; + $handle.css('left', "" + (delta * 100 / width) + '%'); + $elem.trigger('slider:pos', { ratio: lastRatio, handle: $handle[0] }); + } + + /** Mouseup event handler to stop mouse tracking. */ + function end(e) { + $doc.off('mousemove', track); + $doc.off('mouseup', end); + $root.removeClass('no-select'); + } + + /** Snaps the slider to the given ratio and generates a slider:pos event with the new ratio. */ + function setTo(ratio) { + var w = $elem.innerWidth(); + if (w <= 0 || $elem.is(':hidden')) return; + lastRatio = Math.min( 1.0, Math.max(0, ratio)); + $handle.css('left', "" + Math.max(0, 100 * (lastRatio * (w - $handle.outerWidth(false))) / w) + '%'); + $elem.trigger('slider:pos', { ratio: lastRatio, handle: $handle[0] }); + } + + /** + * Moves the slider to the given ratio, clipped to [0..1], in duration milliseconds. + * Generates slider:pos events during the animation. If duration <= 30, same as setTo. + * Default duration is 500ms. If a callback is given, it's called once the animation + * has completed. + */ + function moveTo(ratio, duration, callback) { + ratio = Math.min(1.0, Math.max(0, ratio)); + if (ratio === lastRatio) { + if (typeof callback == 'function') callback(); + return; + } + if (typeof duration == 'undefined') duration = 500; + if (duration <= 30) { + // Cinema is 24 or 48 frames/sec, so 20-40ms per frame. Makes no sense to animate for such a short duration. + setTo(ratio); + if (typeof callback == 'function') callback(); + } else { + var target = ratio * ($elem.innerWidth() - $handle.outerWidth(false)); + if (ratio > lastRatio) target--; else target++; + $handle.stop().animate({left: target}, + { 'duration' : duration, + 'step' : function() { + lastRatio = Math.min(1.0, Math.max(0, $handle.position().left / ($elem.innerWidth() - $handle.outerWidth(false)))); + $elem.trigger('slider:pos', { ratio : lastRatio, handle : $handle[0] }); + }, + 'complete' : function() { setTo(ratio); if (typeof callback == 'function') callback(); } // Ensure we have again a % value + } + ); + } + } + + /** + * As moveTo, but determines an appropriate duration in the range [0..maxDuration] on its own, + * depending on the distance the handle would move. If no maxDuration is given it defaults + * to 1500ms. + */ + function moveAuto(ratio, maxDuration, callback) { + if (typeof maxDuration == 'undefined') maxDuration = 1500; + var delta = ratio - lastRatio; + if (delta < 0) delta = -delta; + var speed = $elem.innerWidth() * delta * 2; + if (speed > maxDuration) speed = maxDuration; + moveTo(ratio, speed, callback); + } + + /** Returns the current ratio. */ + function getValue() { + return lastRatio; + } + + $elem.append($handle); + if (options.handleClass) { + $handle.addClass(options.handleClass); + } else { // Provide a default style so that it is at least visible + $handle.css({ width: '10px', height: '10px', background: 'white', border: '1px solid black' }); + } + if (options.initial) setTo(options.initial); + + /** Install mousedown handler to start mouse tracking. */ + $handle.on('mousedown', function(e) { + $root.addClass('no-select'); + $doc.on('mousemove', track); + $doc.on('mouseup', end); + e.stopPropagation(); + e.preventDefault(); + }); + + return { setRatio: setTo, moveRatio: moveTo, 'moveAuto': moveAuto, getRatio: getValue, handle: $handle[0] }; +} + +function setup() { + $('.imgdiff-container').each(function() { + var $this = $(this); + var $overlaySlider = $this.find('.imgdiff-ovr-slider').first(); + var $opacitySlider = $this.find('.imgdiff-opa-slider').first(); + var overlayAccess = rangeSlider($overlaySlider, {handleClass: 'imgdiff-ovr-handle'}); + var opacityAccess = rangeSlider($opacitySlider, {handleClass: 'imgdiff-opa-handle'}); + var $img = $('#' + this.id.substr(this.id.indexOf('-')+1)); // Here we change opacity + var $div = $img.parent(); // This controls visibility: here we change width. + var blinking = false; + + $overlaySlider.on('slider:pos', function(e, data) { + var pos = $(data.handle).offset().left; + var imgLeft = $img.offset().left; // Global + var imgW = $img.outerWidth(true); + var imgOff = $img.position().left; // From left edge of $div + if (pos <= imgLeft) { + $div.width(0); + } else if (pos <= imgLeft + imgW) { + $div.width(pos - imgLeft + imgOff); + } else if ($div.width() < imgW + imgOff) { + $div.width(imgW + imgOff); + } + }); + $overlaySlider.css('cursor', 'pointer'); + $overlaySlider.on('mousedown', function(e) { + var newRatio = (e.pageX - $overlaySlider.offset().left) / $overlaySlider.innerWidth(); + var oldRatio = overlayAccess.getRatio(); + if (newRatio !== oldRatio) { + overlayAccess.moveAuto(newRatio); + } + }); + + var autoShowing = false; + $opacitySlider.on('slider:pos', function(e, data) { + if ($div.width() <= 0 && !blinking) { + // Make old image visible in a nice way, *then* adjust opacity + autoShowing = true; + overlayAccess.moveAuto(1.0, 500, function() { + $img.stop().animate( + {opacity: 1.0 - opacityAccess.getRatio()}, + {duration: 400, + complete: function () { + // In case the opacity handle was moved while we were trying to catch up + $img.css('opacity', 1.0 - opacityAccess.getRatio()); + autoShowing = false; + } + } + ); + }); + } else if (!autoShowing) { + $img.css('opacity', 1.0 - data.ratio); + } + }); + $opacitySlider.on('click', function(e) { + var newRatio = (e.pageX - $opacitySlider.offset().left) / $opacitySlider.innerWidth(); + var oldRatio = opacityAccess.getRatio(); + if (newRatio !== oldRatio) { + if ($div.width() <= 0) { + overlayAccess.moveRatio(1.0, 500, function() {opacityAccess.moveAuto(newRatio);}); // Make old image visible in a nice way + } else { + opacityAccess.moveAuto(newRatio) + } + } + e.preventDefault(); + }); + + // Blinking before and after images is a good way for the human eye to catch differences. + var $blinker = $this.find('.imgdiff-blink'); + var initialOpacity = null; + $blinker.on('click', function(e) { + if (blinking) { + window.clearTimeout(blinking); + $blinker.children('img').first().css('border', '1px solid transparent'); + opacityAccess.setRatio(initialOpacity); + blinking = null; + } else { + $blinker.children('img').first().css('border', '1px solid #AAA'); + initialOpacity = opacityAccess.getRatio(); + var currentOpacity = 1.0; + function blink() { + opacityAccess.setRatio(currentOpacity); + currentOpacity = 1.0 - currentOpacity; + // Keep frequeny below 2Hz (i.e., delay above 500ms) + blinking = window.setTimeout(blink, 600); + } + if ($div.width() <= 0) { + overlayAccess.moveRatio(1.0, 500, blink); + } else { + blink(); + } + } + e.preventDefault(); + }); + + // Subtracting before and after images is another good way to detect differences. Result will be + // black where identical. + if (typeof $img[0].style.mixBlendMode != 'undefined') { + // Feature test: does the browser support the mix-blend-mode CSS property from the Compositing + // and Blending Level 1 spec (http://dev.w3.org/fxtf/compositing-1/#mix-blend-mode )? + // As of 2014-11, only Firefox >= 32 and Safari >= 7.1 support this. Other browsers will have to + // make do with the blink comparator only. + var $sub = $this.find('.imgdiff-subtract'); + $sub.css('display', 'inline-block'); + $sub.on('click', function (e) { + var curr = $img.css('mix-blend-mode'); + if (curr != 'difference') { + curr = 'difference'; + $sub.children('img').first().css('border', '1px solid #AAA'); + if ($div.width() <= 0) overlayAccess.moveRatio(1.0, 500); + opacityAccess.setRatio(0); + } else { + curr = 'normal'; + $sub.children('img').first().css('border', '1px solid transparent'); + + } + $img.css('mix-blend-mode', curr); + e.preventDefault(); + }); + } + }); +} + +$(setup); // Run on jQuery's dom-ready + +})(jQuery);
\ No newline at end of file diff --git a/src/main/java/com/gitblit/wicket/panels/FilterableRepositoryList.java b/src/main/java/com/gitblit/wicket/panels/FilterableRepositoryList.java index 4433b043..e4ce5ced 100644 --- a/src/main/java/com/gitblit/wicket/panels/FilterableRepositoryList.java +++ b/src/main/java/com/gitblit/wicket/panels/FilterableRepositoryList.java @@ -125,7 +125,15 @@ public class FilterableRepositoryList extends BasePanel { item.t = getTimeUtils().timeAgo(repo.lastChange);
item.d = df.format(repo.lastChange);
item.c = StringUtils.getColor(StringUtils.stripDotGit(repo.name));
- item.wc = repo.isBare ? 0 : 1;
+ if (!repo.isBare) {
+ item.y = 3;
+ } else if (repo.isMirror) {
+ item.y = 2;
+ } else if (repo.isFork()) {
+ item.y = 1;
+ } else {
+ item.y = 0;
+ }
list.add(item);
}
@@ -147,6 +155,6 @@ public class FilterableRepositoryList extends BasePanel { String i; // information/description
long s; // stars
String c; // html color
- int wc; // working copy: 1 = true, 0 = false
+ int y; // type: 0 = normal, 1 = fork, 2 = mirror, 3 = clone
}
}
\ No newline at end of file diff --git a/src/main/java/com/gitblit/wicket/panels/ProjectRepositoryPanel.html b/src/main/java/com/gitblit/wicket/panels/ProjectRepositoryPanel.html index 33345a0a..88de3b4c 100644 --- a/src/main/java/com/gitblit/wicket/panels/ProjectRepositoryPanel.html +++ b/src/main/java/com/gitblit/wicket/panels/ProjectRepositoryPanel.html @@ -24,13 +24,28 @@ <p class="originRepository" style="margin-left:20px;" ><wicket:message key="gb.forkedFrom">[forked from]</wicket:message> <span wicket:id="originRepository">[origin repository]</span></p>
</wicket:fragment>
+ <wicket:fragment wicket:id="repoIconFragment">
+ <span class="mega-octicon octicon-repo"></span>
+ </wicket:fragment>
+
+ <wicket:fragment wicket:id="mirrorIconFragment">
+ <span class="mega-octicon octicon-mirror"></span>
+ </wicket:fragment>
+
+ <wicket:fragment wicket:id="forkIconFragment">
+ <span class="mega-octicon octicon-repo-forked"></span>
+ </wicket:fragment>
+
+ <wicket:fragment wicket:id="cloneIconFragment">
+ <span class="mega-octicon octicon-repo-push" wicket:message="title:gb.workingCopyWarning"></span>
+ </wicket:fragment>
+
<div>
<div style="padding-top:15px;padding-bottom:15px;margin-right:15px;">
<div class="pull-right" style="text-align:right;padding-right:15px;">
<span wicket:id="repositoryLinks"></span>
<div>
<img class="inlineIcon" wicket:id="sparkleshareIcon" />
- <img class="inlineIcon" wicket:id="mirrorIcon" />
<img class="inlineIcon" wicket:id="frozenIcon" />
<img class="inlineIcon" wicket:id="federatedIcon" />
@@ -42,11 +57,15 @@ </div>
<div class="pageTitle" style="border:0px;">
- <div>
- <span class="repositorySwatch" wicket:id="repositorySwatch"></span>
- <span class="repository" style="padding-left:3px;color:black;" wicket:id="repositoryName">[repository name]</span>
- </div>
- <span wicket:id="originRepository">[origin repository]</span>
+ <div style="display:inline-block;vertical-align:top;padding-right:5px;">
+ <span wicket:id="repoIcon"></span>
+ </div>
+ <div style="display:inline-block;">
+ <div>
+ <span class="repository" style="padding-left:3px;color:black;" wicket:id="repositoryName">[repository name]</span>
+ </div>
+ <span wicket:id="originRepository">[origin repository]</span>
+ </div>
</div>
<div style="padding-left:20px;">
diff --git a/src/main/java/com/gitblit/wicket/panels/ProjectRepositoryPanel.java b/src/main/java/com/gitblit/wicket/panels/ProjectRepositoryPanel.java index 8630d201..91ce9864 100644 --- a/src/main/java/com/gitblit/wicket/panels/ProjectRepositoryPanel.java +++ b/src/main/java/com/gitblit/wicket/panels/ProjectRepositoryPanel.java @@ -51,24 +51,27 @@ public class ProjectRepositoryPanel extends BasePanel { final boolean showSwatch = app().settings().getBoolean(Keys.web.repositoryListSwatches, true);
final boolean showSize = app().settings().getBoolean(Keys.web.showRepositorySizes, true);
- // repository swatch
- Component swatch;
- if (entry.isBare) {
- swatch = new Label("repositorySwatch", " ").setEscapeModelStrings(false);
- } else {
- swatch = new Label("repositorySwatch", "!");
- WicketUtils.setHtmlTooltip(swatch, localizer.getString("gb.workingCopyWarning", parent));
- }
- WicketUtils.setCssBackground(swatch, entry.toString());
- add(swatch);
- swatch.setVisible(showSwatch);
-
PageParameters pp = WicketUtils.newRepositoryParameter(entry.name);
add(new LinkPanel("repositoryName", "list", StringUtils.getRelativePath(entry.projectPath,
StringUtils.stripDotGit(entry.name)), SummaryPage.class, pp));
add(new Label("repositoryDescription", entry.description).setVisible(!StringUtils
.isEmpty(entry.description)));
+ Fragment iconFragment;
+ if (entry.isMirror) {
+ iconFragment = new Fragment("repoIcon", "mirrorIconFragment", this);
+ } else if (entry.isFork()) {
+ iconFragment = new Fragment("repoIcon", "forkIconFragment", this);
+ } else if (entry.isBare) {
+ iconFragment = new Fragment("repoIcon", "repoIconFragment", this);
+ } else {
+ iconFragment = new Fragment("repoIcon", "cloneIconFragment", this);
+ }
+ if (showSwatch) {
+ WicketUtils.setCssStyle(iconFragment, "color:" + StringUtils.getColor(entry.toString()));
+ }
+ add(iconFragment);
+
if (StringUtils.isEmpty(entry.originRepository)) {
add(new Label("originRepository").setVisible(false));
} else {
@@ -84,13 +87,7 @@ public class ProjectRepositoryPanel extends BasePanel { add(WicketUtils.newClearPixel("sparkleshareIcon").setVisible(false));
}
- if (entry.isMirror) {
- add(WicketUtils.newImage("mirrorIcon", "mirror_16x16.png", localizer.getString("gb.isMirror", parent)));
- } else {
- add(WicketUtils.newClearPixel("mirrorIcon").setVisible(false));
- }
-
- if (entry.isFrozen) {
+ if (!entry.isMirror && entry.isFrozen) {
add(WicketUtils.newImage("frozenIcon", "cold_16x16.png", localizer.getString("gb.isFrozen", parent)));
} else {
add(WicketUtils.newClearPixel("frozenIcon").setVisible(false));
diff --git a/src/main/java/com/gitblit/wicket/panels/RepositoriesPanel.html b/src/main/java/com/gitblit/wicket/panels/RepositoriesPanel.html index e2e7b72b..2de52b09 100644 --- a/src/main/java/com/gitblit/wicket/panels/RepositoriesPanel.html +++ b/src/main/java/com/gitblit/wicket/panels/RepositoriesPanel.html @@ -17,6 +17,22 @@ </tr>
</tbody>
</table>
+
+ <wicket:fragment wicket:id="repoIconFragment">
+ <span class="octicon octicon-centered octicon-repo"></span>
+ </wicket:fragment>
+
+ <wicket:fragment wicket:id="mirrorIconFragment">
+ <span class="octicon octicon-centered octicon-mirror"></span>
+ </wicket:fragment>
+
+ <wicket:fragment wicket:id="forkIconFragment">
+ <span class="octicon octicon-centered octicon-repo-forked"></span>
+ </wicket:fragment>
+
+ <wicket:fragment wicket:id="cloneIconFragment">
+ <span class="octicon octicon-centered octicon-repo-push" wicket:message="title:gb.workingCopyWarning"></span>
+ </wicket:fragment>
<wicket:fragment wicket:id="adminLinks">
<!-- page nav links -->
@@ -76,10 +92,10 @@ </wicket:fragment>
<wicket:fragment wicket:id="repositoryRow">
- <td class="left" style="padding-left:3px;" ><b><span class="repositorySwatch" wicket:id="repositorySwatch"></span></b> <span style="padding-left:3px;" wicket:id="repositoryName">[repository name]</span></td>
+ <td class="left" style="padding-left:3px;" ><span wicket:id="repoIcon"></span><span style="padding-left:3px;" wicket:id="repositoryName">[repository name]</span></td>
<td class="hidden-phone"><span class="list" wicket:id="repositoryDescription">[repository description]</span></td>
<td class="hidden-tablet hidden-phone author"><span wicket:id="repositoryOwner">[repository owner]</span></td>
- <td class="hidden-phone" style="text-align: right;padding-right:10px;"><img class="inlineIcon" wicket:id="sparkleshareIcon" /><img class="inlineIcon" wicket:id="mirrorIcon" /><img class="inlineIcon" wicket:id="forkIcon" /><img class="inlineIcon" wicket:id="frozenIcon" /><img class="inlineIcon" wicket:id="federatedIcon" /><img class="inlineIcon" wicket:id="accessRestrictionIcon" /></td>
+ <td class="hidden-phone" style="text-align: right;padding-right:10px;"><img class="inlineIcon" wicket:id="sparkleshareIcon" /><img class="inlineIcon" wicket:id="frozenIcon" /><img class="inlineIcon" wicket:id="federatedIcon" /><img class="inlineIcon" wicket:id="accessRestrictionIcon" /></td>
<td><span wicket:id="repositoryLastChange">[last change]</span></td>
<td class="rightAlign hidden-phone" style="text-align: right;padding-right:15px;"><span style="font-size:0.8em;" wicket:id="repositorySize">[repository size]</span></td>
</wicket:fragment>
diff --git a/src/main/java/com/gitblit/wicket/panels/RepositoriesPanel.java b/src/main/java/com/gitblit/wicket/panels/RepositoriesPanel.java index 8573e1a6..c3f07099 100644 --- a/src/main/java/com/gitblit/wicket/panels/RepositoriesPanel.java +++ b/src/main/java/com/gitblit/wicket/panels/RepositoriesPanel.java @@ -24,7 +24,6 @@ import java.util.Iterator; import java.util.List;
import java.util.Map;
-import org.apache.wicket.Component;
import org.apache.wicket.PageParameters;
import org.apache.wicket.extensions.markup.html.repeater.data.sort.OrderByBorder;
import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
@@ -183,24 +182,28 @@ public class RepositoriesPanel extends BasePanel { Fragment row = new Fragment("rowContent", "repositoryRow", this);
item.add(row);
+ // show colored repository type icon
+ Fragment iconFragment;
+ if (entry.isMirror) {
+ iconFragment = new Fragment("repoIcon", "mirrorIconFragment", this);
+ } else if (entry.isFork()) {
+ iconFragment = new Fragment("repoIcon", "forkIconFragment", this);
+ } else if (entry.isBare) {
+ iconFragment = new Fragment("repoIcon", "repoIconFragment", this);
+ } else {
+ iconFragment = new Fragment("repoIcon", "cloneIconFragment", this);
+ }
+ if (showSwatch) {
+ WicketUtils.setCssStyle(iconFragment, "color:" + StringUtils.getColor(entry.toString()));
+ }
+ row.add(iconFragment);
+
// try to strip group name for less cluttered list
String repoName = entry.toString();
if (!StringUtils.isEmpty(currGroupName) && (repoName.indexOf('/') > -1)) {
repoName = repoName.substring(currGroupName.length() + 1);
}
- // repository swatch
- Component swatch;
- if (entry.isBare){
- swatch = new Label("repositorySwatch", " ").setEscapeModelStrings(false);
- } else {
- swatch = new Label("repositorySwatch", "!");
- WicketUtils.setHtmlTooltip(swatch, getString("gb.workingCopyWarning"));
- }
- WicketUtils.setCssBackground(swatch, entry.toString());
- row.add(swatch);
- swatch.setVisible(showSwatch);
-
if (linksActive) {
Class<? extends BasePage> linkPage = SummaryPage.class;
PageParameters pp = WicketUtils.newRepositoryParameter(entry.name);
@@ -228,21 +231,7 @@ public class RepositoriesPanel extends BasePanel { row.add(WicketUtils.newClearPixel("sparkleshareIcon").setVisible(false));
}
- if (entry.isMirror) {
- row.add(WicketUtils.newImage("mirrorIcon", "mirror_16x16.png",
- getString("gb.isMirror")));
- } else {
- row.add(WicketUtils.newClearPixel("mirrorIcon").setVisible(false));
- }
-
- if (entry.isFork()) {
- row.add(WicketUtils.newImage("forkIcon", "commit_divide_16x16.png",
- getString("gb.isFork")));
- } else {
- row.add(WicketUtils.newClearPixel("forkIcon").setVisible(false));
- }
-
- if (entry.isFrozen) {
+ if (!entry.isMirror && entry.isFrozen) {
row.add(WicketUtils.newImage("frozenIcon", "cold_16x16.png",
getString("gb.isFrozen")));
} else {
@@ -255,24 +244,30 @@ public class RepositoriesPanel extends BasePanel { } else {
row.add(WicketUtils.newClearPixel("federatedIcon").setVisible(false));
}
- switch (entry.accessRestriction) {
- case NONE:
- row.add(WicketUtils.newBlankImage("accessRestrictionIcon"));
- break;
- case PUSH:
- row.add(WicketUtils.newImage("accessRestrictionIcon", "lock_go_16x16.png",
- accessRestrictionTranslations.get(entry.accessRestriction)));
- break;
- case CLONE:
- row.add(WicketUtils.newImage("accessRestrictionIcon", "lock_pull_16x16.png",
- accessRestrictionTranslations.get(entry.accessRestriction)));
- break;
- case VIEW:
- row.add(WicketUtils.newImage("accessRestrictionIcon", "shield_16x16.png",
- accessRestrictionTranslations.get(entry.accessRestriction)));
- break;
- default:
- row.add(WicketUtils.newBlankImage("accessRestrictionIcon"));
+
+ if (entry.isMirror) {
+ row.add(WicketUtils.newImage("accessRestrictionIcon", "mirror_16x16.png",
+ getString("gb.isMirror")));
+ } else {
+ switch (entry.accessRestriction) {
+ case NONE:
+ row.add(WicketUtils.newBlankImage("accessRestrictionIcon"));
+ break;
+ case PUSH:
+ row.add(WicketUtils.newImage("accessRestrictionIcon", "lock_go_16x16.png",
+ accessRestrictionTranslations.get(entry.accessRestriction)));
+ break;
+ case CLONE:
+ row.add(WicketUtils.newImage("accessRestrictionIcon", "lock_pull_16x16.png",
+ accessRestrictionTranslations.get(entry.accessRestriction)));
+ break;
+ case VIEW:
+ row.add(WicketUtils.newImage("accessRestrictionIcon", "shield_16x16.png",
+ accessRestrictionTranslations.get(entry.accessRestriction)));
+ break;
+ default:
+ row.add(WicketUtils.newBlankImage("accessRestrictionIcon"));
+ }
}
String owner = "";
diff --git a/src/main/java/com/gitblit/wicket/panels/RepositoryUrlPanel.java b/src/main/java/com/gitblit/wicket/panels/RepositoryUrlPanel.java index 938226a6..0666fcd8 100644 --- a/src/main/java/com/gitblit/wicket/panels/RepositoryUrlPanel.java +++ b/src/main/java/com/gitblit/wicket/panels/RepositoryUrlPanel.java @@ -80,7 +80,7 @@ public class RepositoryUrlPanel extends BasePanel { HttpServletRequest req = ((WebRequest) getRequest()).getHttpServletRequest();
- List<RepositoryUrl> repositoryUrls = app().gitblit().getRepositoryUrls(req, user, repository);
+ List<RepositoryUrl> repositoryUrls = app().services().getRepositoryUrls(req, user, repository);
// grab primary url from the top of the list
primaryUrl = repositoryUrls.size() == 0 ? null : repositoryUrls.get(0);
@@ -165,7 +165,7 @@ public class RepositoryUrlPanel extends BasePanel { if (repository.isMirror) {
urlPanel.add(WicketUtils.newImage("accessRestrictionIcon", "mirror_16x16.png",
getString("gb.isMirror")));
- } else if (app().gitblit().isServingRepositories()) {
+ } else if (app().services().isServingRepositories()) {
switch (repository.accessRestriction) {
case NONE:
urlPanel.add(WicketUtils.newClearPixel("accessRestrictionIcon").setVisible(false));
diff --git a/src/main/java/com/gitblit/wicket/panels/TeamsPanel.html b/src/main/java/com/gitblit/wicket/panels/TeamsPanel.html index ff689292..2cce7b18 100644 --- a/src/main/java/com/gitblit/wicket/panels/TeamsPanel.html +++ b/src/main/java/com/gitblit/wicket/panels/TeamsPanel.html @@ -15,16 +15,18 @@ <img style="vertical-align: middle; border: 1px solid #888; background-color: white;" src="users_16x16.png"/>
<wicket:message key="gb.teams">[teams]</wicket:message>
</th>
+ <th class="hidden-phone" style="width:140px;"><wicket:message key="gb.type">[type]</wicket:message></th>
<th class="hidden-phone" style="width:140px;"><wicket:message key="gb.teamMembers">[team members]</wicket:message></th>
<th class="hidden-phone" style="width:100px;"><wicket:message key="gb.repositories">[repositories]</wicket:message></th>
<th style="width:80px;" class="right"></th>
</tr>
- <tbody>
+ <tbody>
<tr wicket:id="teamRow">
<td class="left" ><div class="list" wicket:id="teamname">[teamname]</div></td>
+ <td class="hidden-phone left" ><span style="font-size: 0.8em;" wicket:id="accountType">[account type]</span></td>
<td class="hidden-phone left" ><div class="list" wicket:id="members">[members]</div></td>
<td class="hidden-phone left" ><div class="list" wicket:id="repositories">[repositories]</div></td>
- <td class="rightAlign"><span wicket:id="teamLinks"></span></td>
+ <td class="rightAlign"><span wicket:id="teamLinks"></span></td>
</tr>
</tbody>
</table>
diff --git a/src/main/java/com/gitblit/wicket/panels/TeamsPanel.java b/src/main/java/com/gitblit/wicket/panels/TeamsPanel.java index c1e1a43d..7f3fd9a2 100644 --- a/src/main/java/com/gitblit/wicket/panels/TeamsPanel.java +++ b/src/main/java/com/gitblit/wicket/panels/TeamsPanel.java @@ -60,6 +60,7 @@ public class TeamsPanel extends BasePanel { EditTeamPage.class, WicketUtils.newTeamnameParameter(entry.name));
WicketUtils.setHtmlTooltip(editLink, getString("gb.edit") + " " + entry.name);
item.add(editLink);
+ item.add(new Label("accountType", entry.accountType.name()));
item.add(new Label("members", entry.users.size() > 0 ? ("" + entry.users.size())
: ""));
item.add(new Label("repositories",
diff --git a/src/main/java/com/gitblit/wicket/panels/TicketListPanel.html b/src/main/java/com/gitblit/wicket/panels/TicketListPanel.html index 30f50367..659baeac 100644 --- a/src/main/java/com/gitblit/wicket/panels/TicketListPanel.html +++ b/src/main/java/com/gitblit/wicket/panels/TicketListPanel.html @@ -30,6 +30,9 @@ <td class="hidden-phone ticket-list-state">
<i wicket:message="title:gb.watching" style="color:#888;" class="fa fa-eye" wicket:id="watching"></i>
</td>
+ <td class="ticket-list-priority">
+ <div wicket:id="priority"></div>
+ </td>
<td class="ticket-list-state">
<div wicket:id="status"></div>
</td>
diff --git a/src/main/java/com/gitblit/wicket/panels/TicketListPanel.java b/src/main/java/com/gitblit/wicket/panels/TicketListPanel.java index cc0b57a8..2ac33d53 100644 --- a/src/main/java/com/gitblit/wicket/panels/TicketListPanel.java +++ b/src/main/java/com/gitblit/wicket/panels/TicketListPanel.java @@ -83,7 +83,10 @@ public class TicketListPanel extends BasePanel { item.add(new Label("ticketsLink").setVisible(false)); } - item.add(TicketsUI.getStateIcon("state", ticket.type, ticket.status)); + Label icon = TicketsUI.getStateIcon("state", ticket.type, ticket.status, ticket.severity); + WicketUtils.addCssClass(icon, TicketsUI.getSeverityClass(ticket.severity)); + item.add(icon); + item.add(new Label("id", "" + ticket.number)); UserModel creator = app().users().getUserModel(ticket.createdBy); if (creator != null) { @@ -167,6 +170,11 @@ public class TicketListPanel extends BasePanel { // watching indicator item.add(new Label("watching").setVisible(ticket.isWatching(GitBlitWebSession.get().getUsername()))); + // priority indicator + Label priorityIcon = TicketsUI.getPriorityIcon("priority", ticket.priority); + WicketUtils.addCssClass(priorityIcon, TicketsUI.getPriorityClass(ticket.priority)); + item.add(priorityIcon.setVisible(true)); + // status indicator String css = TicketsUI.getLozengeClass(ticket.status, true); Label l = new Label("status", ticket.status.toString()); |