diff options
Diffstat (limited to 'src')
190 files changed, 9385 insertions, 3127 deletions
diff --git a/src/main/distrib/data/gitblit.properties b/src/main/distrib/data/gitblit.properties index 3215094e..65fe41e5 100644 --- a/src/main/distrib/data/gitblit.properties +++ b/src/main/distrib/data/gitblit.properties @@ -271,6 +271,11 @@ git.defaultIncrementalPushTagPrefix = r # SINCE 1.4.0
git.createRepositoriesShared = false
+# Directory for gitignore templates used during repository creation.
+#
+# SINCE 1.6.0
+git.gitignoreFolder = ${baseFolder}/gitignore
+
# Enable JGit-based garbage collection. (!!EXPERIMENTAL!!)
#
# USE AT YOUR OWN RISK!
@@ -613,6 +618,12 @@ plugins.folder = ${baseFolder}/plugins # SINCE 1.5.0
plugins.registry = http://plugins.gitblit.com/plugins.json
+# Number of threads used to handle miscellaneous tasks in the background.
+#
+# SINCE 1.6.0
+# RESTART REQUIRED
+execution.defaultThreadPoolSize = 1
+
#
# Groovy Integration
#
@@ -766,6 +777,16 @@ web.authenticateAdminPages = true # SINCE 0.5.0
web.allowCookieAuthentication = true
+# Allow deletion of non-empty repositories. This is enforced for all delete vectors.
+#
+# SINCE 1.6.0
+web.allowDeletingNonEmptyRepositories = true
+
+# Setting to include personal repositories in the main repositories list.
+#
+# SINCE 1.6.0
+web.includePersonalRepositories = false
+
# Config file for storing project metadata
#
# SINCE 1.2.0
@@ -1410,6 +1431,11 @@ mail.debug = false # use SMTPs flag
mail.smtps = false
+# use STARTTLS flag
+#
+# SINCE 1.6.0
+mail.starttls = false
+
# if your smtp server requires authentication, supply the credentials here
#
# SINCE 0.6.0
@@ -1779,7 +1805,8 @@ realm.ldap.removeDeletedUsers = true realm.redmine.url = http://example.com/redmine
#
-# Server Settings
+# Gitblit GO Server Settings
+# The following settings only affect the integrated GO variant.
#
# The temporary folder to decompress the embedded gitblit webapp.
@@ -1789,7 +1816,9 @@ realm.redmine.url = http://example.com/redmine # BASEFOLDER
server.tempFolder = ${baseFolder}/temp
-# Specify the maximum number of concurrent http/https worker threads to allow.
+# Specify the maximum number of concurrent http/https Jetty worker
+# threads to allow. This setting does not affect other threaded
+# daemons and components of Gitblit.
#
# SINCE 1.3.0
# RESTART REQUIRED
diff --git a/src/main/distrib/data/gitignore b/src/main/distrib/data/gitignore new file mode 160000 +Subproject 097db81c08b138dea7cb031eb18eeb16afe44bd diff --git a/src/main/distrib/linux/migrate-tickets.sh b/src/main/distrib/linux/migrate-tickets.sh new file mode 100644 index 00000000..f521528e --- /dev/null +++ b/src/main/distrib/linux/migrate-tickets.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# -------------------------------------------------------------------------- +# This is for migrating Tickets from one service to another. +# +# usage: +# +# migrate-tickets.sh <outputservice> <baseFolder> +# +# -------------------------------------------------------------------------- + +if [[ -z $1 || -z $2 ]]; then + echo "Please specify the output ticket service and your baseFolder!"; + echo ""; + echo "usage:"; + echo " migrate-tickets <outputservice> <baseFolder>"; + echo ""; + exit 1; +fi + +java -cp gitblit.jar:./ext/* com.gitblit.MigrateTickets $1 --baseFolder $2 + diff --git a/src/main/distrib/linux/reindex-tickets.sh b/src/main/distrib/linux/reindex-tickets.sh index 5a4fc34f..8261b819 100644 --- a/src/main/distrib/linux/reindex-tickets.sh +++ b/src/main/distrib/linux/reindex-tickets.sh @@ -11,7 +11,7 @@ # # -------------------------------------------------------------------------- -if [ -z $1 ]; then +if [[ -z $1 ]]; then echo "Please specify your baseFolder!"; echo ""; echo "usage:"; diff --git a/src/main/distrib/win/migrate-tickets.cmd b/src/main/distrib/win/migrate-tickets.cmd new file mode 100644 index 00000000..5a26c8ed --- /dev/null +++ b/src/main/distrib/win/migrate-tickets.cmd @@ -0,0 +1,21 @@ +@REM --------------------------------------------------------------------------
+@REM This is for migrating Tickets from one service to another.
+@REM
+@REM usage:
+@REM migrate-tickets <outputservice> <baseFolder>
+@REM
+@REM --------------------------------------------------------------------------
+@if [%1]==[] goto help
+
+@if [%2]==[] goto help
+
+@java -cp gitblit.jar;"%CD%\ext\*" com.gitblit.MigrateTickets %1 --baseFolder %2
+@goto end
+
+:help
+@echo "Please specify the output ticket service and your baseFolder!"
+@echo
+@echo " migrate-tickets com.gitblit.tickets.RedisTicketService c:/gitblit-data"
+@echo
+
+:end
\ No newline at end of file diff --git a/src/main/java/WEB-INF/web.xml b/src/main/java/WEB-INF/web.xml index 77456d47..13f612e4 100644 --- a/src/main/java/WEB-INF/web.xml +++ b/src/main/java/WEB-INF/web.xml @@ -30,12 +30,8 @@ </env-entry>
<!-- Gitblit Displayname -->
- <display-name>
- Gitblit - @gb.version@
- </display-name>
+ <display-name>Gitblit - @gb.version@</display-name>
- <!-- PARAMS -->
-
<!-- Gitblit Context Listener --><!-- STRIP
<listener>
@@ -134,6 +130,21 @@ </servlet-mapping>
+ <!-- Raw Servlet
+ <url-pattern> MUST match:
+ * RawFilter
+ * com.gitblit.Constants.RAW_PATH
+ * Wicket Filter ignorePaths parameter -->
+ <servlet>
+ <servlet-name>RawServlet</servlet-name>
+ <servlet-class>com.gitblit.servlet.RawServlet</servlet-class>
+ </servlet>
+ <servlet-mapping>
+ <servlet-name>RawServlet</servlet-name>
+ <url-pattern>/raw/*</url-pattern>
+ </servlet-mapping>
+
+
<!-- Pages Servlet
<url-pattern> MUST match:
* PagesFilter
@@ -199,6 +210,15 @@ <url-pattern>/robots.txt</url-pattern>
</servlet-mapping>
+ <filter>
+ <filter-name>ProxyFilter</filter-name>
+ <filter-class>com.gitblit.servlet.ProxyFilter</filter-class>
+ </filter>
+ <filter-mapping>
+ <filter-name>ProxyFilter</filter-name>
+ <url-pattern>/*</url-pattern>
+ </filter-mapping>
+
<!-- Git Access Restriction Filter
<url-pattern> MUST match:
* GitServlet
@@ -263,7 +283,22 @@ </filter-mapping>
- <!-- Pges Restriction Filter
+ <!-- Branch Restriction Filter
+ <url-pattern> MUST match:
+ * RawServlet
+ * com.gitblit.Constants.BRANCH_PATH
+ * Wicket Filter ignorePaths parameter -->
+ <filter>
+ <filter-name>RawFilter</filter-name>
+ <filter-class>com.gitblit.servlet.RawFilter</filter-class>
+ </filter>
+ <filter-mapping>
+ <filter-name>RawFilter</filter-name>
+ <url-pattern>/raw/*</url-pattern>
+ </filter-mapping>
+
+
+ <!-- Pages Restriction Filter
<url-pattern> MUST match:
* PagesServlet
* com.gitblit.Constants.PAGES_PATH
@@ -310,10 +345,12 @@ * FederationServlet <url-pattern>
* RpcFilter <url-pattern>
* RpcServlet <url-pattern>
+ * RawFilter <url-pattern>
+ * RawServlet <url-pattern>
* PagesFilter <url-pattern>
* PagesServlet <url-pattern>
* com.gitblit.Constants.PAGES_PATH -->
- <param-value>r/,git/,pt,feed/,zip/,federation/,rpc/,pages/,robots.txt,logo.png,graph/,sparkleshare/</param-value>
+ <param-value>r/,git/,pt,feed/,zip/,federation/,rpc/,raw/,pages/,robots.txt,logo.png,graph/,sparkleshare/</param-value>
</init-param>
</filter>
<filter-mapping>
@@ -321,4 +358,4 @@ <url-pattern>/*</url-pattern>
</filter-mapping>
-</web-app>
+</web-app>
\ No newline at end of file diff --git a/src/main/java/com/gitblit/ConfigUserService.java b/src/main/java/com/gitblit/ConfigUserService.java index 9b4dd7f1..d7d6c14f 100644 --- a/src/main/java/com/gitblit/ConfigUserService.java +++ b/src/main/java/com/gitblit/ConfigUserService.java @@ -24,6 +24,7 @@ import java.util.Collection; import java.util.Collections;
import java.util.HashSet;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -36,6 +37,7 @@ import org.slf4j.LoggerFactory; import com.gitblit.Constants.AccessPermission;
import com.gitblit.Constants.AccountType;
+import com.gitblit.Constants.Transport;
import com.gitblit.manager.IRuntimeManager;
import com.gitblit.models.TeamModel;
import com.gitblit.models.UserModel;
@@ -96,6 +98,10 @@ public class ConfigUserService implements IUserService { private static final String LOCALE = "locale";
+ private static final String EMAILONMYTICKETCHANGES = "emailMeOnMyTicketChanges";
+
+ private static final String TRANSPORT = "transport";
+
private static final String ACCOUNTTYPE = "accountType";
private static final String DISABLED = "disabled";
@@ -707,8 +713,21 @@ public class ConfigUserService implements IUserService { config.setBoolean(USER, model.username, DISABLED, true);
}
if (model.getPreferences() != null) {
- if (!StringUtils.isEmpty(model.getPreferences().locale)) {
- config.setString(USER, model.username, LOCALE, model.getPreferences().locale);
+ Locale locale = model.getPreferences().getLocale();
+ if (locale != null) {
+ String val;
+ if (StringUtils.isEmpty(locale.getCountry())) {
+ val = locale.getLanguage();
+ } else {
+ val = locale.getLanguage() + "_" + locale.getCountry();
+ }
+ config.setString(USER, model.username, LOCALE, val);
+ }
+
+ config.setBoolean(USER, model.username, EMAILONMYTICKETCHANGES, model.getPreferences().isEmailMeOnMyTicketChanges());
+
+ if (model.getPreferences().getTransport() != null) {
+ config.setString(USER, model.username, TRANSPORT, model.getPreferences().getTransport().name());
}
}
@@ -880,11 +899,15 @@ public class ConfigUserService implements IUserService { user.stateProvince = config.getString(USER, username, STATEPROVINCE);
user.countryCode = config.getString(USER, username, COUNTRYCODE);
user.cookie = config.getString(USER, username, COOKIE);
- user.getPreferences().locale = config.getString(USER, username, LOCALE);
if (StringUtils.isEmpty(user.cookie) && !StringUtils.isEmpty(user.password)) {
user.cookie = StringUtils.getSHA1(user.username + user.password);
}
+ // preferences
+ user.getPreferences().setLocale(config.getString(USER, username, LOCALE));
+ user.getPreferences().setEmailMeOnMyTicketChanges(config.getBoolean(USER, username, EMAILONMYTICKETCHANGES, true));
+ user.getPreferences().setTransport(Transport.fromString(config.getString(USER, username, TRANSPORT)));
+
// user roles
Set<String> roles = new HashSet<String>(Arrays.asList(config.getStringList(
USER, username, ROLE)));
diff --git a/src/main/java/com/gitblit/Constants.java b/src/main/java/com/gitblit/Constants.java index e4d92e1a..3e307537 100644 --- a/src/main/java/com/gitblit/Constants.java +++ b/src/main/java/com/gitblit/Constants.java @@ -68,6 +68,8 @@ public class Constants { public static final String SPARKLESHARE_INVITE_PATH = "/sparkleshare/";
+ public static final String RAW_PATH = "/raw/";
+
public static final String BRANCH_GRAPH_PATH = "/graph/";
public static final String BORDER = "*****************************************************************";
@@ -120,6 +122,14 @@ public class Constants { public static final String R_TICKETS_PATCHSETS = "refs/tickets/";
+ public static final String R_MASTER = "refs/heads/master";
+
+ public static final String MASTER = "master";
+
+ public static final String R_DEVELOP = "refs/heads/develop";
+
+ public static final String DEVELOP = "develop";
+
public static String getVersion() {
String v = Constants.class.getPackage().getImplementationVersion();
if (v == null) {
@@ -348,9 +358,10 @@ public class Constants { * a client.
*/
public static enum RpcRequest {
- // Order is important here. anything above LIST_SETTINGS requires
+ // Order is important here. anything after LIST_SETTINGS requires
// administrator privileges and web.allowRpcManagement.
- CLEAR_REPOSITORY_CACHE, REINDEX_TICKETS, GET_PROTOCOL, LIST_REPOSITORIES, LIST_BRANCHES, GET_USER, LIST_SETTINGS,
+ CLEAR_REPOSITORY_CACHE, REINDEX_TICKETS, GET_PROTOCOL, LIST_REPOSITORIES, LIST_BRANCHES, GET_USER,
+ FORK_REPOSITORY, LIST_SETTINGS,
CREATE_REPOSITORY, EDIT_REPOSITORY, DELETE_REPOSITORY,
LIST_USERS, CREATE_USER, EDIT_USER, DELETE_USER,
LIST_TEAMS, CREATE_TEAM, EDIT_TEAM, DELETE_TEAM,
@@ -400,6 +411,27 @@ public class Constants { }
/**
+ * Enumeration of the feed content object types.
+ */
+ public static enum FeedObjectType {
+ COMMIT, TAG;
+
+ public static FeedObjectType forName(String name) {
+ for (FeedObjectType type : values()) {
+ if (type.name().equalsIgnoreCase(name)) {
+ return type;
+ }
+ }
+ return COMMIT;
+ }
+
+ @Override
+ public String toString() {
+ return name().toLowerCase();
+ }
+ }
+
+ /**
* The types of objects that can be indexed and queried.
*/
public static enum SearchObjectType {
diff --git a/src/main/java/com/gitblit/DaggerModule.java b/src/main/java/com/gitblit/DaggerModule.java index b89f8c44..6ad3fe63 100644 --- a/src/main/java/com/gitblit/DaggerModule.java +++ b/src/main/java/com/gitblit/DaggerModule.java @@ -91,8 +91,11 @@ public class DaggerModule { return new NotificationManager(settings); } - @Provides @Singleton IUserManager provideUserManager(IRuntimeManager runtimeManager) { - return new UserManager(runtimeManager); + @Provides @Singleton IUserManager provideUserManager( + IRuntimeManager runtimeManager, + IPluginManager pluginManager) { + + return new UserManager(runtimeManager, pluginManager); } @Provides @Singleton IAuthenticationManager provideAuthenticationManager( @@ -131,10 +134,12 @@ public class DaggerModule { @Provides @Singleton IRepositoryManager provideRepositoryManager( IRuntimeManager runtimeManager, + IPluginManager pluginManager, IUserManager userManager) { return new RepositoryManager( runtimeManager, + pluginManager, userManager); } diff --git a/src/main/java/com/gitblit/FederationClient.java b/src/main/java/com/gitblit/FederationClient.java index c3dcd9da..29cdefe6 100644 --- a/src/main/java/com/gitblit/FederationClient.java +++ b/src/main/java/com/gitblit/FederationClient.java @@ -94,8 +94,8 @@ public class FederationClient { // configure the Gitblit singleton for minimal, non-server operation
RuntimeManager runtime = new RuntimeManager(settings, baseFolder).start();
NoopNotificationManager notifications = new NoopNotificationManager().start();
- UserManager users = new UserManager(runtime).start();
- RepositoryManager repositories = new RepositoryManager(runtime, users).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);
@@ -166,6 +166,11 @@ public class FederationClient { }
@Override
+ public boolean isSendingMail() {
+ return false;
+ }
+
+ @Override
public void sendMailToAdministrators(String subject, String message) {
}
diff --git a/src/main/java/com/gitblit/GitBlit.java b/src/main/java/com/gitblit/GitBlit.java index 3db5f087..f9d9be9f 100644 --- a/src/main/java/com/gitblit/GitBlit.java +++ b/src/main/java/com/gitblit/GitBlit.java @@ -20,6 +20,7 @@ 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; @@ -117,6 +118,21 @@ public class GitBlit extends GitblitManager { 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()}; } @@ -249,6 +265,24 @@ public class GitBlit extends GitblitManager { } }); + // 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; } diff --git a/src/main/java/com/gitblit/MigrateTickets.java b/src/main/java/com/gitblit/MigrateTickets.java new file mode 100644 index 00000000..ad1c63ea --- /dev/null +++ b/src/main/java/com/gitblit/MigrateTickets.java @@ -0,0 +1,256 @@ +/* + * 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; + +import java.io.File; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import org.kohsuke.args4j.Argument; +import org.kohsuke.args4j.CmdLineException; +import org.kohsuke.args4j.CmdLineParser; +import org.kohsuke.args4j.Option; + +import com.gitblit.manager.IRepositoryManager; +import com.gitblit.manager.IRuntimeManager; +import com.gitblit.manager.RepositoryManager; +import com.gitblit.manager.RuntimeManager; +import com.gitblit.models.RepositoryModel; +import com.gitblit.models.TicketModel; +import com.gitblit.models.TicketModel.Change; +import com.gitblit.tickets.BranchTicketService; +import com.gitblit.tickets.FileTicketService; +import com.gitblit.tickets.ITicketService; +import com.gitblit.tickets.RedisTicketService; +import com.gitblit.utils.StringUtils; + +/** + * A command-line tool to move all tickets from one ticket service to another. + * + * @author James Moger + * + */ +public class MigrateTickets { + + public static void main(String... args) { + MigrateTickets migrate = new MigrateTickets(); + + // filter out the baseFolder parameter + List<String> filtered = new ArrayList<String>(); + String folder = "data"; + for (int i = 0; i < args.length; i++) { + String arg = args[i]; + if (arg.equals("--baseFolder")) { + if (i + 1 == args.length) { + System.out.println("Invalid --baseFolder parameter!"); + System.exit(-1); + } else if (!".".equals(args[i + 1])) { + folder = args[i + 1]; + } + i = i + 1; + } else { + filtered.add(arg); + } + } + + Params.baseFolder = folder; + Params params = new Params(); + CmdLineParser parser = new CmdLineParser(params); + try { + parser.parseArgument(filtered); + if (params.help) { + migrate.usage(parser, null); + return; + } + } catch (CmdLineException t) { + migrate.usage(parser, t); + return; + } + + // load the settings + FileSettings settings = params.FILESETTINGS; + if (!StringUtils.isEmpty(params.settingsfile)) { + if (new File(params.settingsfile).exists()) { + settings = new FileSettings(params.settingsfile); + } + } + + // migrate tickets + migrate.migrate(new File(Params.baseFolder), settings, params.outputServiceName); + System.exit(0); + } + + /** + * Display the command line usage of MigrateTickets. + * + * @param parser + * @param t + */ + protected final void usage(CmdLineParser parser, CmdLineException t) { + System.out.println(Constants.BORDER); + System.out.println(Constants.getGitBlitVersion()); + System.out.println(Constants.BORDER); + System.out.println(); + if (t != null) { + System.out.println(t.getMessage()); + System.out.println(); + } + if (parser != null) { + parser.printUsage(System.out); + System.out + .println("\nExample:\n java -gitblit.jar com.gitblit.MigrateTickets com.gitblit.tickets.RedisTicketService --baseFolder c:\\gitblit-data"); + } + System.exit(0); + } + + /** + * Migrate all tickets + * + * @param baseFolder + * @param settings + * @param outputServiceName + */ + protected void migrate(File baseFolder, IStoredSettings settings, String outputServiceName) { + // disable some services + settings.overrideSetting(Keys.web.allowLuceneIndexing, false); + settings.overrideSetting(Keys.git.enableGarbageCollection, false); + settings.overrideSetting(Keys.git.enableMirroring, false); + settings.overrideSetting(Keys.web.activityCacheDays, 0); + settings.overrideSetting(ITicketService.SETTING_UPDATE_DIFFSTATS, false); + + IRuntimeManager runtimeManager = new RuntimeManager(settings, baseFolder).start(); + IRepositoryManager repositoryManager = new RepositoryManager(runtimeManager, null, null).start(); + + String inputServiceName = settings.getString(Keys.tickets.service, BranchTicketService.class.getSimpleName()); + if (StringUtils.isEmpty(inputServiceName)) { + System.err.println(MessageFormat.format("Please define a ticket service in \"{0}\"", Keys.tickets.service)); + System.exit(1); + } + + ITicketService inputService = null; + ITicketService outputService = null; + try { + inputService = getService(inputServiceName, runtimeManager, repositoryManager); + outputService = getService(outputServiceName, runtimeManager, repositoryManager); + } catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } + + if (!inputService.isReady()) { + System.err.println(String.format("%s INPUT service is not ready, check config.", inputService.getClass().getSimpleName())); + System.exit(1); + } + + if (!outputService.isReady()) { + System.err.println(String.format("%s OUTPUT service is not ready, check config.", outputService.getClass().getSimpleName())); + System.exit(1); + } + + // migrate tickets + long start = System.nanoTime(); + long totalTickets = 0; + long totalChanges = 0; + for (RepositoryModel repository : repositoryManager.getRepositoryModels(null)) { + Set<Long> ids = inputService.getIds(repository); + if (ids == null || ids.isEmpty()) { + // nothing to migrate + continue; + } + + // delete any tickets we may have in the output ticket service + outputService.deleteAll(repository); + + for (long id : ids) { + List<Change> journal = inputService.getJournal(repository, id); + if (journal == null || journal.size() == 0) { + continue; + } + TicketModel ticket = outputService.createTicket(repository, id, journal.get(0)); + if (ticket == null) { + System.err.println(String.format("Failed to migrate %s #%s", repository.name, id)); + System.exit(1); + } + totalTickets++; + System.out.println(String.format("%s #%s: %s", repository.name, ticket.number, ticket.title)); + for (int i = 1; i < journal.size(); i++) { + TicketModel updated = outputService.updateTicket(repository, ticket.number, journal.get(i)); + if (updated != null) { + System.out.println(String.format(" applied change %d", i)); + totalChanges++; + } else { + System.err.println(String.format("Failed to apply change %d:\n%s", i, journal.get(i))); + System.exit(1); + } + } + } + } + + inputService.stop(); + outputService.stop(); + + repositoryManager.stop(); + runtimeManager.stop(); + + long end = System.nanoTime(); + + System.out.println(String.format("Migrated %d tickets composed of %d journal entries in %d seconds", + totalTickets, totalTickets + totalChanges, TimeUnit.NANOSECONDS.toSeconds(end - start))); + } + + protected ITicketService getService(String serviceName, IRuntimeManager runtimeManager, IRepositoryManager repositoryManager) throws Exception { + ITicketService service = null; + Class<?> serviceClass = Class.forName(serviceName); + if (RedisTicketService.class.isAssignableFrom(serviceClass)) { + // Redis ticket service + service = new RedisTicketService(runtimeManager, null, null, null, repositoryManager).start(); + } else if (BranchTicketService.class.isAssignableFrom(serviceClass)) { + // Branch ticket service + service = new BranchTicketService(runtimeManager, null, null, null, repositoryManager).start(); + } else if (FileTicketService.class.isAssignableFrom(serviceClass)) { + // File ticket service + service = new FileTicketService(runtimeManager, null, null, null, repositoryManager).start(); + } else { + System.err.println("Unknown ticket service " + serviceName); + } + return service; + } + + /** + * Parameters. + */ + public static class Params { + + public static String baseFolder; + + @Option(name = "--help", aliases = { "-h"}, usage = "Show this help") + public Boolean help = false; + + private final FileSettings FILESETTINGS = new FileSettings(new File(baseFolder, Constants.PROPERTIES_FILE).getAbsolutePath()); + + @Option(name = "--repositoriesFolder", usage = "Git Repositories Folder", metaVar = "PATH") + public String repositoriesFolder = FILESETTINGS.getString(Keys.git.repositoriesFolder, "git"); + + @Option(name = "--settings", usage = "Path to alternative settings", metaVar = "FILE") + public String settingsfile; + + @Argument(index = 0, required = true, metaVar = "OUTPUTSERVICE", usage = "The destination/output ticket service") + public String outputServiceName; + } +} diff --git a/src/main/java/com/gitblit/ReindexTickets.java b/src/main/java/com/gitblit/ReindexTickets.java index 51ca1657..5a614481 100644 --- a/src/main/java/com/gitblit/ReindexTickets.java +++ b/src/main/java/com/gitblit/ReindexTickets.java @@ -127,7 +127,7 @@ public class ReindexTickets { settings.overrideSetting(Keys.web.activityCacheDays, 0); IRuntimeManager runtimeManager = new RuntimeManager(settings, baseFolder).start(); - IRepositoryManager repositoryManager = new RepositoryManager(runtimeManager, null).start(); + IRepositoryManager repositoryManager = new RepositoryManager(runtimeManager, null, null).start(); String serviceName = settings.getString(Keys.tickets.service, BranchTicketService.class.getSimpleName()); if (StringUtils.isEmpty(serviceName)) { diff --git a/src/main/java/com/gitblit/client/GitblitClient.java b/src/main/java/com/gitblit/client/GitblitClient.java index 66625a8e..f5bba1ad 100644 --- a/src/main/java/com/gitblit/client/GitblitClient.java +++ b/src/main/java/com/gitblit/client/GitblitClient.java @@ -253,9 +253,11 @@ public class GitblitClient implements Serializable { // create list of available scripts by excluding inherited scripts
List<String> scripts = new ArrayList<String>();
- for (String script : settings.pushScripts) {
- if (!inherited.contains(script)) {
- scripts.add(script);
+ if (!ArrayUtils.isEmpty(settings.pushScripts)) {
+ for (String script : settings.pushScripts) {
+ if (!inherited.contains(script)) {
+ scripts.add(script);
+ }
}
}
return scripts;
diff --git a/src/main/java/com/gitblit/dagger/DaggerFilter.java b/src/main/java/com/gitblit/dagger/DaggerFilter.java index 1c73d4b7..01c07a4a 100644 --- a/src/main/java/com/gitblit/dagger/DaggerFilter.java +++ b/src/main/java/com/gitblit/dagger/DaggerFilter.java @@ -36,10 +36,10 @@ public abstract class DaggerFilter implements Filter { public final void init(FilterConfig filterConfig) throws ServletException {
ServletContext context = filterConfig.getServletContext();
ObjectGraph objectGraph = (ObjectGraph) context.getAttribute(DaggerContext.INJECTOR_NAME);
- inject(objectGraph);
+ inject(objectGraph, filterConfig);
}
- protected abstract void inject(ObjectGraph dagger);
+ protected abstract void inject(ObjectGraph dagger, FilterConfig filterConfig) throws ServletException;
@Override
public void destroy() {
diff --git a/src/main/java/com/gitblit/extensions/GitblitWicketPlugin.java b/src/main/java/com/gitblit/extensions/GitblitWicketPlugin.java new file mode 100644 index 00000000..130f4993 --- /dev/null +++ b/src/main/java/com/gitblit/extensions/GitblitWicketPlugin.java @@ -0,0 +1,49 @@ +/* + * 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.extensions; + +import org.apache.wicket.Application; +import org.apache.wicket.IInitializer; + +import ro.fortsoft.pf4j.PluginWrapper; + +import com.gitblit.wicket.GitblitWicketApp; + +/** + * A Gitblit plugin that is allowed to extend the Wicket webapp. + * + * @author James Moger + * @since 1.6.0 + */ +public abstract class GitblitWicketPlugin extends GitblitPlugin implements IInitializer { + + public GitblitWicketPlugin(PluginWrapper wrapper) { + super(wrapper); + } + + @Override + public final void init(Application application) { + init((GitblitWicketApp) application); + } + + /** + * Allows plugins to extend the web application. + * + * @param app + * @since 1.6.0 + */ + protected abstract void init(GitblitWicketApp app); +}
\ No newline at end of file diff --git a/src/main/java/com/gitblit/extensions/HttpRequestFilter.java b/src/main/java/com/gitblit/extensions/HttpRequestFilter.java new file mode 100644 index 00000000..e3e330cb --- /dev/null +++ b/src/main/java/com/gitblit/extensions/HttpRequestFilter.java @@ -0,0 +1,49 @@ +/* + * 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.extensions; + +import java.io.IOException; + +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 ro.fortsoft.pf4j.ExtensionPoint; + +/** + * Extension point to intercept HTTP requests passing through the server. + * + * @author David Ostrovsky + * @since 1.6.0 + * + */ +public abstract class HttpRequestFilter implements Filter, ExtensionPoint { + + @Override + public void init(FilterConfig config) throws ServletException { + } + + @Override + public void destroy() { + } + + @Override + public abstract void doFilter(ServletRequest request, ServletResponse response, + FilterChain chain) throws IOException, ServletException; +} diff --git a/src/main/java/com/gitblit/extensions/LifeCycleListener.java b/src/main/java/com/gitblit/extensions/LifeCycleListener.java new file mode 100644 index 00000000..84c7348d --- /dev/null +++ b/src/main/java/com/gitblit/extensions/LifeCycleListener.java @@ -0,0 +1,43 @@ +/* + * 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.extensions; + +import ro.fortsoft.pf4j.ExtensionPoint; + +/** + * Extension point to allow plugins to listen to major Gitblit lifecycle events. + * + * @author James Moger + * @since 1.6.0 + */ +public abstract class LifeCycleListener implements ExtensionPoint { + + /** + * Called after all internal managers have been started. + * This may be useful for reporting "server is ready" to a monitoring system. + * + * @since 1.6.0 + */ + public abstract void onStartup(); + + /** + * Called when the servlet container is gracefully shutting-down the webapp. + * This is called before the internal managers are stopped. + * + * @since 1.6.0 + */ + public abstract void onShutdown(); +} diff --git a/src/main/java/com/gitblit/extensions/NavLinkExtension.java b/src/main/java/com/gitblit/extensions/NavLinkExtension.java new file mode 100644 index 00000000..c8958603 --- /dev/null +++ b/src/main/java/com/gitblit/extensions/NavLinkExtension.java @@ -0,0 +1,40 @@ +/* + * 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.extensions; + +import java.util.List; + +import ro.fortsoft.pf4j.ExtensionPoint; + +import com.gitblit.models.NavLink; +import com.gitblit.models.UserModel; + +/** + * Extension point to contribute top-level navigation links. + * + * @author James Moger + * @since 1.6.0 + * + */ +public abstract class NavLinkExtension implements ExtensionPoint { + + /** + * @param user + * @since 1.6.0 + * @return a list of nav links + */ + public abstract List<NavLink> getNavLinks(UserModel user); +} diff --git a/src/main/java/com/gitblit/extensions/RepositoryLifeCycleListener.java b/src/main/java/com/gitblit/extensions/RepositoryLifeCycleListener.java new file mode 100644 index 00000000..5ef03af7 --- /dev/null +++ b/src/main/java/com/gitblit/extensions/RepositoryLifeCycleListener.java @@ -0,0 +1,45 @@ +/* + * 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.extensions; + +import ro.fortsoft.pf4j.ExtensionPoint; + +import com.gitblit.models.RepositoryModel; + +/** + * Extension point to allow plugins to listen to major repository lifecycle events. + * + * @author James Moger + * @since 1.6.0 + */ +public abstract class RepositoryLifeCycleListener implements ExtensionPoint { + + /** + * Called after a repository has been created. + * + * @param repository + * @since 1.6.0 + */ + public abstract void onCreation(RepositoryModel repository); + + /** + * Called after a repository has been deleted. + * + * @param repository + * @since 1.6.0 + */ + public abstract void onDeletion(RepositoryModel repository); +} diff --git a/src/main/java/com/gitblit/extensions/RepositoryNavLinkExtension.java b/src/main/java/com/gitblit/extensions/RepositoryNavLinkExtension.java new file mode 100644 index 00000000..2b05c5a0 --- /dev/null +++ b/src/main/java/com/gitblit/extensions/RepositoryNavLinkExtension.java @@ -0,0 +1,42 @@ +/* + * 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.extensions; + +import java.util.List; + +import ro.fortsoft.pf4j.ExtensionPoint; + +import com.gitblit.models.NavLink; +import com.gitblit.models.RepositoryModel; +import com.gitblit.models.UserModel; + +/** + * Extension point to contribute repository page navigation links. + * + * @author James Moger + * @since 1.6.0 + * + */ +public abstract class RepositoryNavLinkExtension implements ExtensionPoint { + + /** + * @param user + * @param repository + * @since 1.6.0 + * @return a list of nav links + */ + public abstract List<NavLink> getNavLinks(UserModel user, RepositoryModel repository); +} diff --git a/src/main/java/com/gitblit/extensions/UserMenuExtension.java b/src/main/java/com/gitblit/extensions/UserMenuExtension.java new file mode 100644 index 00000000..078dbfd4 --- /dev/null +++ b/src/main/java/com/gitblit/extensions/UserMenuExtension.java @@ -0,0 +1,40 @@ +/* + * 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.extensions; + +import java.util.List; + +import ro.fortsoft.pf4j.ExtensionPoint; + +import com.gitblit.models.Menu.MenuItem; +import com.gitblit.models.UserModel; + +/** + * Extension point to contribute user menu items. + * + * @author James Moger + * @since 1.6.0 + * + */ +public abstract class UserMenuExtension implements ExtensionPoint { + + /** + * @param user + * @since 1.6.0 + * @return a list of menu items + */ + public abstract List<MenuItem> getMenuItems(UserModel user); +} diff --git a/src/main/java/com/gitblit/extensions/UserTeamLifeCycleListener.java b/src/main/java/com/gitblit/extensions/UserTeamLifeCycleListener.java new file mode 100644 index 00000000..6f4cd9ba --- /dev/null +++ b/src/main/java/com/gitblit/extensions/UserTeamLifeCycleListener.java @@ -0,0 +1,62 @@ +/* + * 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.extensions; + +import ro.fortsoft.pf4j.ExtensionPoint; + +import com.gitblit.models.TeamModel; +import com.gitblit.models.UserModel; + +/** + * Extension point to allow plugins to listen to major user and team lifecycle events. + * + * @author James Moger + * @since 1.6.0 + */ +public abstract class UserTeamLifeCycleListener implements ExtensionPoint { + + /** + * Called after a user has been created. + * + * @param user + * @since 1.6.0 + */ + public abstract void onCreation(UserModel user); + + /** + * Called after a user has been deleted. + * + * @param user + * @since 1.6.0 + */ + public abstract void onDeletion(UserModel user); + + /** + * Called after a team has been created. + * + * @param team + * @since 1.6.0 + */ + public abstract void onCreation(TeamModel team); + + /** + * Called after a team has been deleted. + * + * @param team + * @since 1.6.0 + */ + public abstract void onDeletion(TeamModel team); +} diff --git a/src/main/java/com/gitblit/git/GitblitReceivePack.java b/src/main/java/com/gitblit/git/GitblitReceivePack.java index b4449f06..34bbea27 100644 --- a/src/main/java/com/gitblit/git/GitblitReceivePack.java +++ b/src/main/java/com/gitblit/git/GitblitReceivePack.java @@ -331,6 +331,43 @@ public class GitblitReceivePack extends ReceivePack implements PreReceiveHook, P return;
}
+ logRefChange(commands);
+ updateIncrementalPushTags(commands);
+ updateGitblitRefLog(commands);
+
+ // check for updates pushed to the BranchTicketService branch
+ // if the BranchTicketService is active it will reindex, as appropriate
+ for (ReceiveCommand cmd : commands) {
+ if (Result.OK.equals(cmd.getResult())
+ && BranchTicketService.BRANCH.equals(cmd.getRefName())) {
+ rp.getRepository().fireEvent(new ReceiveCommandEvent(repository, cmd));
+ }
+ }
+
+ // call post-receive plugins
+ for (ReceiveHook hook : gitblit.getExtensions(ReceiveHook.class)) {
+ try {
+ hook.onPostReceive(this, commands);
+ } catch (Exception e) {
+ LOGGER.error("Failed to execute extension", e);
+ }
+ }
+
+ // run Groovy hook scripts
+ Set<String> scripts = new LinkedHashSet<String>();
+ scripts.addAll(gitblit.getPostReceiveScriptsInherited(repository));
+ if (!ArrayUtils.isEmpty(repository.postReceiveScripts)) {
+ scripts.addAll(repository.postReceiveScripts);
+ }
+ runGroovy(commands, scripts);
+ }
+
+ /**
+ * Log the ref changes in the container log.
+ *
+ * @param commands
+ */
+ protected void logRefChange(Collection<ReceiveCommand> commands) {
boolean isRefCreationOrDeletion = false;
// log ref changes
@@ -362,76 +399,65 @@ public class GitblitReceivePack extends ReceivePack implements PreReceiveHook, P if (isRefCreationOrDeletion) {
gitblit.resetRepositoryCache(repository.name);
}
+ }
- if (repository.useIncrementalPushTags) {
- // tag each pushed branch tip
- String emailAddress = user.emailAddress == null ? rp.getRefLogIdent().getEmailAddress() : user.emailAddress;
- PersonIdent userIdent = new PersonIdent(user.getDisplayName(), emailAddress);
-
- for (ReceiveCommand cmd : commands) {
- if (!cmd.getRefName().startsWith(Constants.R_HEADS)) {
- // only tag branch ref changes
- continue;
- }
-
- if (!ReceiveCommand.Type.DELETE.equals(cmd.getType())
- && ReceiveCommand.Result.OK.equals(cmd.getResult())) {
- String objectId = cmd.getNewId().getName();
- String branch = cmd.getRefName().substring(Constants.R_HEADS.length());
- // get translation based on the server's locale setting
- String template = Translation.get("gb.incrementalPushTagMessage");
- String msg = MessageFormat.format(template, branch);
- String prefix;
- if (StringUtils.isEmpty(repository.incrementalPushTagPrefix)) {
- prefix = settings.getString(Keys.git.defaultIncrementalPushTagPrefix, "r");
- } else {
- prefix = repository.incrementalPushTagPrefix;
- }
-
- JGitUtils.createIncrementalRevisionTag(
- rp.getRepository(),
- objectId,
- userIdent,
- prefix,
- "0",
- msg);
- }
- }
+ /**
+ * Optionally update the incremental push tags.
+ *
+ * @param commands
+ */
+ protected void updateIncrementalPushTags(Collection<ReceiveCommand> commands) {
+ if (!repository.useIncrementalPushTags) {
+ return;
}
- // update push log
- try {
- RefLogUtils.updateRefLog(user, rp.getRepository(), commands);
- LOGGER.debug(MessageFormat.format("{0} push log updated", repository.name));
- } catch (Exception e) {
- LOGGER.error(MessageFormat.format("Failed to update {0} pushlog", repository.name), e);
- }
+ // tag each pushed branch tip
+ String emailAddress = user.emailAddress == null ? getRefLogIdent().getEmailAddress() : user.emailAddress;
+ PersonIdent userIdent = new PersonIdent(user.getDisplayName(), emailAddress);
- // check for updates pushed to the BranchTicketService branch
- // if the BranchTicketService is active it will reindex, as appropriate
for (ReceiveCommand cmd : commands) {
- if (Result.OK.equals(cmd.getResult())
- && BranchTicketService.BRANCH.equals(cmd.getRefName())) {
- rp.getRepository().fireEvent(new ReceiveCommandEvent(repository, cmd));
+ if (!cmd.getRefName().startsWith(Constants.R_HEADS)) {
+ // only tag branch ref changes
+ continue;
}
- }
- // call post-receive plugins
- for (ReceiveHook hook : gitblit.getExtensions(ReceiveHook.class)) {
- try {
- hook.onPostReceive(this, commands);
- } catch (Exception e) {
- LOGGER.error("Failed to execute extension", e);
+ if (!ReceiveCommand.Type.DELETE.equals(cmd.getType())
+ && ReceiveCommand.Result.OK.equals(cmd.getResult())) {
+ String objectId = cmd.getNewId().getName();
+ String branch = cmd.getRefName().substring(Constants.R_HEADS.length());
+ // get translation based on the server's locale setting
+ String template = Translation.get("gb.incrementalPushTagMessage");
+ String msg = MessageFormat.format(template, branch);
+ String prefix;
+ if (StringUtils.isEmpty(repository.incrementalPushTagPrefix)) {
+ prefix = settings.getString(Keys.git.defaultIncrementalPushTagPrefix, "r");
+ } else {
+ prefix = repository.incrementalPushTagPrefix;
+ }
+
+ JGitUtils.createIncrementalRevisionTag(
+ getRepository(),
+ objectId,
+ userIdent,
+ prefix,
+ "0",
+ msg);
}
}
+ }
- // run Groovy hook scripts
- Set<String> scripts = new LinkedHashSet<String>();
- scripts.addAll(gitblit.getPostReceiveScriptsInherited(repository));
- if (!ArrayUtils.isEmpty(repository.postReceiveScripts)) {
- scripts.addAll(repository.postReceiveScripts);
+ /**
+ * Update Gitblit's internal reflog.
+ *
+ * @param commands
+ */
+ protected void updateGitblitRefLog(Collection<ReceiveCommand> commands) {
+ try {
+ RefLogUtils.updateRefLog(user, getRepository(), commands);
+ LOGGER.debug(MessageFormat.format("{0} reflog updated", repository.name));
+ } catch (Exception e) {
+ LOGGER.error(MessageFormat.format("Failed to update {0} reflog", repository.name), e);
}
- runGroovy(commands, scripts);
}
/** Execute commands to update references. */
diff --git a/src/main/java/com/gitblit/git/PatchsetReceivePack.java b/src/main/java/com/gitblit/git/PatchsetReceivePack.java index f7412a34..9e55524d 100644 --- a/src/main/java/com/gitblit/git/PatchsetReceivePack.java +++ b/src/main/java/com/gitblit/git/PatchsetReceivePack.java @@ -1202,11 +1202,15 @@ public class PatchsetReceivePack extends GitblitReceivePack { if (ticket != null) {
ticketNotifier.queueMailing(ticket);
- // update the reflog with the merge
if (oldRef != null) {
ReceiveCommand cmd = new ReceiveCommand(oldRef.getObjectId(),
ObjectId.fromString(mergeResult.sha), oldRef.getName());
- RefLogUtils.updateRefLog(user, getRepository(), Arrays.asList(cmd));
+ cmd.setResult(Result.OK);
+ List<ReceiveCommand> commands = Arrays.asList(cmd);
+
+ logRefChange(commands);
+ updateIncrementalPushTags(commands);
+ updateGitblitRefLog(commands);
}
// call patchset hooks
diff --git a/src/main/java/com/gitblit/manager/GitblitManager.java b/src/main/java/com/gitblit/manager/GitblitManager.java index 2782e724..98ad33e7 100644 --- a/src/main/java/com/gitblit/manager/GitblitManager.java +++ b/src/main/java/com/gitblit/manager/GitblitManager.java @@ -602,6 +602,21 @@ public class GitblitManager implements IGitblit { } @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(); } @@ -646,6 +661,11 @@ public class GitblitManager implements IGitblit { */ @Override + public boolean isSendingMail() { + return notificationManager.isSendingMail(); + } + + @Override public void sendMailToAdministrators(String subject, String message) { notificationManager.sendMailToAdministrators(subject, message); } @@ -1015,6 +1035,11 @@ public class GitblitManager implements IGitblit { } @Override + public boolean canDelete(RepositoryModel model) { + return repositoryManager.canDelete(model); + } + + @Override public boolean deleteRepositoryModel(RepositoryModel model) { return repositoryManager.deleteRepositoryModel(model); } diff --git a/src/main/java/com/gitblit/manager/INotificationManager.java b/src/main/java/com/gitblit/manager/INotificationManager.java index 231cf43a..64fc01e8 100644 --- a/src/main/java/com/gitblit/manager/INotificationManager.java +++ b/src/main/java/com/gitblit/manager/INotificationManager.java @@ -22,6 +22,14 @@ import com.gitblit.models.Mailing; public interface INotificationManager extends IManager { /** + * Returns true if the email service is configured and ready to send notifications. + * + * @return true if the email service is operational + * @since 1.6.0 + */ + boolean isSendingMail(); + + /** * Notify the administrators by email. * * @param subject diff --git a/src/main/java/com/gitblit/manager/IRepositoryManager.java b/src/main/java/com/gitblit/manager/IRepositoryManager.java index 69177f1e..f5cf03fa 100644 --- a/src/main/java/com/gitblit/manager/IRepositoryManager.java +++ b/src/main/java/com/gitblit/manager/IRepositoryManager.java @@ -339,6 +339,15 @@ public interface IRepositoryManager extends IManager { void updateConfiguration(Repository r, RepositoryModel repository); /** + * Returns true if the repository can be deleted. + * + * @param model + * @return true if the repository can be deleted + * @since 1.6.0 + */ + boolean canDelete(RepositoryModel model); + + /** * Deletes the repository from the file system and removes the repository * permission from all repository users. * diff --git a/src/main/java/com/gitblit/manager/IRuntimeManager.java b/src/main/java/com/gitblit/manager/IRuntimeManager.java index 29e7368f..b2d7a2b3 100644 --- a/src/main/java/com/gitblit/manager/IRuntimeManager.java +++ b/src/main/java/com/gitblit/manager/IRuntimeManager.java @@ -57,6 +57,33 @@ public interface IRuntimeManager extends IManager { 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/NotificationManager.java b/src/main/java/com/gitblit/manager/NotificationManager.java index ba63cfc1..69a611bb 100644 --- a/src/main/java/com/gitblit/manager/NotificationManager.java +++ b/src/main/java/com/gitblit/manager/NotificationManager.java @@ -71,6 +71,11 @@ public class NotificationManager implements INotificationManager { return this; } + @Override + public boolean isSendingMail() { + return mailService.isReady(); + } + /** * Notify the administrators by email. * diff --git a/src/main/java/com/gitblit/manager/RepositoryManager.java b/src/main/java/com/gitblit/manager/RepositoryManager.java index 71bce869..e0721c7c 100644 --- a/src/main/java/com/gitblit/manager/RepositoryManager.java +++ b/src/main/java/com/gitblit/manager/RepositoryManager.java @@ -66,6 +66,7 @@ import com.gitblit.Constants.RegistrantType; import com.gitblit.GitBlitException; import com.gitblit.IStoredSettings; import com.gitblit.Keys; +import com.gitblit.extensions.RepositoryLifeCycleListener; import com.gitblit.models.ForkModel; import com.gitblit.models.Metric; import com.gitblit.models.RefModel; @@ -114,6 +115,8 @@ public class RepositoryManager implements IRepositoryManager { private final IRuntimeManager runtimeManager; + private final IPluginManager pluginManager; + private final IUserManager userManager; private final File repositoriesFolder; @@ -126,10 +129,12 @@ public class RepositoryManager implements IRepositoryManager { public RepositoryManager( IRuntimeManager runtimeManager, + IPluginManager pluginManager, IUserManager userManager) { this.settings = runtimeManager.getSettings(); this.runtimeManager = runtimeManager; + this.pluginManager = pluginManager; this.userManager = userManager; this.repositoriesFolder = runtimeManager.getFileOrFolder(Keys.git.repositoriesFolder, "${baseFolder}/git"); } @@ -1420,6 +1425,16 @@ public class RepositoryManager implements IRepositoryManager { removeFromCachedRepositoryList(repositoryName); // model will actually be replaced on next load because config is stale addToCachedRepositoryList(repository); + + if (isCreate && pluginManager != null) { + for (RepositoryLifeCycleListener listener : pluginManager.getExtensions(RepositoryLifeCycleListener.class)) { + try { + listener.onCreation(repository); + } catch (Throwable t) { + logger.error(String.format("failed to call plugin onCreation %s", repositoryName), t); + } + } + } } /** @@ -1536,6 +1551,17 @@ public class RepositoryManager implements IRepositoryManager { } /** + * Returns true if the repository can be deleted. + * + * @return true if the repository can be deleted + */ + @Override + public boolean canDelete(RepositoryModel repository) { + return settings.getBoolean(Keys.web.allowDeletingNonEmptyRepositories, true) + || !repository.hasCommits; + } + + /** * Deletes the repository from the file system and removes the repository * permission from all repository users. * @@ -1556,6 +1582,12 @@ public class RepositoryManager implements IRepositoryManager { */ @Override public boolean deleteRepository(String repositoryName) { + RepositoryModel repository = getRepositoryModel(repositoryName); + if (!canDelete(repository)) { + logger.warn("Attempt to delete {} rejected!", repositoryName); + return false; + } + try { close(repositoryName); // clear the repository cache @@ -1571,6 +1603,16 @@ public class RepositoryManager implements IRepositoryManager { FileUtils.delete(folder, FileUtils.RECURSIVE | FileUtils.RETRY); if (userManager.deleteRepositoryRole(repositoryName)) { logger.info(MessageFormat.format("Repository \"{0}\" deleted", repositoryName)); + + if (pluginManager != null) { + for (RepositoryLifeCycleListener listener : pluginManager.getExtensions(RepositoryLifeCycleListener.class)) { + try { + listener.onDeletion(repository); + } catch (Throwable t) { + logger.error(String.format("failed to call plugin onDeletion %s", repositoryName), t); + } + } + } return true; } } diff --git a/src/main/java/com/gitblit/manager/RuntimeManager.java b/src/main/java/com/gitblit/manager/RuntimeManager.java index 52f4d67b..9cdc64eb 100644 --- a/src/main/java/com/gitblit/manager/RuntimeManager.java +++ b/src/main/java/com/gitblit/manager/RuntimeManager.java @@ -119,9 +119,42 @@ public class RuntimeManager implements IRuntimeManager { */ @Override public boolean isServingRepositories() { - return settings.getBoolean(Keys.git.enableGitServlet, true) - || (settings.getInteger(Keys.git.daemonPort, 0) > 0) - || (settings.getInteger(Keys.git.sshPort, 0) > 0); + 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; } /** diff --git a/src/main/java/com/gitblit/manager/ServicesManager.java b/src/main/java/com/gitblit/manager/ServicesManager.java index b1c97ba4..37215786 100644 --- a/src/main/java/com/gitblit/manager/ServicesManager.java +++ b/src/main/java/com/gitblit/manager/ServicesManager.java @@ -80,8 +80,9 @@ public class ServicesManager implements IManager { public ServicesManager(IGitblit gitblit) { this.settings = gitblit.getSettings(); this.gitblit = gitblit; + int defaultThreadPoolSize = settings.getInteger(Keys.execution.defaultThreadPoolSize, 1); this.idGenerator = new IdGenerator(); - this.workQueue = new WorkQueue(idGenerator, 1); + this.workQueue = new WorkQueue(idGenerator, defaultThreadPoolSize); } @Override @@ -111,9 +112,21 @@ public class ServicesManager implements IManager { } public boolean isServingRepositories() { - return settings.getBoolean(Keys.git.enableGitServlet, true) - || (gitDaemon != null && gitDaemon.isRunning()) - || (sshDaemon != null && sshDaemon.isRunning()); + return isServingHTTP() + || isServingGIT() + || isServingSSH(); + } + + public boolean isServingHTTP() { + return settings.getBoolean(Keys.git.enableGitServlet, true); + } + + public boolean isServingGIT() { + return gitDaemon != null && gitDaemon.isRunning(); + } + + public boolean isServingSSH() { + return sshDaemon != null && sshDaemon.isRunning(); } protected void configureFederation() { diff --git a/src/main/java/com/gitblit/manager/UserManager.java b/src/main/java/com/gitblit/manager/UserManager.java index 67b1d68f..2b82ffb6 100644 --- a/src/main/java/com/gitblit/manager/UserManager.java +++ b/src/main/java/com/gitblit/manager/UserManager.java @@ -32,6 +32,7 @@ import com.gitblit.Constants; import com.gitblit.IStoredSettings; import com.gitblit.IUserService; import com.gitblit.Keys; +import com.gitblit.extensions.UserTeamLifeCycleListener; import com.gitblit.models.TeamModel; import com.gitblit.models.UserModel; import com.gitblit.utils.StringUtils; @@ -50,13 +51,16 @@ public class UserManager implements IUserManager { private final IRuntimeManager runtimeManager; + private final IPluginManager pluginManager; + private final Map<String, String> legacyBackingServices; private IUserService userService; - public UserManager(IRuntimeManager runtimeManager) { + public UserManager(IRuntimeManager runtimeManager, IPluginManager pluginManager) { this.settings = runtimeManager.getSettings(); this.runtimeManager = runtimeManager; + this.pluginManager = pluginManager; // map of legacy realm backing user services legacyBackingServices = new HashMap<String, String>(); @@ -209,7 +213,14 @@ public class UserManager implements IUserManager { */ @Override public boolean updateUserModel(UserModel model) { - return userService.updateUserModel(model); + final boolean isCreate = null == userService.getUserModel(model.username); + if (userService.updateUserModel(model)) { + if (isCreate) { + callCreateUserListeners(model); + } + return true; + } + return false; } /** @@ -236,7 +247,14 @@ public class UserManager implements IUserManager { */ @Override public boolean updateUserModel(String username, UserModel model) { - return userService.updateUserModel(username, model); + final boolean isCreate = null == userService.getUserModel(username); + if (userService.updateUserModel(username, model)) { + if (isCreate) { + callCreateUserListeners(model); + } + return true; + } + return false; } /** @@ -247,7 +265,11 @@ public class UserManager implements IUserManager { */ @Override public boolean deleteUserModel(UserModel model) { - return userService.deleteUserModel(model); + if (userService.deleteUserModel(model)) { + callDeleteUserListeners(model); + return true; + } + return false; } /** @@ -262,7 +284,12 @@ public class UserManager implements IUserManager { return false; } String usernameDecoded = StringUtils.decodeUsername(username); - return userService.deleteUser(usernameDecoded); + UserModel user = getUserModel(usernameDecoded); + if (userService.deleteUser(usernameDecoded)) { + callDeleteUserListeners(user); + return true; + } + return false; } /** @@ -349,7 +376,14 @@ public class UserManager implements IUserManager { */ @Override public boolean updateTeamModel(TeamModel model) { - return userService.updateTeamModel(model); + final boolean isCreate = null == userService.getTeamModel(model.name); + if (userService.updateTeamModel(model)) { + if (isCreate) { + callCreateTeamListeners(model); + } + return true; + } + return false; } /** @@ -377,7 +411,14 @@ public class UserManager implements IUserManager { */ @Override public boolean updateTeamModel(String teamname, TeamModel model) { - return userService.updateTeamModel(teamname, model); + final boolean isCreate = null == userService.getTeamModel(teamname); + if (userService.updateTeamModel(teamname, model)) { + if (isCreate) { + callCreateTeamListeners(model); + } + return true; + } + return false; } /** @@ -389,7 +430,11 @@ public class UserManager implements IUserManager { */ @Override public boolean deleteTeamModel(TeamModel model) { - return userService.deleteTeamModel(model); + if (userService.deleteTeamModel(model)) { + callDeleteTeamListeners(model); + return true; + } + return false; } /** @@ -401,7 +446,12 @@ public class UserManager implements IUserManager { */ @Override public boolean deleteTeam(String teamname) { - return userService.deleteTeam(teamname); + TeamModel team = userService.getTeamModel(teamname); + if (userService.deleteTeam(teamname)) { + callDeleteTeamListeners(team); + return true; + } + return false; } /** @@ -440,4 +490,60 @@ public class UserManager implements IUserManager { public boolean deleteRepositoryRole(String role) { return userService.deleteRepositoryRole(role); } + + protected void callCreateUserListeners(UserModel user) { + if (pluginManager == null || user == null) { + return; + } + + for (UserTeamLifeCycleListener listener : pluginManager.getExtensions(UserTeamLifeCycleListener.class)) { + try { + listener.onCreation(user); + } catch (Throwable t) { + logger.error(String.format("failed to call plugin.onCreation%s", user.username), t); + } + } + } + + protected void callCreateTeamListeners(TeamModel team) { + if (pluginManager == null || team == null) { + return; + } + + for (UserTeamLifeCycleListener listener : pluginManager.getExtensions(UserTeamLifeCycleListener.class)) { + try { + listener.onCreation(team); + } catch (Throwable t) { + logger.error(String.format("failed to call plugin.onCreation %s", team.name), t); + } + } + } + + protected void callDeleteUserListeners(UserModel user) { + if (pluginManager == null || user == null) { + return; + } + + for (UserTeamLifeCycleListener listener : pluginManager.getExtensions(UserTeamLifeCycleListener.class)) { + try { + listener.onDeletion(user); + } catch (Throwable t) { + logger.error(String.format("failed to call plugin.onDeletion %s", user.username), t); + } + } + } + + protected void callDeleteTeamListeners(TeamModel team) { + if (pluginManager == null || team == null) { + return; + } + + for (UserTeamLifeCycleListener listener : pluginManager.getExtensions(UserTeamLifeCycleListener.class)) { + try { + listener.onDeletion(team); + } catch (Throwable t) { + logger.error(String.format("failed to call plugin.onDeletion %s", team.name), t); + } + } + } } diff --git a/src/main/java/com/gitblit/models/Menu.java b/src/main/java/com/gitblit/models/Menu.java new file mode 100644 index 00000000..7c949b3f --- /dev/null +++ b/src/main/java/com/gitblit/models/Menu.java @@ -0,0 +1,302 @@ +package com.gitblit.models; + +import java.io.Serializable; + +import org.apache.wicket.PageParameters; +import org.apache.wicket.markup.html.WebPage; + +import com.gitblit.utils.StringUtils; + +public class Menu { + + /** + * A MenuItem for a drop down menu. + * + * @author James Moger + * @since 1.6.0 + */ + public abstract static class MenuItem implements Serializable { + + private static final long serialVersionUID = 1L; + + final String displayText; + + MenuItem(String displayText) { + this.displayText = displayText; + } + + @Override + public int hashCode() { + return displayText.hashCode(); + } + + @Override + public boolean equals(Object o) { + if (o instanceof MenuItem) { + return hashCode() == o.hashCode(); + } + return false; + } + + @Override + public String toString() { + return displayText; + } + } + + /** + * A divider for the menu. + * + * @since 1.6.0 + */ + public static class MenuDivider extends MenuItem { + + private static final long serialVersionUID = 1L; + + public MenuDivider() { + super(""); + } + } + + + /** + * A MenuItem for setting a parameter of the current url. + * + * @author James Moger + * + */ + public static class ParameterMenuItem extends MenuItem { + + private static final long serialVersionUID = 1L; + + final PageParameters parameters; + final String parameter; + final String value; + final boolean isSelected; + + /** + * @param displayText + */ + public ParameterMenuItem(String displayText) { + this(displayText, null, null, null); + } + + /** + * @param displayText + * @param parameter + * @param value + */ + public ParameterMenuItem(String displayText, String parameter, String value) { + this(displayText, parameter, value, null); + } + + /** + * @param displayText + * @param parameter + * @param value + */ + public ParameterMenuItem(String displayText, String parameter, String value, + PageParameters params) { + super(displayText); + this.parameter = parameter; + this.value = value; + + if (params == null) { + // no parameters specified + parameters = new PageParameters(); + setParameter(parameter, value); + isSelected = false; + } else { + parameters = new PageParameters(params); + if (parameters.containsKey(parameter)) { + isSelected = params.getString(parameter).equals(value); + // set the new selection value + setParameter(parameter, value); + } else { + // not currently selected + isSelected = false; + setParameter(parameter, value); + } + } + } + + protected void setParameter(String parameter, String value) { + if (!StringUtils.isEmpty(parameter)) { + if (StringUtils.isEmpty(value)) { + this.parameters.remove(parameter); + } else { + this.parameters.put(parameter, value); + } + } + } + + public String formatParameter() { + if (StringUtils.isEmpty(parameter) || StringUtils.isEmpty(value)) { + return ""; + } + return parameter + "=" + value; + } + + public PageParameters getPageParameters() { + return parameters; + } + + public boolean isSelected() { + return isSelected; + } + + @Override + public int hashCode() { + if (StringUtils.isEmpty(displayText)) { + return value.hashCode() + parameter.hashCode(); + } + return displayText.hashCode(); + } + + @Override + public boolean equals(Object o) { + if (o instanceof MenuItem) { + return hashCode() == o.hashCode(); + } + return false; + } + + @Override + public String toString() { + if (StringUtils.isEmpty(displayText)) { + return formatParameter(); + } + return displayText; + } + } + + /** + * Menu item for toggling a parameter. + * + */ + public static class ToggleMenuItem extends ParameterMenuItem { + + private static final long serialVersionUID = 1L; + + /** + * @param displayText + * @param parameter + * @param value + */ + public ToggleMenuItem(String displayText, String parameter, String value, + PageParameters params) { + super(displayText, parameter, value, params); + if (isSelected) { + // already selected, so remove this enables toggling + parameters.remove(parameter); + } + } + } + + /** + * Menu item for linking to another Wicket page. + * + * @since 1.6.0 + */ + public static class PageLinkMenuItem extends MenuItem { + + private static final long serialVersionUID = 1L; + + private final Class<? extends WebPage> pageClass; + + private final PageParameters params; + + /** + * Page Link Item links to another page. + * + * @param displayText + * @param pageClass + * @since 1.6.0 + */ + public PageLinkMenuItem(String displayText, Class<? extends WebPage> pageClass) { + this(displayText, pageClass, null); + } + + /** + * Page Link Item links to another page. + * + * @param displayText + * @param pageClass + * @param params + * @since 1.6.0 + */ + public PageLinkMenuItem(String displayText, Class<? extends WebPage> pageClass, PageParameters params) { + super(displayText); + this.pageClass = pageClass; + this.params = params; + } + + /** + * @return the page class + * @since 1.6.0 + */ + public Class<? extends WebPage> getPageClass() { + return pageClass; + } + + /** + * @return the page parameters + * @since 1.6.0 + */ + public PageParameters getPageParameters() { + return params; + } + } + + /** + * Menu item to link to an external page. + * + * @since 1.6.0 + */ + public static class ExternalLinkMenuItem extends MenuItem { + + private static final long serialVersionUID = 1L; + + private final String href; + + private final boolean newWindow; + + /** + * External Link Item links to something else. + * + * @param displayText + * @param href + * @since 1.6.0 + */ + public ExternalLinkMenuItem(String displayText, String href) { + this(displayText, href, false); + } + + /** + * External Link Item links to something else. + * + * @param displayText + * @param href + * @since 1.6.0 + */ + public ExternalLinkMenuItem(String displayText, String href, boolean newWindow) { + super(displayText); + this.href = href; + this.newWindow = newWindow; + } + + /** + * @since 1.6.0 + */ + public String getHref() { + return href; + } + + /** + * @since 1.6.0 + */ + public boolean openInNewWindow() { + return newWindow; + } + } +} diff --git a/src/main/java/com/gitblit/models/NavLink.java b/src/main/java/com/gitblit/models/NavLink.java new file mode 100644 index 00000000..993d6954 --- /dev/null +++ b/src/main/java/com/gitblit/models/NavLink.java @@ -0,0 +1,140 @@ +/*
+ * 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.models;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.wicket.PageParameters;
+import org.apache.wicket.markup.html.WebPage;
+
+import com.gitblit.models.Menu.MenuItem;
+
+/**
+ * Represents a navigation link for the navigation panel.
+ *
+ * @author James Moger
+ *
+ */
+public abstract class NavLink implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ public final String translationKey;
+ public final boolean hiddenPhone;
+
+ public NavLink(String translationKey, boolean hiddenPhone) {
+ this.translationKey = translationKey;
+ this.hiddenPhone = hiddenPhone;
+ }
+
+
+ /**
+ * Represents a Wicket page link.
+ *
+ * @author James Moger
+ *
+ */
+ public static class PageNavLink extends NavLink implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ public final Class<? extends WebPage> pageClass;
+ public final PageParameters params;
+
+ public PageNavLink(String translationKey, Class<? extends WebPage> pageClass) {
+ this(translationKey, pageClass, null);
+ }
+
+ public PageNavLink(String translationKey, Class<? extends WebPage> pageClass,
+ PageParameters params) {
+ this(translationKey, pageClass, params, false);
+ }
+
+ public PageNavLink(String translationKey, Class<? extends WebPage> pageClass,
+ PageParameters params, boolean hiddenPhone) {
+ super(translationKey, hiddenPhone);
+ this.pageClass = pageClass;
+ this.params = params;
+ }
+ }
+
+ /**
+ * Represents an explicitly href link.
+ *
+ * @author James Moger
+ *
+ */
+ public static class ExternalNavLink extends NavLink implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ public final String url;
+
+ public ExternalNavLink(String keyOrText, String url) {
+ super(keyOrText, false);
+ this.url = url;
+ }
+
+ public ExternalNavLink(String keyOrText, String url, boolean hiddenPhone) {
+ super(keyOrText, hiddenPhone);
+ this.url = url;
+ }
+ }
+
+ /**
+ * Represents a DropDownMenu for the current page.
+ *
+ * @author James Moger
+ *
+ */
+ public static class DropDownPageMenuNavLink extends PageNavLink implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ public final List<MenuItem> menuItems;
+
+ public DropDownPageMenuNavLink(String keyOrText, Class<? extends WebPage> pageClass) {
+ this(keyOrText, pageClass, false);
+ }
+
+ public DropDownPageMenuNavLink(String keyOrText, Class<? extends WebPage> pageClass, boolean hiddenPhone) {
+ super(keyOrText, pageClass, null, hiddenPhone);
+ menuItems = new ArrayList<MenuItem>();
+ }
+ }
+
+ /**
+ * Represents a DropDownMenu.
+ *
+ * @author James Moger
+ *
+ */
+ public static class DropDownMenuNavLink extends NavLink implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ public final List<MenuItem> menuItems;
+
+ public DropDownMenuNavLink(String keyOrText) {
+ this(keyOrText, false);
+ }
+
+ public DropDownMenuNavLink(String keyOrText, boolean hiddenPhone) {
+ super(keyOrText, hiddenPhone);
+ menuItems = new ArrayList<MenuItem>();
+ }
+ }
+}
\ No newline at end of file diff --git a/src/main/java/com/gitblit/models/UserPreferences.java b/src/main/java/com/gitblit/models/UserPreferences.java index 44e44933..90c034fc 100644 --- a/src/main/java/com/gitblit/models/UserPreferences.java +++ b/src/main/java/com/gitblit/models/UserPreferences.java @@ -23,6 +23,7 @@ import java.util.Locale; import java.util.Map; import java.util.TreeMap; +import com.gitblit.Constants.Transport; import com.gitblit.utils.StringUtils; /** @@ -37,7 +38,11 @@ public class UserPreferences implements Serializable { public final String username; - public String locale; + private String locale; + + private Boolean emailMeOnMyTicketChanges; + + private Transport transport; private final Map<String, UserRepositoryPreferences> repositoryPreferences = new TreeMap<String, UserRepositoryPreferences>(); @@ -58,6 +63,10 @@ public class UserPreferences implements Serializable { return new Locale(locale); } + public void setLocale(String locale) { + this.locale = locale; + } + public UserRepositoryPreferences getRepositoryPreferences(String repositoryName) { String key = repositoryName.toLowerCase(); if (!repositoryPreferences.containsKey(key)) { @@ -96,4 +105,23 @@ public class UserPreferences implements Serializable { Collections.sort(list); return list; } + + public boolean isEmailMeOnMyTicketChanges() { + if (emailMeOnMyTicketChanges == null) { + return true; + } + return emailMeOnMyTicketChanges; + } + + public void setEmailMeOnMyTicketChanges(boolean value) { + this.emailMeOnMyTicketChanges = value; + } + + public Transport getTransport() { + return transport; + } + + public void setTransport(Transport transport) { + this.transport = transport; + } } diff --git a/src/main/java/com/gitblit/service/MailService.java b/src/main/java/com/gitblit/service/MailService.java index 4759d177..ae9727fc 100644 --- a/src/main/java/com/gitblit/service/MailService.java +++ b/src/main/java/com/gitblit/service/MailService.java @@ -68,6 +68,7 @@ public class MailService implements Runnable { final String mailUser = settings.getString(Keys.mail.username, null);
final String mailPassword = settings.getString(Keys.mail.password, null);
final boolean smtps = settings.getBoolean(Keys.mail.smtps, false);
+ final boolean starttls = settings.getBoolean(Keys.mail.starttls, false);
boolean authenticate = !StringUtils.isEmpty(mailUser) && !StringUtils.isEmpty(mailPassword);
String server = settings.getString(Keys.mail.server, "");
if (StringUtils.isEmpty(server)) {
@@ -86,6 +87,7 @@ public class MailService implements Runnable { props.setProperty("mail.smtp.port", String.valueOf(port));
props.setProperty("mail.smtp.auth", String.valueOf(authenticate));
props.setProperty("mail.smtp.auths", String.valueOf(authenticate));
+ props.setProperty("mail.smtp.starttls.enable", String.valueOf(starttls));
if (isGMail || smtps) {
props.setProperty("mail.smtp.starttls.enable", "true");
diff --git a/src/main/java/com/gitblit/servlet/AccessRestrictionFilter.java b/src/main/java/com/gitblit/servlet/AccessRestrictionFilter.java index e6a0169c..0e6d323d 100644 --- a/src/main/java/com/gitblit/servlet/AccessRestrictionFilter.java +++ b/src/main/java/com/gitblit/servlet/AccessRestrictionFilter.java @@ -19,6 +19,7 @@ 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;
@@ -54,8 +55,8 @@ public abstract class AccessRestrictionFilter extends AuthenticationFilter { protected IRepositoryManager repositoryManager;
@Override
- protected void inject(ObjectGraph dagger) {
- super.inject(dagger);
+ protected void inject(ObjectGraph dagger, FilterConfig filterConfig) {
+ super.inject(dagger, filterConfig);
this.runtimeManager = dagger.get(IRuntimeManager.class);
this.repositoryManager = dagger.get(IRepositoryManager.class);
}
diff --git a/src/main/java/com/gitblit/servlet/AuthenticationFilter.java b/src/main/java/com/gitblit/servlet/AuthenticationFilter.java index ddb85f49..c21f8692 100644 --- a/src/main/java/com/gitblit/servlet/AuthenticationFilter.java +++ b/src/main/java/com/gitblit/servlet/AuthenticationFilter.java @@ -22,6 +22,7 @@ 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;
@@ -62,7 +63,7 @@ public abstract class AuthenticationFilter extends DaggerFilter { protected IAuthenticationManager authenticationManager;
@Override
- protected void inject(ObjectGraph dagger) {
+ protected void inject(ObjectGraph dagger, FilterConfig filterConfig) {
this.authenticationManager = dagger.get(IAuthenticationManager.class);
}
diff --git a/src/main/java/com/gitblit/servlet/EnforceAuthenticationFilter.java b/src/main/java/com/gitblit/servlet/EnforceAuthenticationFilter.java index 5fdccb71..c015021d 100644 --- a/src/main/java/com/gitblit/servlet/EnforceAuthenticationFilter.java +++ b/src/main/java/com/gitblit/servlet/EnforceAuthenticationFilter.java @@ -19,6 +19,7 @@ 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; @@ -53,7 +54,7 @@ public class EnforceAuthenticationFilter extends DaggerFilter { private IAuthenticationManager authenticationManager; @Override - protected void inject(ObjectGraph dagger) { + protected void inject(ObjectGraph dagger, FilterConfig filterConfig) { this.settings = dagger.get(IStoredSettings.class); this.authenticationManager = dagger.get(IAuthenticationManager.class); } diff --git a/src/main/java/com/gitblit/servlet/FilterRuntimeConfig.java b/src/main/java/com/gitblit/servlet/FilterRuntimeConfig.java new file mode 100644 index 00000000..9f0c0ac5 --- /dev/null +++ b/src/main/java/com/gitblit/servlet/FilterRuntimeConfig.java @@ -0,0 +1,71 @@ +/* + * 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.servlet; + +import java.util.Enumeration; + +import javax.servlet.FilterConfig; +import javax.servlet.ServletContext; + +import com.gitblit.IStoredSettings; +import com.gitblit.manager.IRuntimeManager; + +/** + * Wraps a filter config and will prefer a setting retrieved from IStoredSettings + * if one is available. + * + * @author James Moger + * @since 1.6.0 + */ +public class FilterRuntimeConfig implements FilterConfig { + + final IRuntimeManager runtime; + final IStoredSettings settings; + final String namespace; + final FilterConfig config; + + public FilterRuntimeConfig(IRuntimeManager runtime, String namespace, FilterConfig config) { + this.runtime = runtime; + this.settings = runtime.getSettings(); + this.namespace = namespace; + this.config = config; + } + + @Override + public String getFilterName() { + return config.getFilterName(); + } + + @Override + public ServletContext getServletContext() { + return config.getServletContext(); + } + + @Override + public String getInitParameter(String name) { + String key = namespace + "." + name; + if (settings.hasSettings(key)) { + String value = settings.getString(key, null); + return value; + } + return config.getInitParameter(name); + } + + @Override + public Enumeration<String> getInitParameterNames() { + return config.getInitParameterNames(); + } +} diff --git a/src/main/java/com/gitblit/servlet/GitFilter.java b/src/main/java/com/gitblit/servlet/GitFilter.java index f9c062d2..bb3d3216 100644 --- a/src/main/java/com/gitblit/servlet/GitFilter.java +++ b/src/main/java/com/gitblit/servlet/GitFilter.java @@ -17,6 +17,7 @@ package com.gitblit.servlet; import java.text.MessageFormat;
+import javax.servlet.FilterConfig;
import javax.servlet.http.HttpServletRequest;
import com.gitblit.Constants.AccessRestrictionType;
@@ -53,8 +54,8 @@ public class GitFilter extends AccessRestrictionFilter { private IFederationManager federationManager;
@Override
- protected void inject(ObjectGraph dagger) {
- super.inject(dagger);
+ protected void inject(ObjectGraph dagger, FilterConfig filterConfig) {
+ super.inject(dagger, filterConfig);
this.settings = dagger.get(IStoredSettings.class);
this.federationManager = dagger.get(IFederationManager.class);
}
diff --git a/src/main/java/com/gitblit/servlet/GitblitContext.java b/src/main/java/com/gitblit/servlet/GitblitContext.java index ebe2cc2b..d5b4092c 100644 --- a/src/main/java/com/gitblit/servlet/GitblitContext.java +++ b/src/main/java/com/gitblit/servlet/GitblitContext.java @@ -38,6 +38,7 @@ 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.manager.IAuthenticationManager; import com.gitblit.manager.IFederationManager; import com.gitblit.manager.IGitblit; @@ -174,6 +175,9 @@ public class GitblitContext extends DaggerContext { runtime.start(); managers.add(runtime); + // create the plugin manager instance but do not start it + loadManager(injector, IPluginManager.class); + // start all other managers startManager(injector, INotificationManager.class); startManager(injector, IUserManager.class); @@ -191,6 +195,15 @@ public class GitblitContext extends DaggerContext { logger.info(""); logger.info("All managers started."); logger.info(""); + + IPluginManager pluginManager = injector.get(IPluginManager.class); + for (LifeCycleListener listener : pluginManager.getExtensions(LifeCycleListener.class)) { + try { + listener.onStartup(); + } catch (Throwable t) { + logger.error(null, t); + } + } } private String lookupBaseFolderFromJndi() { @@ -205,9 +218,14 @@ public class GitblitContext extends DaggerContext { return null; } + protected <X extends IManager> X loadManager(ObjectGraph injector, Class<X> clazz) { + X x = injector.get(clazz); + return x; + } + protected <X extends IManager> X startManager(ObjectGraph injector, Class<X> clazz) { + X x = loadManager(injector, clazz); logManager(clazz); - X x = injector.get(clazz); x.start(); managers.add(x); return x; @@ -225,6 +243,16 @@ public class GitblitContext extends DaggerContext { @Override protected void destroyContext(ServletContext context) { logger.info("Gitblit context destroyed by servlet container."); + + IPluginManager pluginManager = getManager(IPluginManager.class); + for (LifeCycleListener listener : pluginManager.getExtensions(LifeCycleListener.class)) { + try { + listener.onShutdown(); + } catch (Throwable t) { + logger.error(null, t); + } + } + for (IManager manager : managers) { logger.debug("stopping {}", manager.getClass().getSimpleName()); manager.stop(); @@ -352,6 +380,22 @@ public class GitblitContext extends DaggerContext { } } + // Copy the included gitignore files to the configured gitignore folder + String gitignorePath = webxmlSettings.getString(Keys.git.gitignoreFolder, "gitignore"); + File localGitignores = com.gitblit.utils.FileUtils.resolveParameter(Constants.baseFolder$, base, gitignorePath); + if (!localGitignores.exists()) { + File warGitignores = new File(contextFolder, "/WEB-INF/data/gitignore"); + if (!warGitignores.equals(localGitignores)) { + try { + com.gitblit.utils.FileUtils.copy(localGitignores, warGitignores.listFiles()); + } catch (IOException e) { + logger.error(MessageFormat.format( + "Failed to copy included .gitignore files from {0} to {1}", + warGitignores, localGitignores)); + } + } + } + // merge the WebXmlSettings into the runtime settings (for backwards-compatibilty) runtimeSettings.merge(webxmlSettings); diff --git a/src/main/java/com/gitblit/servlet/PagesFilter.java b/src/main/java/com/gitblit/servlet/PagesFilter.java index 9e009163..e07d9b3b 100644 --- a/src/main/java/com/gitblit/servlet/PagesFilter.java +++ b/src/main/java/com/gitblit/servlet/PagesFilter.java @@ -15,11 +15,6 @@ */
package com.gitblit.servlet;
-import org.eclipse.jgit.lib.Repository;
-
-import com.gitblit.Constants.AccessRestrictionType;
-import com.gitblit.models.RepositoryModel;
-import com.gitblit.models.UserModel;
/**
* The PagesFilter is an AccessRestrictionFilter which ensures the gh-pages
@@ -28,99 +23,7 @@ import com.gitblit.models.UserModel; * @author James Moger
*
*/
-public class PagesFilter extends AccessRestrictionFilter {
-
- /**
- * Extract the repository name from the url.
- *
- * @param url
- * @return repository name
- */
- @Override
- protected String extractRepositoryName(String url) {
- // get the repository name from the url by finding a known url suffix
- String repository = "";
- Repository r = null;
- int offset = 0;
- while (r == null) {
- int slash = url.indexOf('/', offset);
- if (slash == -1) {
- repository = url;
- } else {
- repository = url.substring(0, slash);
- }
- r = repositoryManager.getRepository(repository, false);
- if (r == null) {
- // try again
- offset = slash + 1;
- } else {
- // close the repo
- r.close();
- }
- if (repository.equals(url)) {
- // either only repository in url or no repository found
- break;
- }
- }
- return repository;
- }
-
- /**
- * Analyze the url and returns the action of the request.
- *
- * @param cloneUrl
- * @return action of the request
- */
- @Override
- protected String getUrlRequestAction(String suffix) {
- return "VIEW";
- }
-
- /**
- * Determine if a non-existing repository can be created using this filter.
- *
- * @return true if the filter allows repository creation
- */
- @Override
- protected boolean isCreationAllowed() {
- return false;
- }
-
- /**
- * Determine if the action may be executed on the repository.
- *
- * @param repository
- * @param action
- * @return true if the action may be performed
- */
- @Override
- protected boolean isActionAllowed(RepositoryModel repository, String action) {
- return true;
- }
+public class PagesFilter extends RawFilter {
- /**
- * Determine if the repository requires authentication.
- *
- * @param repository
- * @param action
- * @return true if authentication required
- */
- @Override
- protected boolean requiresAuthentication(RepositoryModel repository, String action) {
- return repository.accessRestriction.atLeast(AccessRestrictionType.VIEW);
- }
- /**
- * Determine if the user can access the repository and perform the specified
- * action.
- *
- * @param repository
- * @param user
- * @param action
- * @return true if user may execute the action on the repository
- */
- @Override
- protected boolean canAccess(RepositoryModel repository, UserModel user, String action) {
- return user.canView(repository);
- }
}
diff --git a/src/main/java/com/gitblit/servlet/PagesServlet.java b/src/main/java/com/gitblit/servlet/PagesServlet.java index 7e48f8e2..f578f86f 100644 --- a/src/main/java/com/gitblit/servlet/PagesServlet.java +++ b/src/main/java/com/gitblit/servlet/PagesServlet.java @@ -15,42 +15,10 @@ */
package com.gitblit.servlet;
-import java.io.IOException;
-import java.text.MessageFormat;
-import java.text.ParseException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.TreeMap;
-
-import javax.servlet.ServletContext;
-import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-import org.eclipse.jgit.lib.FileMode;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevTree;
-import org.slf4j.Logger;
-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.models.PathModel;
-import com.gitblit.models.RefModel;
-import com.gitblit.utils.ArrayUtils;
-import com.gitblit.utils.ByteFormat;
-import com.gitblit.utils.JGitUtils;
-import com.gitblit.utils.MarkdownUtils;
-import com.gitblit.utils.StringUtils;
-import com.gitblit.wicket.MarkupProcessor;
-import com.gitblit.wicket.MarkupProcessor.MarkupDocument;
-
-import dagger.ObjectGraph;
/**
* Serves the content of a gh-pages branch.
@@ -58,21 +26,10 @@ import dagger.ObjectGraph; * @author James Moger
*
*/
-public class PagesServlet extends DaggerServlet {
+public class PagesServlet extends RawServlet {
private static final long serialVersionUID = 1L;
- private transient Logger logger = LoggerFactory.getLogger(PagesServlet.class);
-
- private IStoredSettings settings;
-
- private IRepositoryManager repositoryManager;
-
- @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.
@@ -89,248 +46,31 @@ public class PagesServlet extends DaggerServlet { return baseURL + Constants.PAGES + repository + "/" + (path == null ? "" : ("/" + path));
}
- /**
- * Retrieves the specified resource from the gh-pages branch of the
- * repository.
- *
- * @param request
- * @param response
- * @throws javax.servlet.ServletException
- * @throws java.io.IOException
- */
- private void processRequest(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- String path = request.getPathInfo();
- if (path.toLowerCase().endsWith(".git")) {
- // forward to url with trailing /
- // this is important for relative pages links
- response.sendRedirect(request.getServletPath() + path + "/");
- return;
- }
- if (path.charAt(0) == '/') {
- // strip leading /
- path = path.substring(1);
- }
+ @Override
+ protected String getBranch(String repository, HttpServletRequest request) {
+ return "gh-pages";
+ }
- // determine repository and resource from url
- String repository = "";
- String resource = "";
- Repository r = null;
- int offset = 0;
- while (r == null) {
- int slash = path.indexOf('/', offset);
- if (slash == -1) {
- repository = path;
- } else {
- repository = path.substring(0, slash);
- }
- r = repositoryManager.getRepository(repository, false);
- offset = slash + 1;
- if (offset > 0) {
- resource = path.substring(offset);
- }
- if (repository.equals(path)) {
- // either only repository in url or no repository found
- break;
- }
+ @Override
+ protected String getPath(String repository, String branch, HttpServletRequest request) {
+ String pi = request.getPathInfo().substring(1);
+ if (pi.equals(repository)) {
+ return "";
}
-
- ServletContext context = request.getSession().getServletContext();
-
- try {
- if (r == null) {
- // repository not found!
- String mkd = MessageFormat.format(
- "# Error\nSorry, no valid **repository** specified in this url: {0}!",
- repository);
- error(response, mkd);
- return;
- }
-
- // retrieve the content from the repository
- RefModel pages = JGitUtils.getPagesBranch(r);
- RevCommit commit = JGitUtils.getCommit(r, pages.getObjectId().getName());
-
- if (commit == null) {
- // branch not found!
- String mkd = MessageFormat.format(
- "# Error\nSorry, the repository {0} does not have a **gh-pages** branch!",
- repository);
- error(response, mkd);
- return;
- }
-
- MarkupProcessor processor = new MarkupProcessor(settings);
- String [] encodings = settings.getStrings(Keys.web.blobEncodings).toArray(new String[0]);
-
- RevTree tree = commit.getTree();
-
- String res = resource;
- if (res.endsWith("/")) {
- res = res.substring(0, res.length() - 1);
- }
-
- List<PathModel> pathEntries = JGitUtils.getFilesInPath(r, res, commit);
-
- byte[] content = null;
- if (pathEntries.isEmpty()) {
- // not a path, a specific resource
- try {
- String contentType = context.getMimeType(res);
- if (contentType == null) {
- contentType = "text/plain";
- }
- if (contentType.startsWith("text")) {
- content = JGitUtils.getStringContent(r, tree, res, encodings).getBytes(
- Constants.ENCODING);
- } else {
- content = JGitUtils.getByteContent(r, tree, res, false);
- }
- response.setContentType(contentType);
- } catch (Exception e) {
- }
- } else {
- // path request
- if (!request.getPathInfo().endsWith("/")) {
- // redirect to trailing '/' url
- response.sendRedirect(request.getServletPath() + request.getPathInfo() + "/");
- return;
- }
-
- Map<String, String> names = new TreeMap<String, String>();
- for (PathModel entry : pathEntries) {
- names.put(entry.name.toLowerCase(), entry.name);
- }
-
- List<String> extensions = new ArrayList<String>();
- extensions.add("html");
- extensions.add("htm");
- extensions.addAll(processor.getMarkupExtensions());
- for (String ext : extensions) {
- String key = "index." + ext;
-
- if (names.containsKey(key)) {
- String fileName = names.get(key);
- String fullPath = fileName;
- if (!res.isEmpty()) {
- fullPath = res + "/" + fileName;
- }
- String stringContent = JGitUtils.getStringContent(r, tree, fullPath, encodings);
- if (stringContent == null) {
- continue;
- }
- content = stringContent.getBytes(Constants.ENCODING);
- if (content != null) {
- res = fullPath;
- // assume text/html unless the servlet container
- // overrides
- response.setContentType("text/html; charset=" + Constants.ENCODING);
- break;
- }
- }
- }
- }
-
- // no content, document list or custom 404 page
- if (ArrayUtils.isEmpty(content)) {
- if (pathEntries.isEmpty()) {
- // 404
- String custom404 = JGitUtils.getStringContent(r, tree, "404.html", encodings);
- if (!StringUtils.isEmpty(custom404)) {
- content = custom404.getBytes(Constants.ENCODING);
- }
-
- // still no content
- if (ArrayUtils.isEmpty(content)) {
- String str = MessageFormat.format(
- "# Error\nSorry, the requested resource **{0}** was not found.",
- resource);
- content = MarkdownUtils.transformMarkdown(str).getBytes(Constants.ENCODING);
- }
-
- try {
- // output the content
- logger.warn("Pages 404: " + resource);
- response.setStatus(HttpServletResponse.SC_NOT_FOUND);
- response.getOutputStream().write(content);
- response.flushBuffer();
- } catch (Throwable t) {
- logger.error("Failed to write page to client", t);
- }
- } else {
- // document list
- response.setContentType("text/html");
- response.getWriter().append("<style>table th, table td { min-width: 150px; text-align: left; }</style>");
- response.getWriter().append("<table>");
- response.getWriter().append("<thead><tr><th>path</th><th>mode</th><th>size</th></tr>");
- response.getWriter().append("</thead>");
- response.getWriter().append("<tbody>");
- String pattern = "<tr><td><a href=\"{0}/{1}\">{1}</a></td><td>{2}</td><td>{3}</td></tr>";
- final ByteFormat byteFormat = new ByteFormat();
- if (!pathEntries.isEmpty()) {
- if (pathEntries.get(0).path.indexOf('/') > -1) {
- // we are in a subdirectory, add parent directory link
- pathEntries.add(0, new PathModel("..", resource + "/..", 0, FileMode.TREE.getBits(), null, null));
- }
- }
-
- String basePath = request.getServletPath() + request.getPathInfo();
- if (basePath.charAt(basePath.length() - 1) == '/') {
- // strip trailing slash
- basePath = basePath.substring(0, basePath.length() - 1);
- }
- for (PathModel entry : pathEntries) {
- response.getWriter().append(MessageFormat.format(pattern, basePath, entry.name,
- JGitUtils.getPermissionsFromMode(entry.mode), byteFormat.format(entry.size)));
- }
- response.getWriter().append("</tbody>");
- response.getWriter().append("</table>");
- }
- return;
- }
-
- // check to see if we should transform markup files
- String ext = StringUtils.getFileExtension(resource);
- if (processor.getMarkupExtensions().contains(ext)) {
- String markup = new String(content, Constants.ENCODING);
- MarkupDocument markupDoc = processor.parse(repository, commit.getName(), resource, markup);
- content = markupDoc.html.getBytes("UTF-8");
- response.setContentType("text/html; charset=" + Constants.ENCODING);
- }
-
- try {
- // output the content
- response.setHeader("Cache-Control", "public, max-age=3600, must-revalidate");
- response.setDateHeader("Last-Modified", JGitUtils.getCommitDate(commit).getTime());
- response.getOutputStream().write(content);
- response.flushBuffer();
- } catch (Throwable t) {
- logger.error("Failed to write page to client", t);
- }
-
- } catch (Throwable t) {
- logger.error("Failed to write page to client", t);
- } finally {
- r.close();
+ String path = pi.substring(pi.indexOf(repository) + repository.length() + 1);
+ if (path.endsWith("/")) {
+ path = path.substring(0, path.length() - 1);
}
- }
-
- private void error(HttpServletResponse response, String mkd) throws ServletException,
- IOException, ParseException {
- String content = MarkdownUtils.transformMarkdown(mkd);
- response.setContentType("text/html; charset=" + Constants.ENCODING);
- response.getWriter().write(content);
+ return path;
}
@Override
- protected void doPost(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- processRequest(request, response);
+ protected boolean renderIndex() {
+ return true;
}
@Override
- protected void doGet(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- processRequest(request, response);
+ protected void setContentType(HttpServletResponse response, String contentType) {
+ response.setContentType(contentType);;
}
}
diff --git a/src/main/java/com/gitblit/servlet/ProxyFilter.java b/src/main/java/com/gitblit/servlet/ProxyFilter.java new file mode 100644 index 00000000..46f59de9 --- /dev/null +++ b/src/main/java/com/gitblit/servlet/ProxyFilter.java @@ -0,0 +1,86 @@ +/* + * 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.servlet; + +import java.io.IOException; +import java.util.Iterator; +import java.util.List; + +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +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. + * + * @author David Ostrovsky + * @since 1.6.0 + */ +public class ProxyFilter extends DaggerFilter { + private List<HttpRequestFilter> filters; + + @Override + protected void inject(ObjectGraph dagger, FilterConfig filterConfig) throws ServletException { + IRuntimeManager runtimeManager = dagger.get(IRuntimeManager.class); + IPluginManager pluginManager = dagger.get(IPluginManager.class); + + filters = pluginManager.getExtensions(HttpRequestFilter.class); + for (HttpRequestFilter f : filters) { + // wrap the filter config for Gitblit settings retrieval + PluginWrapper pluginWrapper = pluginManager.whichPlugin(f.getClass()); + FilterConfig runtimeConfig = new FilterRuntimeConfig(runtimeManager, + pluginWrapper.getPluginId(), filterConfig); + + f.init(runtimeConfig); + } + } + + @Override + public void doFilter(ServletRequest req, ServletResponse res, final FilterChain last) + throws IOException, ServletException { + final Iterator<HttpRequestFilter> itr = filters.iterator(); + new FilterChain() { + @Override + public void doFilter(ServletRequest req, ServletResponse res) throws IOException, + ServletException { + if (itr.hasNext()) { + itr.next().doFilter(req, res, this); + } else { + last.doFilter(req, res); + } + } + }.doFilter(req, res); + } + + @Override + public void destroy() { + for (HttpRequestFilter f : filters) { + f.destroy(); + } + } +} diff --git a/src/main/java/com/gitblit/servlet/RawFilter.java b/src/main/java/com/gitblit/servlet/RawFilter.java new file mode 100644 index 00000000..34989c98 --- /dev/null +++ b/src/main/java/com/gitblit/servlet/RawFilter.java @@ -0,0 +1,126 @@ +/*
+ * 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 org.eclipse.jgit.lib.Repository;
+
+import com.gitblit.Constants.AccessRestrictionType;
+import com.gitblit.models.RepositoryModel;
+import com.gitblit.models.UserModel;
+
+/**
+ * The RawFilter is an AccessRestrictionFilter which ensures http branch
+ * requests for a view-restricted repository are authenticated and authorized.
+ *
+ * @author James Moger
+ *
+ */
+public class RawFilter extends AccessRestrictionFilter {
+
+ /**
+ * Extract the repository name from the url.
+ *
+ * @param url
+ * @return repository name
+ */
+ @Override
+ protected String extractRepositoryName(String url) {
+ // get the repository name from the url by finding a known url suffix
+ String repository = "";
+ Repository r = null;
+ int offset = 0;
+ while (r == null) {
+ int slash = url.indexOf('/', offset);
+ if (slash == -1) {
+ repository = url;
+ } else {
+ repository = url.substring(0, slash);
+ }
+ r = repositoryManager.getRepository(repository, false);
+ if (r == null) {
+ // try again
+ offset = slash + 1;
+ } else {
+ // close the repo
+ r.close();
+ }
+ if (repository.equals(url)) {
+ // either only repository in url or no repository found
+ break;
+ }
+ }
+ return repository;
+ }
+
+ /**
+ * Analyze the url and returns the action of the request.
+ *
+ * @param cloneUrl
+ * @return action of the request
+ */
+ @Override
+ protected String getUrlRequestAction(String suffix) {
+ return "VIEW";
+ }
+
+ /**
+ * Determine if a non-existing repository can be created using this filter.
+ *
+ * @return true if the filter allows repository creation
+ */
+ @Override
+ protected boolean isCreationAllowed() {
+ return false;
+ }
+
+ /**
+ * Determine if the action may be executed on the repository.
+ *
+ * @param repository
+ * @param action
+ * @return true if the action may be performed
+ */
+ @Override
+ protected boolean isActionAllowed(RepositoryModel repository, String action) {
+ return true;
+ }
+
+ /**
+ * Determine if the repository requires authentication.
+ *
+ * @param repository
+ * @param action
+ * @return true if authentication required
+ */
+ @Override
+ protected boolean requiresAuthentication(RepositoryModel repository, String action) {
+ return repository.accessRestriction.atLeast(AccessRestrictionType.VIEW);
+ }
+
+ /**
+ * Determine if the user can access the repository and perform the specified
+ * action.
+ *
+ * @param repository
+ * @param user
+ * @param action
+ * @return true if user may execute the action on the repository
+ */
+ @Override
+ protected boolean canAccess(RepositoryModel repository, UserModel user, String action) {
+ return user.canView(repository);
+ }
+}
diff --git a/src/main/java/com/gitblit/servlet/RawServlet.java b/src/main/java/com/gitblit/servlet/RawServlet.java new file mode 100644 index 00000000..15e036ea --- /dev/null +++ b/src/main/java/com/gitblit/servlet/RawServlet.java @@ -0,0 +1,483 @@ +/* + * 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.servlet; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.text.MessageFormat; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.tika.Tika; +import org.eclipse.jgit.lib.FileMode; +import org.eclipse.jgit.lib.MutableObjectId; +import org.eclipse.jgit.lib.ObjectLoader; +import org.eclipse.jgit.lib.ObjectReader; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevWalk; +import org.eclipse.jgit.treewalk.TreeWalk; +import org.eclipse.jgit.treewalk.filter.PathFilter; +import org.slf4j.Logger; +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; +import com.gitblit.utils.ByteFormat; +import com.gitblit.utils.JGitUtils; +import com.gitblit.utils.MarkdownUtils; +import com.gitblit.utils.StringUtils; + +import dagger.ObjectGraph; + +/** + * Serves the content of a branch. + * + * @author James Moger + * + */ +public class RawServlet extends DaggerServlet { + + private static final long serialVersionUID = 1L; + + private transient Logger logger = LoggerFactory.getLogger(RawServlet.class); + + private IRuntimeManager runtimeManager; + + private IRepositoryManager repositoryManager; + + @Override + protected void inject(ObjectGraph dagger) { + this.runtimeManager = dagger.get(IRuntimeManager.class); + this.repositoryManager = dagger.get(IRepositoryManager.class); + } + + /** + * Returns an url to this servlet for the specified parameters. + * + * @param baseURL + * @param repository + * @param branch + * @param path + * @return an url + */ + public static String asLink(String baseURL, String repository, String branch, String path) { + if (baseURL.length() > 0 && baseURL.charAt(baseURL.length() - 1) == '/') { + baseURL = baseURL.substring(0, baseURL.length() - 1); + } + + if (branch != null) { + char fsc = '!'; + char c = GitblitContext.getManager(IRuntimeManager.class).getSettings().getChar(Keys.web.forwardSlashCharacter, '/'); + if (c != '/') { + fsc = c; + } + branch = branch.replace('/', fsc); + } + + String encodedPath = path == null ? "" : path.replace(' ', '-'); + try { + encodedPath = URLEncoder.encode(encodedPath, "UTF-8"); + } catch (UnsupportedEncodingException e) { + } + return baseURL + Constants.RAW_PATH + repository + "/" + (branch == null ? "" : (branch + "/" + (path == null ? "" : encodedPath))); + } + + protected String getBranch(String repository, HttpServletRequest request) { + String pi = request.getPathInfo(); + String branch = pi.substring(pi.indexOf(repository) + repository.length() + 1); + int fs = branch.indexOf('/'); + if (fs > -1) { + branch = branch.substring(0, fs); + } + char c = runtimeManager.getSettings().getChar(Keys.web.forwardSlashCharacter, '/'); + return branch.replace('!', '/').replace(c, '/'); + } + + protected String getPath(String repository, String branch, HttpServletRequest request) { + String base = repository + "/" + branch; + String pi = request.getPathInfo().substring(1); + if (pi.equals(base)) { + return ""; + } + String path = pi.substring(pi.indexOf(base) + base.length() + 1); + if (path.endsWith("/")) { + path = path.substring(0, path.length() - 1); + } + return path; + } + + protected boolean renderIndex() { + return false; + } + + /** + * Retrieves the specified resource from the specified branch of the + * repository. + * + * @param request + * @param response + * @throws javax.servlet.ServletException + * @throws java.io.IOException + */ + private void processRequest(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + String path = request.getPathInfo(); + if (path.toLowerCase().endsWith(".git")) { + // forward to url with trailing / + // this is important for relative pages links + response.sendRedirect(request.getServletPath() + path + "/"); + return; + } + if (path.charAt(0) == '/') { + // strip leading / + path = path.substring(1); + } + + // determine repository and resource from url + String repository = ""; + Repository r = null; + int offset = 0; + while (r == null) { + int slash = path.indexOf('/', offset); + if (slash == -1) { + repository = path; + } else { + repository = path.substring(0, slash); + } + offset += slash; + r = repositoryManager.getRepository(repository, false); + if (repository.equals(path)) { + // either only repository in url or no repository found + break; + } + } + + ServletContext context = request.getSession().getServletContext(); + + try { + if (r == null) { + // repository not found! + String mkd = MessageFormat.format( + "# Error\nSorry, no valid **repository** specified in this url: {0}!", + path); + error(response, mkd); + return; + } + + // identify the branch + String branch = getBranch(repository, request); + if (StringUtils.isEmpty(branch)) { + branch = r.getBranch(); + if (branch == null) { + // no branches found! empty? + String mkd = MessageFormat.format( + "# Error\nSorry, no valid **branch** specified in this url: {0}!", + path); + error(response, mkd); + } else { + // redirect to default branch + String base = request.getRequestURI(); + String url = base + branch + "/"; + response.sendRedirect(url); + } + return; + } + + // identify the requested path + String requestedPath = getPath(repository, branch, request); + + // identify the commit + RevCommit commit = JGitUtils.getCommit(r, branch); + if (commit == null) { + // branch not found! + String mkd = MessageFormat.format( + "# Error\nSorry, the repository {0} does not have a **{1}** branch!", + repository, branch); + error(response, mkd); + return; + } + + + List<PathModel> pathEntries = JGitUtils.getFilesInPath(r, requestedPath, commit); + if (pathEntries.isEmpty()) { + // 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); + + if (contentType == null) { + // ask the container for the content type + contentType = context.getMimeType(requestedPath); + + if (contentType == null) { + // still unknown content type, assume binary + contentType = "application/octet-stream"; + } + } + + setContentType(response, contentType); + + if (isTextType(contentType)) { + + // load, interpret, and serve text content as UTF-8 + String [] encodings = runtimeManager.getSettings().getStrings(Keys.web.blobEncodings).toArray(new String[0]); + String content = JGitUtils.getStringContent(r, commit.getTree(), requestedPath, encodings); + + byte [] bytes = content.getBytes(Constants.ENCODING); + response.setContentLength(bytes.length); + ByteArrayInputStream is = new ByteArrayInputStream(bytes); + sendContent(response, JGitUtils.getCommitDate(commit), is); + + } else { + // serve binary content + String filename = StringUtils.getLastPathElement(requestedPath); + try { + String userAgent = request.getHeader("User-Agent"); + if (userAgent != null && userAgent.indexOf("MSIE 5.5") > -1) { + response.setHeader("Content-Disposition", "filename=\"" + + URLEncoder.encode(filename, Constants.ENCODING) + "\""); + } else if (userAgent != null && userAgent.indexOf("MSIE") > -1) { + response.setHeader("Content-Disposition", "attachment; filename=\"" + + URLEncoder.encode(filename, Constants.ENCODING) + "\""); + } else { + response.setHeader("Content-Disposition", "attachment; filename=\"" + + new String(filename.getBytes(Constants.ENCODING), "latin1") + "\""); + } + } + catch (UnsupportedEncodingException e) { + response.setHeader("Content-Disposition", "attachment; filename=\"" + filename + "\""); + } + + // stream binary content directly from the repository + streamFromRepo(response, r, commit, requestedPath); + } + return; + } catch (Exception e) { + logger.error(null, e); + } + } else { + // path request + if (!request.getPathInfo().endsWith("/")) { + // redirect to trailing '/' url + response.sendRedirect(request.getServletPath() + request.getPathInfo() + "/"); + return; + } + + if (renderIndex()) { + // locate and render an index file + Map<String, String> names = new TreeMap<String, String>(); + for (PathModel entry : pathEntries) { + names.put(entry.name.toLowerCase(), entry.name); + } + + List<String> extensions = new ArrayList<String>(); + extensions.add("html"); + extensions.add("htm"); + + String content = null; + for (String ext : extensions) { + String key = "index." + ext; + + if (names.containsKey(key)) { + String fileName = names.get(key); + String fullPath = fileName; + if (!requestedPath.isEmpty()) { + fullPath = requestedPath + "/" + fileName; + } + + String [] encodings = runtimeManager.getSettings().getStrings(Keys.web.blobEncodings).toArray(new String[0]); + String stringContent = JGitUtils.getStringContent(r, commit.getTree(), fullPath, encodings); + if (stringContent == null) { + continue; + } + content = stringContent; + requestedPath = fullPath; + break; + } + } + + response.setContentType("text/html; charset=" + Constants.ENCODING); + byte [] bytes = content.getBytes(Constants.ENCODING); + response.setContentLength(bytes.length); + + ByteArrayInputStream is = new ByteArrayInputStream(bytes); + sendContent(response, JGitUtils.getCommitDate(commit), is); + return; + } + } + + // no content, document list or 404 page + if (pathEntries.isEmpty()) { + // default 404 page + String str = MessageFormat.format( + "# Error\nSorry, the requested resource **{0}** was not found.", + requestedPath); + response.setStatus(HttpServletResponse.SC_NOT_FOUND); + error(response, str); + return; + } else { + // + // directory list + // + response.setContentType("text/html"); + response.getWriter().append("<style>table th, table td { min-width: 150px; text-align: left; }</style>"); + response.getWriter().append("<table>"); + response.getWriter().append("<thead><tr><th>path</th><th>mode</th><th>size</th></tr>"); + response.getWriter().append("</thead>"); + response.getWriter().append("<tbody>"); + String pattern = "<tr><td><a href=\"{0}/{1}\">{1}</a></td><td>{2}</td><td>{3}</td></tr>"; + final ByteFormat byteFormat = new ByteFormat(); + if (!pathEntries.isEmpty()) { + if (pathEntries.get(0).path.indexOf('/') > -1) { + // we are in a subdirectory, add parent directory link + String pp = URLEncoder.encode(requestedPath, Constants.ENCODING); + pathEntries.add(0, new PathModel("..", pp + "/..", 0, FileMode.TREE.getBits(), null, null)); + } + } + + String basePath = request.getServletPath() + request.getPathInfo(); + if (basePath.charAt(basePath.length() - 1) == '/') { + // strip trailing slash + basePath = basePath.substring(0, basePath.length() - 1); + } + for (PathModel entry : pathEntries) { + String pp = URLEncoder.encode(entry.name, Constants.ENCODING); + response.getWriter().append(MessageFormat.format(pattern, basePath, pp, + JGitUtils.getPermissionsFromMode(entry.mode), + entry.isFile() ? byteFormat.format(entry.size) : "")); + } + response.getWriter().append("</tbody>"); + response.getWriter().append("</table>"); + } + } catch (Throwable t) { + logger.error("Failed to write page to client", t); + } finally { + r.close(); + } + } + + protected boolean isTextType(String contentType) { + if (contentType.startsWith("text/") + || "application/json".equals(contentType) + || "application/xml".equals(contentType)) { + return true; + } + return false; + } + + /** + * Override all text types to be plain text. + * + * @param response + * @param contentType + */ + protected void setContentType(HttpServletResponse response, String contentType) { + if (isTextType(contentType)) { + response.setContentType("text/plain"); + } else { + response.setContentType(contentType); + } + } + + private void streamFromRepo(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"); + + RevWalk rw = new RevWalk(repository); + TreeWalk tw = new TreeWalk(repository); + try { + tw.reset(); + tw.addTree(commit.getTree()); + PathFilter f = PathFilter.create(requestedPath); + tw.setFilter(f); + tw.setRecursive(true); + MutableObjectId id = new MutableObjectId(); + ObjectReader reader = tw.getObjectReader(); + while (tw.next()) { + FileMode mode = tw.getFileMode(0); + if (mode == FileMode.GITLINK || mode == FileMode.TREE) { + continue; + } + tw.getObjectId(id, 0); + + long len = reader.getObjectSize(id, org.eclipse.jgit.lib.Constants.OBJ_BLOB); + response.setIntHeader("Content-Length", (int) len); + ObjectLoader ldr = repository.open(id); + ldr.copyTo(response.getOutputStream()); + } + } finally { + tw.release(); + rw.dispose(); + } + + response.flushBuffer(); + } + + private 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"); + try { + byte[] tmp = new byte[8192]; + int len = 0; + while ((len = is.read(tmp)) > -1) { + response.getOutputStream().write(tmp, 0, len); + } + } finally { + is.close(); + } + response.flushBuffer(); + } + + private void error(HttpServletResponse response, String mkd) throws ServletException, + IOException, ParseException { + String content = MarkdownUtils.transformMarkdown(mkd); + response.setContentType("text/html; charset=" + Constants.ENCODING); + response.getWriter().write(content); + } + + @Override + protected void doPost(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + processRequest(request, response); + } + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + processRequest(request, response); + } +} diff --git a/src/main/java/com/gitblit/servlet/RpcFilter.java b/src/main/java/com/gitblit/servlet/RpcFilter.java index e0b1a233..23bf956e 100644 --- a/src/main/java/com/gitblit/servlet/RpcFilter.java +++ b/src/main/java/com/gitblit/servlet/RpcFilter.java @@ -19,6 +19,7 @@ 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;
@@ -53,8 +54,8 @@ public class RpcFilter extends AuthenticationFilter { private IRuntimeManager runtimeManager;
@Override
- protected void inject(ObjectGraph dagger) {
- super.inject(dagger);
+ protected void inject(ObjectGraph dagger, FilterConfig filterConfig) {
+ super.inject(dagger, filterConfig);
this.settings = dagger.get(IStoredSettings.class);
this.runtimeManager = dagger.get(IRuntimeManager.class);
}
diff --git a/src/main/java/com/gitblit/servlet/RpcServlet.java b/src/main/java/com/gitblit/servlet/RpcServlet.java index 2d59ebd7..b8cdfb04 100644 --- a/src/main/java/com/gitblit/servlet/RpcServlet.java +++ b/src/main/java/com/gitblit/servlet/RpcServlet.java @@ -53,13 +53,12 @@ import dagger.ObjectGraph; * Handles remote procedure calls.
*
* @author James Moger
- *
*/
public class RpcServlet extends JsonServlet {
private static final long serialVersionUID = 1L;
- public static final int PROTOCOL_VERSION = 7;
+ public static final int PROTOCOL_VERSION = 8;
private IStoredSettings settings;
@@ -80,12 +79,11 @@ public class RpcServlet extends JsonServlet { * @throws java.io.IOException
*/
@Override
- protected void processRequest(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
+ protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException,
+ IOException {
RpcRequest reqType = RpcRequest.fromName(request.getParameter("req"));
String objectName = request.getParameter("name");
- logger.info(MessageFormat.format("Rpc {0} request from {1}", reqType,
- request.getRemoteAddr()));
+ logger.info(MessageFormat.format("Rpc {0} request from {1}", reqType, request.getRemoteAddr()));
UserModel user = (UserModel) request.getUserPrincipal();
@@ -130,7 +128,8 @@ public class RpcServlet extends JsonServlet { }
if (model.isCollectingGarbage) {
// skip garbage collecting repository
- logger.warn(MessageFormat.format("Temporarily excluding {0} from RPC, busy collecting garbage", model.name));
+ logger.warn(MessageFormat.format("Temporarily excluding {0} from RPC, busy collecting garbage",
+ model.name));
continue;
}
// get local branches
@@ -196,6 +195,33 @@ public class RpcServlet extends JsonServlet { } catch (GitBlitException e) {
response.setStatus(failureCode);
}
+ } else if (RpcRequest.FORK_REPOSITORY.equals(reqType)) {
+ // fork repository
+ RepositoryModel origin = gitblit.getRepositoryModel(objectName);
+ if (origin == null) {
+ // failed to find repository, error is logged by the repository
+ // manager
+ response.setStatus(failureCode);
+ } else {
+ if (user == null || !user.canFork(origin)) {
+ logger.error("User {} is not permitted to fork '{}'!", user == null ? "anonymous" : user.username,
+ objectName);
+ response.setStatus(failureCode);
+ } else {
+ try {
+ // fork the origin
+ RepositoryModel fork = gitblit.fork(origin, user);
+ if (fork == null) {
+ logger.error("Failed to fork repository '{}'!", objectName);
+ response.setStatus(failureCode);
+ } else {
+ logger.info("User {} has forked '{}'!", user.username, objectName);
+ }
+ } catch (GitBlitException e) {
+ response.setStatus(failureCode);
+ }
+ }
+ }
} else if (RpcRequest.EDIT_REPOSITORY.equals(reqType)) {
// edit repository
RepositoryModel model = deserialize(request, response, RepositoryModel.class);
@@ -281,7 +307,8 @@ public class RpcServlet extends JsonServlet { } else if (RpcRequest.SET_REPOSITORY_MEMBER_PERMISSIONS.equals(reqType)) {
// set the repository permissions for the specified users
RepositoryModel model = gitblit.getRepositoryModel(objectName);
- Collection<RegistrantAccessPermission> permissions = deserialize(request, response, RpcUtils.REGISTRANT_PERMISSIONS_TYPE);
+ Collection<RegistrantAccessPermission> permissions = deserialize(request, response,
+ RpcUtils.REGISTRANT_PERMISSIONS_TYPE);
result = gitblit.setUserAccessPermissions(model, permissions);
} else if (RpcRequest.LIST_REPOSITORY_TEAMS.equals(reqType)) {
// get repository teams
@@ -297,7 +324,8 @@ public class RpcServlet extends JsonServlet { } else if (RpcRequest.SET_REPOSITORY_TEAM_PERMISSIONS.equals(reqType)) {
// set the repository permissions for the specified teams
RepositoryModel model = gitblit.getRepositoryModel(objectName);
- Collection<RegistrantAccessPermission> permissions = deserialize(request, response, RpcUtils.REGISTRANT_PERMISSIONS_TYPE);
+ Collection<RegistrantAccessPermission> permissions = deserialize(request, response,
+ RpcUtils.REGISTRANT_PERMISSIONS_TYPE);
result = gitblit.setTeamAccessPermissions(model, permissions);
} else if (RpcRequest.LIST_FEDERATION_REGISTRATIONS.equals(reqType)) {
// return the list of federation registrations
@@ -363,8 +391,7 @@ public class RpcServlet extends JsonServlet { } else if (RpcRequest.EDIT_SETTINGS.equals(reqType)) {
// update settings on the server
if (allowAdmin) {
- Map<String, String> map = deserialize(request, response,
- RpcUtils.SETTINGS_TYPE);
+ Map<String, String> map = deserialize(request, response, RpcUtils.SETTINGS_TYPE);
gitblit.updateSettings(map);
} else {
response.sendError(notAllowedCode);
diff --git a/src/main/java/com/gitblit/servlet/SyndicationFilter.java b/src/main/java/com/gitblit/servlet/SyndicationFilter.java index 67a845ea..78da47e9 100644 --- a/src/main/java/com/gitblit/servlet/SyndicationFilter.java +++ b/src/main/java/com/gitblit/servlet/SyndicationFilter.java @@ -19,6 +19,7 @@ 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;
@@ -50,8 +51,8 @@ public class SyndicationFilter extends AuthenticationFilter { private IProjectManager projectManager;
@Override
- protected void inject(ObjectGraph dagger) {
- super.inject(dagger);
+ 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);
diff --git a/src/main/java/com/gitblit/servlet/SyndicationServlet.java b/src/main/java/com/gitblit/servlet/SyndicationServlet.java index 24def995..631df781 100644 --- a/src/main/java/com/gitblit/servlet/SyndicationServlet.java +++ b/src/main/java/com/gitblit/servlet/SyndicationServlet.java @@ -163,6 +163,15 @@ public class SyndicationServlet extends DaggerServlet { searchType = type;
}
}
+
+ Constants.FeedObjectType objectType = Constants.FeedObjectType.COMMIT;
+ if (!StringUtils.isEmpty(request.getParameter("ot"))) {
+ Constants.FeedObjectType type = Constants.FeedObjectType.forName(request.getParameter("ot"));
+ if (type != null) {
+ objectType = type;
+ }
+ }
+
int length = settings.getInteger(Keys.web.syndicationEntries, 25);
if (StringUtils.isEmpty(objectId)) {
objectId = org.eclipse.jgit.lib.Constants.HEAD;
@@ -214,14 +223,7 @@ public class SyndicationServlet extends DaggerServlet { boolean mountParameters = settings.getBoolean(Keys.web.mountParameters, true);
- String urlPattern;
- if (mountParameters) {
- // mounted parameters
- urlPattern = "{0}/commit/{1}/{2}";
- } else {
- // parameterized parameters
- urlPattern = "{0}/commit/?r={1}&h={2}";
- }
+
String gitblitUrl = settings.getString(Keys.web.canonicalUrl, null);
if (StringUtils.isEmpty(gitblitUrl)) {
gitblitUrl = HttpUtils.getGitblitURL(request);
@@ -247,47 +249,92 @@ public class SyndicationServlet extends DaggerServlet { feedDescription = model.description;
}
- List<RevCommit> commits;
- if (StringUtils.isEmpty(searchString)) {
- // standard log/history lookup
- commits = JGitUtils.getRevLog(repository, objectId, offset, length);
+ if (objectType == Constants.FeedObjectType.TAG) {
+
+ String urlPattern;
+ if (mountParameters) {
+ // mounted parameters
+ urlPattern = "{0}/tag/{1}/{2}";
+ } else {
+ // parameterized parameters
+ urlPattern = "{0}/tag/?r={1}&h={2}";
+ }
+
+ List<RefModel> tags = JGitUtils.getTags(repository, false, length, offset);
+
+ for (RefModel tag : tags) {
+ FeedEntryModel entry = new FeedEntryModel();
+ entry.title = tag.getName();
+ entry.author = tag.getAuthorIdent().getName();
+ entry.link = MessageFormat.format(urlPattern, gitblitUrl,
+ StringUtils.encodeURL(model.name.replace('/', fsc)), tag.getObjectId().getName());
+ entry.published = tag.getDate();
+ entry.contentType = "text/html";
+ entry.content = tag.getFullMessage();
+ entry.repository = model.name;
+ entry.branch = objectId;
+
+ entry.tags = new ArrayList<String>();
+
+ // add tag id and referenced commit id
+ entry.tags.add("tag:" + tag.getObjectId().getName());
+ entry.tags.add("commit:" + tag.getReferencedObjectId().getName());
+
+ entries.add(entry);
+ }
} else {
- // repository search
- commits = JGitUtils.searchRevlogs(repository, objectId, searchString, searchType,
- offset, length);
- }
- Map<ObjectId, List<RefModel>> allRefs = JGitUtils.getAllRefs(repository, model.showRemoteBranches);
- BugtraqProcessor processor = new BugtraqProcessor(settings);
-
- // convert RevCommit to SyndicatedEntryModel
- for (RevCommit commit : commits) {
- FeedEntryModel entry = new FeedEntryModel();
- entry.title = commit.getShortMessage();
- entry.author = commit.getAuthorIdent().getName();
- entry.link = MessageFormat.format(urlPattern, gitblitUrl,
- StringUtils.encodeURL(model.name.replace('/', fsc)), commit.getName());
- entry.published = commit.getCommitterIdent().getWhen();
- entry.contentType = "text/html";
- String message = processor.processCommitMessage(repository, model, commit.getFullMessage());
- entry.content = message;
- entry.repository = model.name;
- entry.branch = objectId;
- entry.tags = new ArrayList<String>();
-
- // add commit id and parent commit ids
- entry.tags.add("commit:" + commit.getName());
- for (RevCommit parent : commit.getParents()) {
- entry.tags.add("parent:" + parent.getName());
+
+ String urlPattern;
+ if (mountParameters) {
+ // mounted parameters
+ urlPattern = "{0}/commit/{1}/{2}";
+ } else {
+ // parameterized parameters
+ urlPattern = "{0}/commit/?r={1}&h={2}";
}
- // add refs to tabs list
- List<RefModel> refs = allRefs.get(commit.getId());
- if (refs != null && refs.size() > 0) {
- for (RefModel ref : refs) {
- entry.tags.add("ref:" + ref.getName());
+ List<RevCommit> commits;
+ if (StringUtils.isEmpty(searchString)) {
+ // standard log/history lookup
+ commits = JGitUtils.getRevLog(repository, objectId, offset, length);
+ } else {
+ // repository search
+ commits = JGitUtils.searchRevlogs(repository, objectId, searchString, searchType,
+ offset, length);
+ }
+ Map<ObjectId, List<RefModel>> allRefs = JGitUtils.getAllRefs(repository, model.showRemoteBranches);
+ BugtraqProcessor processor = new BugtraqProcessor(settings);
+
+ // convert RevCommit to SyndicatedEntryModel
+ for (RevCommit commit : commits) {
+ FeedEntryModel entry = new FeedEntryModel();
+ entry.title = commit.getShortMessage();
+ entry.author = commit.getAuthorIdent().getName();
+ entry.link = MessageFormat.format(urlPattern, gitblitUrl,
+ StringUtils.encodeURL(model.name.replace('/', fsc)), commit.getName());
+ entry.published = commit.getCommitterIdent().getWhen();
+ entry.contentType = "text/html";
+ String message = processor.processCommitMessage(repository, model, commit.getFullMessage());
+ entry.content = message;
+ entry.repository = model.name;
+ entry.branch = objectId;
+ entry.tags = new ArrayList<String>();
+
+ // add commit id and parent commit ids
+ entry.tags.add("commit:" + commit.getName());
+ for (RevCommit parent : commit.getParents()) {
+ entry.tags.add("parent:" + parent.getName());
+ }
+
+ // add refs to tabs list
+ List<RefModel> refs = allRefs.get(commit.getId());
+ if (refs != null && refs.size() > 0) {
+ for (RefModel ref : refs) {
+ entry.tags.add("ref:" + ref.getName());
+ }
}
+ entries.add(entry);
}
- entries.add(entry);
}
}
diff --git a/src/main/java/com/gitblit/tickets/BranchTicketService.java b/src/main/java/com/gitblit/tickets/BranchTicketService.java index 284b1be1..8c000550 100644 --- a/src/main/java/com/gitblit/tickets/BranchTicketService.java +++ b/src/main/java/com/gitblit/tickets/BranchTicketService.java @@ -378,6 +378,37 @@ public class BranchTicketService extends ITicketService implements RefsChangedLi } /** + * Returns the assigned ticket ids. + * + * @return the assigned ticket ids + */ + @Override + public synchronized Set<Long> getIds(RepositoryModel repository) { + Repository db = repositoryManager.getRepository(repository.name); + try { + if (getTicketsBranch(db) == null) { + return Collections.emptySet(); + } + Set<Long> ids = new TreeSet<Long>(); + List<PathModel> paths = JGitUtils.getDocuments(db, Arrays.asList("json"), BRANCH); + for (PathModel path : paths) { + String name = path.name.substring(path.name.lastIndexOf('/') + 1); + if (!JOURNAL.equals(name)) { + continue; + } + String tid = path.path.split("/")[2]; + long ticketId = Long.parseLong(tid); + ids.add(ticketId); + } + return ids; + } finally { + if (db != null) { + db.close(); + } + } + } + + /** * Assigns a new ticket id. * * @param repository @@ -398,16 +429,10 @@ public class BranchTicketService extends ITicketService implements RefsChangedLi } AtomicLong lastId = lastAssignedId.get(repository.name); if (lastId.get() <= 0) { - List<PathModel> paths = JGitUtils.getDocuments(db, Arrays.asList("json"), BRANCH); - for (PathModel path : paths) { - String name = path.name.substring(path.name.lastIndexOf('/') + 1); - if (!JOURNAL.equals(name)) { - continue; - } - String tid = path.path.split("/")[2]; - long ticketId = Long.parseLong(tid); - if (ticketId > lastId.get()) { - lastId.set(ticketId); + Set<Long> ids = getIds(repository); + for (long id : ids) { + if (id > lastId.get()) { + lastId.set(id); } } } @@ -526,6 +551,28 @@ public class BranchTicketService extends ITicketService implements RefsChangedLi } /** + * Retrieves the journal for the ticket. + * + * @param repository + * @param ticketId + * @return a journal, if it exists, otherwise null + */ + @Override + protected List<Change> getJournalImpl(RepositoryModel repository, long ticketId) { + Repository db = repositoryManager.getRepository(repository.name); + try { + List<Change> changes = getJournal(db, ticketId); + if (ArrayUtils.isEmpty(changes)) { + log.warn("Empty journal for {}:{}", repository, ticketId); + return null; + } + return changes; + } finally { + db.close(); + } + } + + /** * Returns the journal for the specified ticket. * * @param db diff --git a/src/main/java/com/gitblit/tickets/FileTicketService.java b/src/main/java/com/gitblit/tickets/FileTicketService.java index 4386020f..b3d8838e 100644 --- a/src/main/java/com/gitblit/tickets/FileTicketService.java +++ b/src/main/java/com/gitblit/tickets/FileTicketService.java @@ -22,6 +22,8 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.TreeSet; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; @@ -146,6 +148,31 @@ public class FileTicketService extends ITicketService { return hasTicket; } + @Override + public synchronized Set<Long> getIds(RepositoryModel repository) { + Set<Long> ids = new TreeSet<Long>(); + Repository db = repositoryManager.getRepository(repository.name); + try { + // identify current highest ticket id by scanning the paths in the tip tree + File dir = new File(db.getDirectory(), TICKETS_PATH); + dir.mkdirs(); + List<File> journals = findAll(dir, JOURNAL); + for (File journal : journals) { + // Reconstruct ticketId from the path + // id/26/326/journal.json + String path = FileUtils.getRelativePath(dir, journal); + String tid = path.split("/")[1]; + long ticketId = Long.parseLong(tid); + ids.add(ticketId); + } + } finally { + if (db != null) { + db.close(); + } + } + return ids; + } + /** * Assigns a new ticket id. * @@ -162,18 +189,10 @@ public class FileTicketService extends ITicketService { } AtomicLong lastId = lastAssignedId.get(repository.name); if (lastId.get() <= 0) { - // identify current highest ticket id by scanning the paths in the tip tree - File dir = new File(db.getDirectory(), TICKETS_PATH); - dir.mkdirs(); - List<File> journals = findAll(dir, JOURNAL); - for (File journal : journals) { - // Reconstruct ticketId from the path - // id/26/326/journal.json - String path = FileUtils.getRelativePath(dir, journal); - String tid = path.split("/")[1]; - long ticketId = Long.parseLong(tid); - if (ticketId > lastId.get()) { - lastId.set(ticketId); + Set<Long> ids = getIds(repository); + for (long id : ids) { + if (id > lastId.get()) { + lastId.set(id); } } } @@ -284,8 +303,7 @@ public class FileTicketService extends ITicketService { } /** - * Retrieves the ticket from the repository by first looking-up the changeId - * associated with the ticketId. + * Retrieves the ticket from the repository. * * @param repository * @param ticketId @@ -313,6 +331,28 @@ public class FileTicketService extends ITicketService { } /** + * Retrieves the journal for the ticket. + * + * @param repository + * @param ticketId + * @return a journal, if it exists, otherwise null + */ + @Override + protected List<Change> getJournalImpl(RepositoryModel repository, long ticketId) { + Repository db = repositoryManager.getRepository(repository.name); + try { + List<Change> changes = getJournal(db, ticketId); + if (ArrayUtils.isEmpty(changes)) { + log.warn("Empty journal for {}:{}", repository, ticketId); + return null; + } + return changes; + } finally { + db.close(); + } + } + + /** * Returns the journal for the specified ticket. * * @param db diff --git a/src/main/java/com/gitblit/tickets/ITicketService.java b/src/main/java/com/gitblit/tickets/ITicketService.java index c2f3283e..668d0bcc 100644 --- a/src/main/java/com/gitblit/tickets/ITicketService.java +++ b/src/main/java/com/gitblit/tickets/ITicketService.java @@ -49,6 +49,7 @@ import com.gitblit.models.TicketModel.Field; import com.gitblit.models.TicketModel.Patchset; import com.gitblit.models.TicketModel.Status; import com.gitblit.tickets.TicketIndexer.Lucene; +import com.gitblit.utils.DeepCopier; import com.gitblit.utils.DiffUtils; import com.gitblit.utils.DiffUtils.DiffStat; import com.gitblit.utils.StringUtils; @@ -64,6 +65,8 @@ import com.google.common.cache.CacheBuilder; */ public abstract class ITicketService { + public static final String SETTING_UPDATE_DIFFSTATS = "migration.updateDiffstats"; + private static final String LABEL = "label"; private static final String MILESTONE = "milestone"; @@ -106,6 +109,8 @@ public abstract class ITicketService { private final Map<String, List<TicketMilestone>> milestonesCache; + private final boolean updateDiffstats; + private static class TicketKey { final String repository; final long ticketId; @@ -163,6 +168,8 @@ public abstract class ITicketService { this.labelsCache = new ConcurrentHashMap<String, List<TicketLabel>>(); this.milestonesCache = new ConcurrentHashMap<String, List<TicketMilestone>>(); + + this.updateDiffstats = settings.getBoolean(SETTING_UPDATE_DIFFSTATS, true); } /** @@ -244,6 +251,7 @@ public abstract class ITicketService { */ public boolean isAcceptingTicketUpdates(RepositoryModel repository) { return isReady() + && repository.hasCommits && repository.isBare && !repository.isFrozen && !repository.isMirror; @@ -556,9 +564,10 @@ public abstract class ITicketService { public TicketMilestone getMilestone(RepositoryModel repository, String milestone) { for (TicketMilestone ms : getMilestones(repository)) { if (ms.name.equalsIgnoreCase(milestone)) { + TicketMilestone tm = DeepCopier.copy(ms); String q = QueryBuilder.q(Lucene.rid.matches(repository.getRID())).and(Lucene.milestone.matches(milestone)).build(); - ms.tickets = indexer.queryFor(q, 1, 0, Lucene.number.name(), true); - return ms; + tm.tickets = indexer.queryFor(q, 1, 0, Lucene.number.name(), true); + return tm; } } return null; @@ -639,6 +648,22 @@ public abstract class ITicketService { * @since 1.4.0 */ public synchronized boolean renameMilestone(RepositoryModel repository, String oldName, String newName, String createdBy) { + return renameMilestone(repository, oldName, newName, createdBy, true); + } + + /** + * Renames a milestone. + * + * @param repository + * @param oldName + * @param newName + * @param createdBy + * @param notifyOpenTickets + * @return true if successful + * @since 1.6.0 + */ + public synchronized boolean renameMilestone(RepositoryModel repository, String oldName, + String newName, String createdBy, boolean notifyOpenTickets) { if (StringUtils.isEmpty(newName)) { throw new IllegalArgumentException("new milestone can not be empty!"); } @@ -651,7 +676,7 @@ public abstract class ITicketService { config.setString(MILESTONE, newName, STATUS, milestone.status.name()); config.setString(MILESTONE, newName, COLOR, milestone.color); if (milestone.due != null) { - config.setString(MILESTONE, milestone.name, DUE, + config.setString(MILESTONE, newName, DUE, new SimpleDateFormat(DUE_DATE_PATTERN).format(milestone.due)); } config.save(); @@ -663,9 +688,13 @@ public abstract class ITicketService { Change change = new Change(createdBy); change.setField(Field.milestone, newName); TicketModel ticket = updateTicket(repository, qr.number, change); - notifier.queueMailing(ticket); + if (notifyOpenTickets && ticket.isOpen()) { + notifier.queueMailing(ticket); + } + } + if (notifyOpenTickets) { + notifier.sendAll(); } - notifier.sendAll(); return true; } catch (IOException e) { @@ -688,11 +717,27 @@ public abstract class ITicketService { * @since 1.4.0 */ public synchronized boolean deleteMilestone(RepositoryModel repository, String milestone, String createdBy) { + return deleteMilestone(repository, milestone, createdBy, true); + } + + /** + * Deletes a milestone. + * + * @param repository + * @param milestone + * @param createdBy + * @param notifyOpenTickets + * @return true if successful + * @since 1.6.0 + */ + public synchronized boolean deleteMilestone(RepositoryModel repository, String milestone, + String createdBy, boolean notifyOpenTickets) { if (StringUtils.isEmpty(milestone)) { throw new IllegalArgumentException("milestone can not be empty!"); } Repository db = null; try { + TicketMilestone tm = getMilestone(repository, milestone); db = repositoryManager.getRepository(repository.name); StoredConfig config = db.getConfig(); config.unsetSection(MILESTONE, milestone); @@ -700,6 +745,18 @@ public abstract class ITicketService { milestonesCache.remove(repository.name); + TicketNotifier notifier = createNotifier(); + for (QueryResult qr : tm.tickets) { + Change change = new Change(createdBy); + change.setField(Field.milestone, ""); + TicketModel ticket = updateTicket(repository, qr.number, change); + if (notifyOpenTickets && ticket.isOpen()) { + notifier.queueMailing(ticket); + } + } + if (notifyOpenTickets) { + notifier.sendAll(); + } return true; } catch (IOException e) { log.error("failed to delete milestone " + milestone + " in " + repository, e); @@ -712,6 +769,15 @@ public abstract class ITicketService { } /** + * Returns the set of assigned ticket ids in the repository. + * + * @param repository + * @return a set of assigned ticket ids in the repository + * @since 1.6.0 + */ + public abstract Set<Long> getIds(RepositoryModel repository); + + /** * Assigns a new ticket id. * * @param repository @@ -773,7 +839,7 @@ public abstract class ITicketService { ticket = getTicketImpl(repository, ticketId); // if ticket exists if (ticket != null) { - if (ticket.hasPatchsets()) { + if (ticket.hasPatchsets() && updateDiffstats) { Repository r = repositoryManager.getRepository(repository.name); try { Patchset patchset = ticket.getCurrentPatchset(); @@ -806,6 +872,33 @@ public abstract class ITicketService { */ protected abstract TicketModel getTicketImpl(RepositoryModel repository, long ticketId); + + /** + * Returns the journal used to build a ticket. + * + * @param repository + * @param ticketId + * @return the journal for the ticket, if it exists, otherwise null + * @since 1.6.0 + */ + public final List<Change> getJournal(RepositoryModel repository, long ticketId) { + if (hasTicket(repository, ticketId)) { + List<Change> journal = getJournalImpl(repository, ticketId); + return journal; + } + return null; + } + + /** + * Retrieves the ticket journal. + * + * @param repository + * @param ticketId + * @return a ticket, if it exists, otherwise null + * @since 1.6.0 + */ + protected abstract List<Change> getJournalImpl(RepositoryModel repository, long ticketId); + /** * Get the ticket url * diff --git a/src/main/java/com/gitblit/tickets/NullTicketService.java b/src/main/java/com/gitblit/tickets/NullTicketService.java index 749d8018..d410cdd0 100644 --- a/src/main/java/com/gitblit/tickets/NullTicketService.java +++ b/src/main/java/com/gitblit/tickets/NullTicketService.java @@ -17,6 +17,7 @@ package com.gitblit.tickets; import java.util.Collections; import java.util.List; +import java.util.Set; import com.gitblit.manager.INotificationManager; import com.gitblit.manager.IPluginManager; @@ -78,6 +79,11 @@ public class NullTicketService extends ITicketService { } @Override + public synchronized Set<Long> getIds(RepositoryModel repository) { + return Collections.emptySet(); + } + + @Override public synchronized long assignNewId(RepositoryModel repository) { return 0L; } @@ -93,6 +99,11 @@ public class NullTicketService extends ITicketService { } @Override + protected List<Change> getJournalImpl(RepositoryModel repository, long ticketId) { + return null; + } + + @Override public boolean supportsAttachments() { return false; } diff --git a/src/main/java/com/gitblit/tickets/QueryResult.java b/src/main/java/com/gitblit/tickets/QueryResult.java index 9f5d3a55..7a2b1abe 100644 --- a/src/main/java/com/gitblit/tickets/QueryResult.java +++ b/src/main/java/com/gitblit/tickets/QueryResult.java @@ -74,6 +74,14 @@ public class QueryResult implements Serializable { return type != null && Type.Proposal == type; } + public boolean isOpen() { + return !status.isClosed(); + } + + public boolean isClosed() { + return status.isClosed(); + } + public boolean isMerged() { return Status.Merged == status && !StringUtils.isEmpty(mergeSha); } diff --git a/src/main/java/com/gitblit/tickets/RedisTicketService.java b/src/main/java/com/gitblit/tickets/RedisTicketService.java index 2c5b181f..d773b0bd 100644 --- a/src/main/java/com/gitblit/tickets/RedisTicketService.java +++ b/src/main/java/com/gitblit/tickets/RedisTicketService.java @@ -20,6 +20,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; +import java.util.TreeSet; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; @@ -184,6 +185,30 @@ public class RedisTicketService extends ITicketService { return false; } + @Override + public Set<Long> getIds(RepositoryModel repository) { + Set<Long> ids = new TreeSet<Long>(); + Jedis jedis = pool.getResource(); + try {// account for migrated tickets + Set<String> keys = jedis.keys(key(repository, KeyType.journal, "*")); + for (String tkey : keys) { + // {repo}:journal:{id} + String id = tkey.split(":")[2]; + long ticketId = Long.parseLong(id); + ids.add(ticketId); + } + } catch (JedisException e) { + log.error("failed to assign new ticket id in Redis @ " + getUrl(), e); + pool.returnBrokenResource(jedis); + jedis = null; + } finally { + if (jedis != null) { + pool.returnResource(jedis); + } + } + return ids; + } + /** * Assigns a new ticket id. * @@ -197,7 +222,14 @@ public class RedisTicketService extends ITicketService { String key = key(repository, KeyType.counter, null); String val = jedis.get(key); if (isNull(val)) { - jedis.set(key, "0"); + long lastId = 0; + Set<Long> ids = getIds(repository); + for (long id : ids) { + if (id > lastId) { + lastId = id; + } + } + jedis.set(key, "" + lastId); } long ticketNumber = jedis.incr(key); return ticketNumber; @@ -273,8 +305,7 @@ public class RedisTicketService extends ITicketService { } /** - * Retrieves the ticket from the repository by first looking-up the changeId - * associated with the ticketId. + * Retrieves the ticket from the repository. * * @param repository * @param ticketId @@ -312,6 +343,39 @@ public class RedisTicketService extends ITicketService { } /** + * Retrieves the journal for the ticket. + * + * @param repository + * @param ticketId + * @return a journal, if it exists, otherwise null + */ + @Override + protected List<Change> getJournalImpl(RepositoryModel repository, long ticketId) { + Jedis jedis = pool.getResource(); + if (jedis == null) { + return null; + } + + try { + List<Change> changes = getJournal(jedis, repository, ticketId); + if (ArrayUtils.isEmpty(changes)) { + log.warn("Empty journal for {}:{}", repository, ticketId); + return null; + } + return changes; + } catch (JedisException e) { + log.error("failed to retrieve journal from Redis @ " + getUrl(), e); + pool.returnBrokenResource(jedis); + jedis = null; + } finally { + if (jedis != null) { + pool.returnResource(jedis); + } + } + return null; + } + + /** * Returns the journal for the specified ticket. * * @param repository diff --git a/src/main/java/com/gitblit/tickets/TicketLabel.java b/src/main/java/com/gitblit/tickets/TicketLabel.java index 686ce88b..a7f0ebec 100644 --- a/src/main/java/com/gitblit/tickets/TicketLabel.java +++ b/src/main/java/com/gitblit/tickets/TicketLabel.java @@ -30,14 +30,17 @@ public class TicketLabel implements Serializable { private static final long serialVersionUID = 1L; - public final String name; + public String name; public String color; public List<QueryResult> tickets; - public TicketLabel(String name) { + setName(name); + } + + public void setName(String name) { this.name = name; this.color = StringUtils.getColor(name); } diff --git a/src/main/java/com/gitblit/tickets/TicketMilestone.java b/src/main/java/com/gitblit/tickets/TicketMilestone.java index c6b4fcca..dacedda6 100644 --- a/src/main/java/com/gitblit/tickets/TicketMilestone.java +++ b/src/main/java/com/gitblit/tickets/TicketMilestone.java @@ -38,6 +38,18 @@ public class TicketMilestone extends TicketLabel { status = Status.Open; } + public boolean isOpen() { + return status == Status.Open; + } + + public boolean isOverdue() { + return due == null ? false : System.currentTimeMillis() > due.getTime(); + } + + public void setDue(Date due) { + this.due = due; + } + public int getProgress() { int total = getTotalTickets(); if (total == 0) { diff --git a/src/main/java/com/gitblit/tickets/TicketNotifier.java b/src/main/java/com/gitblit/tickets/TicketNotifier.java index 9a5e4e1d..07371b1b 100644 --- a/src/main/java/com/gitblit/tickets/TicketNotifier.java +++ b/src/main/java/com/gitblit/tickets/TicketNotifier.java @@ -545,7 +545,6 @@ public class TicketNotifier { } } } - mailing.setRecipients(toAddresses); // // CC recipients @@ -554,7 +553,7 @@ public class TicketNotifier { // repository owners if (!ArrayUtils.isEmpty(repository.owners)) { - tos.addAll(repository.owners); + ccs.addAll(repository.owners); } // cc users mentioned in last comment @@ -595,6 +594,14 @@ public class TicketNotifier { } ccAddresses.addAll(settings.getStrings(Keys.mail.mailingLists)); + // respect the author's email preference + UserModel lastAuthor = userManager.getUserModel(lastChange.author); + if (!lastAuthor.getPreferences().isEmailMeOnMyTicketChanges()) { + toAddresses.remove(lastAuthor.emailAddress); + ccAddresses.remove(lastAuthor.emailAddress); + } + + mailing.setRecipients(toAddresses); mailing.setCCs(ccAddresses); } diff --git a/src/main/java/com/gitblit/transport/ssh/SshDaemon.java b/src/main/java/com/gitblit/transport/ssh/SshDaemon.java index 7c512907..261daa66 100644 --- a/src/main/java/com/gitblit/transport/ssh/SshDaemon.java +++ b/src/main/java/com/gitblit/transport/ssh/SshDaemon.java @@ -142,7 +142,7 @@ public class SshDaemon { public String formatUrl(String gituser, String servername, String repository) { if (sshd.getPort() == DEFAULT_PORT) { // standard port - return MessageFormat.format("{0}@{1}/{2}", gituser, servername, + return MessageFormat.format("ssh://{0}@{1}/{2}", gituser, servername, repository); } else { // non-standard port diff --git a/src/main/java/com/gitblit/utils/JGitUtils.java b/src/main/java/com/gitblit/utils/JGitUtils.java index 190872a7..da51ea98 100644 --- a/src/main/java/com/gitblit/utils/JGitUtils.java +++ b/src/main/java/com/gitblit/utils/JGitUtils.java @@ -1668,6 +1668,24 @@ public class JGitUtils { }
/**
+ * Returns the list of tags in the repository. If repository does not exist
+ * or is empty, an empty list is returned.
+ *
+ * @param repository
+ * @param fullName
+ * if true, /refs/tags/yadayadayada is returned. If false,
+ * yadayadayada is returned.
+ * @param maxCount
+ * if < 0, all tags are returned
+ * @param offset
+ * if maxCount provided sets the starting point of the records to return
+ * @return list of tags
+ */
+ public static List<RefModel> getTags(Repository repository, boolean fullName, int maxCount, int offset) {
+ return getRefs(repository, Constants.R_TAGS, fullName, maxCount, offset);
+ }
+
+ /**
* Returns the list of local branches in the repository. If repository does
* not exist or is empty, an empty list is returned.
*
@@ -1748,6 +1766,27 @@ public class JGitUtils { */
private static List<RefModel> getRefs(Repository repository, String refs, boolean fullName,
int maxCount) {
+ return getRefs(repository, refs, fullName, maxCount, 0);
+ }
+
+ /**
+ * Returns a list of references in the repository matching "refs". If the
+ * repository is null or empty, an empty list is returned.
+ *
+ * @param repository
+ * @param refs
+ * if unspecified, all refs are returned
+ * @param fullName
+ * if true, /refs/something/yadayadayada is returned. If false,
+ * yadayadayada is returned.
+ * @param maxCount
+ * if < 0, all references are returned
+ * @param offset
+ * if maxCount provided sets the starting point of the records to return
+ * @return list of references
+ */
+ private static List<RefModel> getRefs(Repository repository, String refs, boolean fullName,
+ int maxCount, int offset) {
List<RefModel> list = new ArrayList<RefModel>();
if (maxCount == 0) {
return list;
@@ -1771,7 +1810,14 @@ public class JGitUtils { Collections.sort(list);
Collections.reverse(list);
if (maxCount > 0 && list.size() > maxCount) {
- list = new ArrayList<RefModel>(list.subList(0, maxCount));
+ if (offset < 0) {
+ offset = 0;
+ }
+ int endIndex = offset + maxCount;
+ if (endIndex > list.size()) {
+ endIndex = list.size();
+ }
+ list = new ArrayList<RefModel>(list.subList(offset, endIndex));
}
} catch (IOException e) {
error(e, repository, "{0} failed to retrieve {1}", refs);
diff --git a/src/main/java/com/gitblit/utils/MarkdownUtils.java b/src/main/java/com/gitblit/utils/MarkdownUtils.java index da0db79d..2ebfdb26 100644 --- a/src/main/java/com/gitblit/utils/MarkdownUtils.java +++ b/src/main/java/com/gitblit/utils/MarkdownUtils.java @@ -136,7 +136,7 @@ public class MarkdownUtils { String canonicalUrl = settings.getString(Keys.web.canonicalUrl, "https://localhost:8443");
// emphasize and link mentions
- String mentionReplacement = String.format(" **<a href=\"%1s/user/$1\">@$1</a>**", canonicalUrl);
+ String mentionReplacement = String.format(" **[@$1](%1s/user/$1)**", canonicalUrl);
text = text.replaceAll("\\s@([A-Za-z0-9-_]+)", mentionReplacement);
// link ticket refs @@ -146,7 +146,7 @@ public class MarkdownUtils { // link commit shas
int shaLen = settings.getInteger(Keys.web.shortCommitIdLength, 6);
String commitPattern = MessageFormat.format("\\s([A-Fa-f0-9]'{'{0}'}')([A-Fa-f0-9]'{'{1}'}')", shaLen, 40 - shaLen);
- String commitReplacement = String.format(" <a class='commit' href='%1$s/commit?r=%2$s&h=$1$2'>$1</a>", canonicalUrl, repositoryName);
+ String commitReplacement = String.format(" [`$1`](%1$s/commit?r=%2$s&h=$1$2)", canonicalUrl, repositoryName);
text = text.replaceAll(commitPattern, commitReplacement);
String html = transformMarkdown(text);
diff --git a/src/main/java/com/gitblit/utils/RpcUtils.java b/src/main/java/com/gitblit/utils/RpcUtils.java index 5e577fb6..82202154 100644 --- a/src/main/java/com/gitblit/utils/RpcUtils.java +++ b/src/main/java/com/gitblit/utils/RpcUtils.java @@ -203,7 +203,21 @@ public class RpcUtils { }
- /**
+ /**
+ * Create a fork of a repository.
+ *
+ * @param repository
+
+ * @return true if the action succeeded
+ * @throws IOException
+ */
+ public static boolean forkRepository(RepositoryModel repository, String serverUrl,
+ String account, char[] password) throws IOException {
+ return doAction(RpcRequest.FORK_REPOSITORY, repository.name, null, serverUrl, account, password);
+ }
+
+
+ /**
* Send a revised version of the repository model to the Gitblit server.
*
* @param repository
diff --git a/src/main/java/com/gitblit/utils/SyndicationUtils.java b/src/main/java/com/gitblit/utils/SyndicationUtils.java index 2ee1cf69..93e9321a 100644 --- a/src/main/java/com/gitblit/utils/SyndicationUtils.java +++ b/src/main/java/com/gitblit/utils/SyndicationUtils.java @@ -25,6 +25,7 @@ import java.util.ArrayList; import java.util.List;
import com.gitblit.Constants;
+import com.gitblit.Constants.FeedObjectType;
import com.gitblit.GitBlitException;
import com.gitblit.models.FeedEntryModel;
import com.sun.syndication.feed.synd.SyndCategory;
@@ -137,6 +138,59 @@ public class SyndicationUtils { */
public static List<FeedEntryModel> readFeed(String url, String repository, String branch,
int numberOfEntries, int page, String username, char[] password) throws IOException {
+ return readFeed(url, repository, branch, FeedObjectType.COMMIT, numberOfEntries,
+ page, username, password);
+ }
+
+ /**
+ * Reads tags from the specified repository.
+ *
+ * @param url
+ * the url of the Gitblit server
+ * @param repository
+ * the repository name
+ * @param branch
+ * the branch name (optional)
+ * @param numberOfEntries
+ * the number of entries to retrieve. if <= 0 the server default
+ * is used.
+ * @param page
+ * 0-indexed. used to paginate the results.
+ * @param username
+ * @param password
+ * @return a list of SyndicationModel entries
+ * @throws {@link IOException}
+ */
+ public static List<FeedEntryModel> readTags(String url, String repository,
+ int numberOfEntries, int page, String username, char[] password) throws IOException {
+ return readFeed(url, repository, null, FeedObjectType.TAG, numberOfEntries,
+ page, username, password);
+ }
+
+ /**
+ * Reads a Gitblit RSS feed.
+ *
+ * @param url
+ * the url of the Gitblit server
+ * @param repository
+ * the repository name
+ * @param branch
+ * the branch name (optional)
+ * @param objectType
+ * the object type to return (optional, COMMIT assummed)
+ * @param numberOfEntries
+ * the number of entries to retrieve. if <= 0 the server default
+ * is used.
+ * @param page
+ * 0-indexed. used to paginate the results.
+ * @param username
+ * @param password
+ * @return a list of SyndicationModel entries
+ * @throws {@link IOException}
+ */
+ private static List<FeedEntryModel> readFeed(String url, String repository, String branch,
+ FeedObjectType objectType, int numberOfEntries, int page, String username,
+ char[] password) throws IOException {
// build feed url
List<String> parameters = new ArrayList<String>();
if (numberOfEntries > 0) {
@@ -148,6 +202,9 @@ public class SyndicationUtils { if (!StringUtils.isEmpty(branch)) {
parameters.add("h=" + branch);
}
+ if (objectType != null) {
+ parameters.add("ot=" + objectType.name());
+ }
return readFeed(url, parameters, repository, branch, username, password);
}
diff --git a/src/main/java/com/gitblit/wicket/GitBlitWebApp.java b/src/main/java/com/gitblit/wicket/GitBlitWebApp.java index c4fdeda5..f63ff3d9 100644 --- a/src/main/java/com/gitblit/wicket/GitBlitWebApp.java +++ b/src/main/java/com/gitblit/wicket/GitBlitWebApp.java @@ -25,11 +25,16 @@ import org.apache.wicket.Application; import org.apache.wicket.Request; import org.apache.wicket.Response; import org.apache.wicket.Session; +import org.apache.wicket.application.IClassResolver; import org.apache.wicket.markup.html.WebPage; import org.apache.wicket.protocol.http.WebApplication; +import ro.fortsoft.pf4j.PluginState; +import ro.fortsoft.pf4j.PluginWrapper; + import com.gitblit.IStoredSettings; import com.gitblit.Keys; +import com.gitblit.extensions.GitblitWicketPlugin; import com.gitblit.manager.IAuthenticationManager; import com.gitblit.manager.IFederationManager; import com.gitblit.manager.IGitblit; @@ -51,6 +56,8 @@ import com.gitblit.wicket.pages.CommitPage; import com.gitblit.wicket.pages.ComparePage; import com.gitblit.wicket.pages.DocPage; import com.gitblit.wicket.pages.DocsPage; +import com.gitblit.wicket.pages.EditMilestonePage; +import com.gitblit.wicket.pages.EditRepositoryPage; import com.gitblit.wicket.pages.EditTicketPage; import com.gitblit.wicket.pages.ExportTicketPage; import com.gitblit.wicket.pages.FederationRegistrationPage; @@ -63,27 +70,32 @@ import com.gitblit.wicket.pages.LogoutPage; import com.gitblit.wicket.pages.LuceneSearchPage; import com.gitblit.wicket.pages.MetricsPage; import com.gitblit.wicket.pages.MyDashboardPage; +import com.gitblit.wicket.pages.MyTicketsPage; +import com.gitblit.wicket.pages.NewMilestonePage; +import com.gitblit.wicket.pages.NewRepositoryPage; import com.gitblit.wicket.pages.NewTicketPage; import com.gitblit.wicket.pages.OverviewPage; import com.gitblit.wicket.pages.PatchPage; import com.gitblit.wicket.pages.ProjectPage; import com.gitblit.wicket.pages.ProjectsPage; -import com.gitblit.wicket.pages.RawPage; import com.gitblit.wicket.pages.ReflogPage; import com.gitblit.wicket.pages.RepositoriesPage; import com.gitblit.wicket.pages.ReviewProposalPage; import com.gitblit.wicket.pages.SummaryPage; import com.gitblit.wicket.pages.TagPage; import com.gitblit.wicket.pages.TagsPage; +import com.gitblit.wicket.pages.TeamsPage; import com.gitblit.wicket.pages.TicketsPage; import com.gitblit.wicket.pages.TreePage; import com.gitblit.wicket.pages.UserPage; import com.gitblit.wicket.pages.UsersPage; -public class GitBlitWebApp extends WebApplication { +public class GitBlitWebApp extends WebApplication implements GitblitWicketApp { private final Class<? extends WebPage> homePageClass = MyDashboardPage.class; + private final Class<? extends WebPage> newRepositoryPageClass = NewRepositoryPage.class; + private final Map<String, CacheControl> cacheablePages = new HashMap<String, CacheControl>(); private final IStoredSettings settings; @@ -170,7 +182,6 @@ public class GitBlitWebApp extends WebApplication { mount("/tag", TagPage.class, "r", "h"); mount("/tree", TreePage.class, "r", "h", "f"); mount("/blob", BlobPage.class, "r", "h", "f"); - mount("/raw", RawPage.class, "r", "h", "f"); mount("/blobdiff", BlobDiffPage.class, "r", "h", "f"); mount("/commitdiff", CommitDiffPage.class, "r", "h"); mount("/compare", ComparePage.class, "r", "h"); @@ -180,6 +191,7 @@ public class GitBlitWebApp extends WebApplication { mount("/metrics", MetricsPage.class, "r"); mount("/blame", BlamePage.class, "r", "h", "f"); mount("/users", UsersPage.class); + mount("/teams", TeamsPage.class); mount("/logout", LogoutPage.class); // setup ticket urls @@ -187,6 +199,9 @@ public class GitBlitWebApp extends WebApplication { mount("/tickets/new", NewTicketPage.class, "r"); mount("/tickets/edit", EditTicketPage.class, "r", "h"); mount("/tickets/export", ExportTicketPage.class, "r", "h"); + mount("/milestones/new", NewMilestonePage.class, "r"); + mount("/milestones/edit", EditMilestonePage.class, "r", "h"); + mount("/mytickets", MyTicketsPage.class, "r", "h"); // setup the markup document urls mount("/docs", DocsPage.class, "r"); @@ -196,6 +211,8 @@ public class GitBlitWebApp extends WebApplication { mount("/proposal", ReviewProposalPage.class, "t"); mount("/registration", FederationRegistrationPage.class, "u", "n"); + mount("/new", NewRepositoryPage.class); + mount("/edit", EditRepositoryPage.class, "r"); mount("/activity", ActivityPage.class, "r", "h"); mount("/lucene", LuceneSearchPage.class); mount("/project", ProjectPage.class, "p"); @@ -204,11 +221,30 @@ public class GitBlitWebApp extends WebApplication { mount("/forks", ForksPage.class, "r"); mount("/fork", ForkPage.class, "r"); + // allow started Wicket plugins to initialize + for (PluginWrapper pluginWrapper : pluginManager.getPlugins()) { + if (PluginState.STARTED != pluginWrapper.getPluginState()) { + continue; + } + if (pluginWrapper.getPlugin() instanceof GitblitWicketPlugin) { + GitblitWicketPlugin wicketPlugin = (GitblitWicketPlugin) pluginWrapper.getPlugin(); + wicketPlugin.init(this); + } + } + + // customize the Wicket class resolver to load from plugins + IClassResolver coreResolver = getApplicationSettings().getClassResolver(); + PluginClassResolver classResolver = new PluginClassResolver(coreResolver, pluginManager); + getApplicationSettings().setClassResolver(classResolver); + getMarkupSettings().setDefaultMarkupEncoding("UTF-8"); - super.init(); } - private void mount(String location, Class<? extends WebPage> clazz, String... parameters) { + /* (non-Javadoc) + * @see com.gitblit.wicket.Webapp#mount(java.lang.String, java.lang.Class, java.lang.String) + */ + @Override + public void mount(String location, Class<? extends WebPage> clazz, String... parameters) { if (parameters == null) { parameters = new String[] {}; } @@ -224,15 +260,30 @@ public class GitBlitWebApp extends WebApplication { } } + /* (non-Javadoc) + * @see com.gitblit.wicket.Webapp#getHomePage() + */ @Override public Class<? extends WebPage> getHomePage() { return homePageClass; } + public Class<? extends WebPage> getNewRepositoryPage() { + return newRepositoryPageClass; + } + + /* (non-Javadoc) + * @see com.gitblit.wicket.Webapp#isCacheablePage(java.lang.String) + */ + @Override public boolean isCacheablePage(String mountPoint) { return cacheablePages.containsKey(mountPoint); } + /* (non-Javadoc) + * @see com.gitblit.wicket.Webapp#getCacheControl(java.lang.String) + */ + @Override public CacheControl getCacheControl(String mountPoint) { return cacheablePages.get(mountPoint); } @@ -248,15 +299,18 @@ public class GitBlitWebApp extends WebApplication { return gitBlitWebSession; } + /* (non-Javadoc) + * @see com.gitblit.wicket.Webapp#settings() + */ + @Override public IStoredSettings settings() { return settings; } - /** - * Is Gitblit running in debug mode? - * - * @return true if Gitblit is running in debug mode + /* (non-Javadoc) + * @see com.gitblit.wicket.Webapp#isDebugMode() */ + @Override public boolean isDebugMode() { return runtimeManager.isDebugMode(); } @@ -265,58 +319,114 @@ public class GitBlitWebApp extends WebApplication { * These methods look strange... and they are... but they are the first * step towards modularization across multiple commits. */ + /* (non-Javadoc) + * @see com.gitblit.wicket.Webapp#getBootDate() + */ + @Override public Date getBootDate() { return runtimeManager.getBootDate(); } + /* (non-Javadoc) + * @see com.gitblit.wicket.Webapp#getLastActivityDate() + */ + @Override public Date getLastActivityDate() { return repositoryManager.getLastActivityDate(); } + /* (non-Javadoc) + * @see com.gitblit.wicket.Webapp#runtime() + */ + @Override public IRuntimeManager runtime() { return runtimeManager; } + /* (non-Javadoc) + * @see com.gitblit.wicket.Webapp#plugins() + */ + @Override public IPluginManager plugins() { return pluginManager; } + /* (non-Javadoc) + * @see com.gitblit.wicket.Webapp#notifier() + */ + @Override public INotificationManager notifier() { return notificationManager; } + /* (non-Javadoc) + * @see com.gitblit.wicket.Webapp#users() + */ + @Override public IUserManager users() { return userManager; } + /* (non-Javadoc) + * @see com.gitblit.wicket.Webapp#authentication() + */ + @Override public IAuthenticationManager authentication() { return authenticationManager; } + /* (non-Javadoc) + * @see com.gitblit.wicket.Webapp#keys() + */ + @Override public IPublicKeyManager keys() { return publicKeyManager; } + /* (non-Javadoc) + * @see com.gitblit.wicket.Webapp#repositories() + */ + @Override public IRepositoryManager repositories() { return repositoryManager; } + /* (non-Javadoc) + * @see com.gitblit.wicket.Webapp#projects() + */ + @Override public IProjectManager projects() { return projectManager; } + /* (non-Javadoc) + * @see com.gitblit.wicket.Webapp#federation() + */ + @Override public IFederationManager federation() { return federationManager; } + /* (non-Javadoc) + * @see com.gitblit.wicket.Webapp#gitblit() + */ + @Override public IGitblit gitblit() { return gitblit; } + /* (non-Javadoc) + * @see com.gitblit.wicket.Webapp#tickets() + */ + @Override public ITicketService tickets() { return gitblit.getTicketService(); } + /* (non-Javadoc) + * @see com.gitblit.wicket.Webapp#getTimezone() + */ + @Override public TimeZone getTimezone() { return runtimeManager.getTimezone(); } diff --git a/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties b/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties index aeb2d9ef..eb92e2d2 100644 --- a/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties +++ b/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties @@ -133,7 +133,7 @@ gb.sendProposal = propose gb.status = status gb.origin = origin gb.headRef = default branch (HEAD) -gb.headRefDescription = change the ref that HEAD links to. e.g. refs/heads/master +gb.headRefDescription = The default branch that will be cloned and displayed on the Summary page. gb.federationStrategy = federation strategy gb.federationRegistration = federation registration gb.federationResults = federation pull results @@ -223,8 +223,8 @@ gb.queryResults = results {0} - {1} ({2} hits) gb.noHits = no hits gb.authored = authored gb.committed = committed -gb.indexedBranches = indexed branches -gb.indexedBranchesDescription = select the branches to include in your Lucene index +gb.indexedBranches = Indexed Branches +gb.indexedBranchesDescription = Select the branches to be indexed by Lucene gb.noIndexedRepositoriesWarning = none of your repositories are configured for Lucene indexing gb.undefinedQueryWarning = query is undefined! gb.noSelectedRepositoriesWarning = please select one or more repositories! @@ -671,4 +671,74 @@ gb.serverDoesNotAcceptPatchsets = This server does not accept patchsets. gb.ticketIsClosed = This ticket is closed. gb.mergeToDescription = default integration branch for merging ticket patchsets gb.anonymousCanNotPropose = Anonymous users can not propose patchsets. -gb.youDoNotHaveClonePermission = You are not permitted to clone this repository.
\ No newline at end of file +gb.youDoNotHaveClonePermission = You are not permitted to clone this repository. +gb.myTickets = my tickets +gb.yourAssignedTickets = assigned to you +gb.newMilestone = new milestone +gb.editMilestone = edit milestone +gb.deleteMilestone = Delete milestone \"{0}\"? +gb.milestoneDeleteFailed = Failed to delete milestone ''{0}''! +gb.notifyChangedOpenTickets = send notification for changed open tickets +gb.overdue = overdue +gb.openMilestones = open milestones +gb.closedMilestones = closed milestones +gb.administration = administration +gb.plugins = plugins +gb.extensions = extensions +gb.pleaseSelectProject = Please select the project! +gb.accessPolicy = Access Policy +gb.accessPolicyDescription = Choose an access policy to control repository visibility and git permissions. +gb.anonymousPolicy = Anonymous View, Clone, & Push +gb.anonymousPolicyDescription = Anyone can see, clone, and push to this repository. +gb.authenticatedPushPolicy = Restrict Push (Authenticated) +gb.authenticatedPushPolicyDescription = Anyone can see and clone this repository. All authenticated users have RW+ push permission. +gb.namedPushPolicy = Restrict Push (Named) +gb.namedPushPolicyDescription = Anyone can see and clone this repository. You choose who can push. +gb.clonePolicy = Restrict Clone & Push +gb.clonePolicyDescription = Anyone can see this repository. You choose who can clone and push. +gb.viewPolicy = Restrict View, Clone, & Push +gb.viewPolicyDescription = You choose who can see, clone, and push to this repository. +gb.initialCommit = Initial Commit +gb.initialCommitDescription = This will allow you to <code>git clone</code> this repository immediately. Skip this step if you have already run <code>git init</code> locally. +gb.initWithReadme = Include a README +gb.initWithReadmeDescription = This will generate a simple README document for your repository. +gb.initWithGitignore = Include a .gitignore file +gb.initWithGitignoreDescription = This will insert a config file that instructs your Git clients to ignore files or directories that match defined patterns. +gb.pleaseSelectGitIgnore = Please select a .gitignore file +gb.receive = receive +gb.permissions = permissions +gb.ownersDescription = Owners can manage all repository settings but they are not allowed to rename a repository unless it is their personal repository. +gb.userPermissionsDescription = You can specify individual user permissions. These settings will override team or regex permissions. +gb.teamPermissionsDescription = You can specify individual team permissions. These settings will override regex permissions. +gb.ticketSettings = Ticket Settings +gb.receiveSettings = Receive Settings +gb.receiveSettingsDescription = The receive settings control pushes to the repository. +gb.preReceiveDescription = Pre-receive hooks are executed after commits are received but <em>BEFORE</em> the refs are updated.<p>This is the appropriate hook for rejecting a push.</p> +gb.postReceiveDescription = Post-receive hooks are executed after commits are received but <em>AFTER</em> the refs are updated.<p>This is the appropriate hook for notifications, build triggers, etc.</p> +gb.federationStrategyDescription = Control if and how to federate this repository with another Gitblit. +gb.federationSetsDescription = This repository will be included in the selected federation sets. +gb.miscellaneous = miscellaneous +gb.originDescription = The url from which this repository was cloned. +gb.gc = GC +gb.garbageCollection = Garbage Collection +gb.garbageCollectionDescription = The garbage collector will pack loose objects pushed from clients and will remove unreferenced objects from the repository. +gb.commitMessageRendererDescription = Commit messages can be displayed as plaintext or as rendered markup. +gb.preferences = preferences +gb.accountPreferences = Account Preferences +gb.accountPreferencesDescription = Specify your account preferences +gb.languagePreference = Language Preference +gb.languagePreferenceDescription = Select your preferred translation for Gitblit +gb.emailMeOnMyTicketChanges = Email me on my ticket changes +gb.emailMeOnMyTicketChangesDescription = Send me an email notification for changes that I make to a ticket +gb.displayNameDescription = The preferred name for display +gb.emailAddressDescription = The primary email address for receiving notifications +gb.sshKeys = SSH Keys +gb.sshKeysDescription = SSH public key authentication is a secure alternative to password authentication +gb.addSshKey = Add SSH Key +gb.key = Key +gb.comment = Comment +gb.sshKeyCommentDescription = Enter an optional comment. If blank, the comment will be extracted from the key data. +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 diff --git a/src/main/java/com/gitblit/wicket/GitBlitWebApp_fr.properties b/src/main/java/com/gitblit/wicket/GitBlitWebApp_fr.properties index 8a725cf4..a6d61842 100644 --- a/src/main/java/com/gitblit/wicket/GitBlitWebApp_fr.properties +++ b/src/main/java/com/gitblit/wicket/GitBlitWebApp_fr.properties @@ -670,3 +670,5 @@ gb.repositoryDoesNotAcceptPatchsets = Ce dépôt n'accepte pas de patchsets. gb.serverDoesNotAcceptPatchsets = Ce serveur n'accepte pas de patchsets. gb.ticketIsClosed = Ce ticket est clos. gb.mergeToDescription = default integration branch for merging ticket patchsets +gb.myTickets = mes tickets +gb.yourAssignedTickets = dont vous êtes responsable diff --git a/src/main/java/com/gitblit/wicket/GitBlitWebApp_no.properties b/src/main/java/com/gitblit/wicket/GitBlitWebApp_no.properties index 67920768..efc13a1e 100644 --- a/src/main/java/com/gitblit/wicket/GitBlitWebApp_no.properties +++ b/src/main/java/com/gitblit/wicket/GitBlitWebApp_no.properties @@ -100,6 +100,9 @@ gb.blob = blob gb.commitActivityTrend = aktivitetstrend for commits gb.commitActivityDOW = commit aktivitet gruppert p\u00e5 ukedag gb.commitActivityAuthors = prim\u00e6rforfattere etter aktivitet +gb.feed = feed +gb.cancel = avbryt +gb.changePassword = endre passord gb.isFederated = er federert gb.federateThis = federer dette repositoriet gb.federateOrigin = federere origin @@ -669,3 +672,73 @@ gb.ticketIsClosed = Denne ticketen er lukket gb.mergeToDescription = standard integration branch for merging av ticket patchsett gb.anonymousCanNotPropose = Anonyme brukere kan ikke foresl\u00e5 patchsett gb.youDoNotHaveClonePermission = Du har ikke tillatelse til \u00e5 klone dette repositoriet. +gb.myTickets = mine tickets +gb.yourAssignedTickets = tilordnet deg +gb.newMilestone = ny milep\u00e6l +gb.editMilestone = endre milep\u00e6l +gb.deleteMilestone = Slette milep\u00e6l \"{0}\"? +gb.milestoneDeleteFailed = Greide ikke Ã¥ slette milep\u00e6l ''{0}''! +gb.notifyChangedOpenTickets = send meldin om endrede \u00e5pne tickets +gb.overdue = forfalt +gb.openMilestones = \u00e5pne milep\u00e6ler +gb.closedMilestones = lukkede milep\u00e6ler +gb.administration = administrasjon +gb.plugins = plugins +gb.extensions = extensions +gb.pleaseSelectProject = Vennligst velg prosjekt! +gb.accessPolicy = Tilgangspolicy +gb.accessPolicyDescription = Velg en tilgangspolicy for Ã¥ kontrollere synlighet og tilganger. +gb.anonymousPolicy = Anonym view, clone og push +gb.anonymousPolicyDescription = Alle kan se, clone ofg pushe til dette repositoriet. +gb.authenticatedPushPolicy = Begrens push (Autentisert) +gb.authenticatedPushPolicyDescription = Alle kan se og klone dette repositoriet. Alle autentiserte brukere har lese og skrivetilgang. +gb.namedPushPolicy = Begrens push (navngitt) +gb.namedPushPolicyDescription = Alle kan se og klone repositoriet. Du bestemmer hvem som kan pushe. +gb.clonePolicy = Begrens Clone og Push +gb.clonePolicyDescription = Alle kan se dette repositoriet. Du bestemmer hvem som kan clone og pushe. +gb.viewPolicy = Begrense View, Clone og Push +gb.viewPolicyDescription = Du bestemmer hvem som kan se, clone og pushe til dette repositoriet. +gb.initialCommit = F\u00F8rste commit +gb.initialCommitDescription = Dette vil la deg kunne <code>git clone</code> repositoriet med en gang. Hopp over dette skrittet hvis du allerede har kj\u0F8rt if <code>git init</code> lokalt. +gb.initWithReadme = Inkluder en README +gb.initWithReadmeDescription = Dette vil generere en enkel README-fil i ditt repository. +gb.initWithGitignore = Inkluder en .gitignore fil +gb.initWithGitignoreDescription = Dette vil inkludere en konfigurasjonsfil som instruerer git-klienter til Ã¥ ignorerer filer og kataloger som matcher definerte m\u00F8nstre. +gb.pleaseSelectGitIgnore = Vennligst velg en .gitignore fil +gb.receive = motta +gb.permissions = rettigheter +gb.ownersDescription = Eiere kan administrere alle repositoriets innstillinger men de kan ikke endre p\u00e5 repositoriets navn med mindre det er deres eget repository. +gb.userPermissionsDescription = Du kan spesifisere individuelle brukerrettigheter. Disse innstillingene overstyrer team- eller regul\u00e6ruttrykk-baserte rettigheter. +gb.teamPermissionsDescription = Du kan spesifisere individuelle team-rettigheter. Disse innstillingene overstyrer regul\u00e6ruttrykk-baserte rettigheter.. +gb.ticketSettings = Ticket innstillinger +gb.receiveSettings = Innstillinger for mottak av push +gb.receiveSettingsDescription = Kontrollerer pushing til repositoriet. +gb.preReceiveDescription = Pre-receive hooks blir utf\u00F8rt etter en committ er mottatt men <em>F\u00D8</em> refs har blitt oppdatert.<p>Dette er det passende stedet \u00e5 avvise en push.</p> +gb.postReceiveDescription = Post-receive hooks blir utf\u00F8rt etter en committ er mottatt og <em>ETTER</em> refs har blitt oppdatert.<p>Dette er det passende stedet for notifikasjoner, build triggers osv.</p> +gb.federationStrategyDescription = Kontroller om og hvordan dette repositoriet skal federeres med en annen Gitblit instans. +gb.federationSetsDescription = Dette repositoriet vil bli inkludert i de valgte federeringssettene. +gb.miscellaneous = ymse +gb.originDescription = URL'en som dette repositoriet ble klonet fra. +gb.gc = GC +gb.garbageCollection = Garbage Collection +gb.garbageCollectionDescription = Garbage collectoren vil pakke sammen l\u00F8se objekter pushet fra klientene og fjerne objekter det ikke refereres til fra minnet. +gb.commitMessageRendererDescription = Commit-meldinger kan vises som ren tekst, eller som markup. +gb.preferences = innstillinger +gb.accountPreferences = Kontoinnstillinger +gb.accountPreferencesDescription = Velg dine kontoinnstillinger +gb.languagePreference = Foretrukket spr\u00e5k +gb.languagePreferenceDescription = Velg ditt foretrukne spr\u00e5k for Gitblit +gb.emailMeOnMyTicketChanges = Send meg en epost nÃ¥r ticketen min endrer seg +gb.emailMeOnMyTicketChangesDescription = Send meg en epost for endringer jeg gjør pÃ¥ en ticket. +gb.displayNameDescription = Foretrukket visningsnavn +gb.emailAddressDescription = Prim\u00e6r epostadresse for notifikasjoner +gb.sshKeys = SSH n\u00F8kler +gb.sshKeysDescription = SSH public key autentisering er et sikkert alternativ til autentisering med passord +gb.addSshKey = Legg til SSH n\u00F8kkel +gb.key = N\u00F8kkel +gb.comment = Kommentar +gb.sshKeyCommentDescription = Angi en valgfri kommentar. Kommentaren vil bli ekstrahert fra n\u00F8kkeldataene hvis blank +gb.permission = Tilgang +gb.sshKeyPermissionDescription = Angi tilgangsrettinghet for SSH n\u00F8kkelen +gb.transportPreference = Foretrukket transport +gb.transportPreferenceDescription = Sett transportmetoden du foretrekker for cloning
\ No newline at end of file diff --git a/src/main/java/com/gitblit/wicket/GitBlitWebApp_zh_CN.properties b/src/main/java/com/gitblit/wicket/GitBlitWebApp_zh_CN.properties index 5976614b..b5e4f658 100644 --- a/src/main/java/com/gitblit/wicket/GitBlitWebApp_zh_CN.properties +++ b/src/main/java/com/gitblit/wicket/GitBlitWebApp_zh_CN.properties @@ -669,4 +669,76 @@ gb.repositoryIsFrozen = \u5f53\u524d\u7248\u672c\u5e93\u5df2\u88ab\u51bb\u7ed3\u gb.repositoryDoesNotAcceptPatchsets = \u5f53\u524d\u7248\u672c\u5e93\u4e0d\u5141\u8bb8\u8865\u4e01\u96c6\u3002 gb.serverDoesNotAcceptPatchsets = \u5f53\u524d\u670d\u52a1\u5668\u4e0d\u5141\u8bb8\u8865\u4e01\u96c6\u3002 gb.ticketIsClosed = \u5f53\u524d\u5de5\u5355\u5df2\u5173\u95ed\u3002 -gb.mergeToDescription = \u5408\u5e76\u5de5\u5355\u8865\u4e01\u96c6\u7684\u9ed8\u8ba4\u96c6\u6210\u5206\u652f
\ No newline at end of file +gb.mergeToDescription = \u5408\u5e76\u5de5\u5355\u8865\u4e01\u96c6\u7684\u9ed8\u8ba4\u96c6\u6210\u5206\u652f +gb.anonymousCanNotPropose = \u7981\u6b62\u533f\u540d\u7528\u6237\u63d0\u4ea4\u8865\u4e01\u96c6\u3002 +gb.youDoNotHaveClonePermission = \u60a8\u6ca1\u6709\u6743\u9650\u514b\u9686\u5f53\u524d\u7248\u672c\u5e93\u3002 +gb.myTickets = \u6211\u7684\u5de5\u5355 +gb.yourAssignedTickets = \u8d23\u4efb\u5de5\u5355 +gb.newMilestone = \u65b0\u5efa milestone +gb.editMilestone = \u7f16\u8f91 milestone +gb.deleteMilestone = \u5220\u9664 milestone \\"{0}\\"? +gb.milestoneDeleteFailed = \u5220\u9664 milestone ''{0}'' \u5931\u8d25! +gb.notifyChangedOpenTickets = \u5bf9\u53d1\u751f\u53d8\u52a8\u7684\u5df2\u5f00\u542f\u5de5\u5355\u53d1\u9001\u901a\u77e5 +gb.overdue = \u8fc7\u671f +gb.openMilestones = \u5df2\u5f00\u542f milestones +gb.closedMilestones = \u5df2\u5173\u95ed milestones +gb.administration = \u7ba1\u7406 +gb.plugins = \u63d2\u4ef6 +gb.extensions = \u6269\u5c55 +gb.pleaseSelectProject = \u8bf7\u9009\u62e9\u9879\u76ee! +gb.accessPolicy = \u8bbf\u95ee\u7b56\u7565 +gb.accessPolicyDescription = \u8bf7\u9009\u62e9\u4e00\u4e2a\u63a7\u5236\u7248\u672c\u5e93\u53ef\u89c1\u6027\u4ee5\u53caGit\u8bbf\u95ee\u6743\u9650\u7684\u8bbf\u95ee\u7b56\u7565\u3002 +gb.anonymousPolicy = \u533f\u540d\u67e5\u770b, \u514b\u9686\u548c\u63a8\u9001 +gb.anonymousPolicyDescription = \u4efb\u4f55\u4eba\u90fd\u53ef\u4ee5\u67e5\u770b\uff0c\u514b\u9686\u4ee5\u53ca\u63a8\u9001\u81f3\u6b64\u7248\u672c\u5e93\u3002 +gb.authenticatedPushPolicy = \u9650\u5236\u63a8\u9001 (\u6388\u6743\u8bbf\u95ee) +gb.authenticatedPushPolicyDescription = \u4efb\u4f55\u4eba\u90fd\u53ef\u4ee5\u67e5\u770b\u4ee5\u53ca\u514b\u9686\u6b64\u7248\u672c\u5e93\u3002\u4efb\u4f55\u5df2\u6388\u6743\u7528\u6237\u62e5\u6709RW+\u63a8\u9001\u6743\u9650\u3002 +gb.namedPushPolicy = \u9650\u5236\u63a8\u9001 (\u6307\u5b9a\u7528\u6237) +gb.namedPushPolicyDescription = \u4efb\u4f55\u4eba\u90fd\u53ef\u4ee5\u67e5\u770b\u4ee5\u53ca\u514b\u9686\u6b64\u7248\u672c\u5e93\u3002 \u60a8\u53ef\u4ee5\u9009\u62e9\u62e5\u6709\u63a8\u9001\u6743\u9650\u7684\u7528\u6237\u3002 +gb.clonePolicy = \u9650\u5236\u514b\u9686\uff0c\u63a8\u9001 +gb.clonePolicyDescription = \u4efb\u4f55\u4eba\u90fd\u53ef\u4ee5\u770b\u5230\u6b64\u7248\u672c\u5e93\u3002\u60a8\u53ef\u4ee5\u9009\u62e9\u62e5\u6709\u514b\u9686\u548c\u63a8\u9001\u6743\u9650\u7684\u7528\u6237\u3002 +gb.viewPolicy = \u9650\u5236\u67e5\u770b\uff0c\u514b\u9686\u548c\u63a8\u9001 +gb.viewPolicyDescription = \u60a8\u53ef\u4ee5\u9009\u62e9\u62e5\u6709\u67e5\u770b\uff0c\u514b\u9686\u548c\u63a8\u9001\u6743\u9650\u7684\u7528\u6237\u3002 +gb.initialCommit = \u521d\u59cb\u5316\u63d0\u4ea4 +gb.initialCommitDescription = \u6b64\u529f\u80fd\u76f8\u5f53\u4e8e\u76f4\u63a5\u5bf9\u5f53\u524d\u7248\u672c\u5e93\u8fdb\u884c <code>git clone</code> \u3002 \u5982\u679c\u8df3\u8fc7\u6b64\u6b65\uff0c\u60a8\u5fc5\u987b\u5728\u672c\u5730\u8fdb\u884c <code>git init</code> \u3002 +gb.initWithReadme = \u52a0\u5165 README +gb.initWithReadmeDescription = \u6b64\u529f\u80fd\u4f1a\u81ea\u52a8\u751f\u6210\u4e00\u4e2a\u60a8\u7684\u7248\u672c\u5e93\u7684 README \u6587\u4ef6\u3002 +gb.initWithGitignore = \u52a0\u5165 .gitignore \u6587\u4ef6 +gb.initWithGitignoreDescription = \u6b64\u529f\u80fd\u4f1a\u751f\u6210\u4e00\u4e2a\u914d\u7f6e\u6587\u4ef6\uff0c\u65e8\u5728\u63d0\u793a Git \u5ba2\u6237\u7aef\u5ffd\u7565\u5bf9\u5e94\u7684\u6587\u4ef6\u6216\u6587\u4ef6\u5939\u3002 +gb.pleaseSelectGitIgnore = \u8bf7\u9009\u62e9\u4e00\u4e2a .gitignore \u6587\u4ef6 +gb.receive = receive +gb.permissions = permissions +gb.ownersDescription = \u7248\u672c\u5e93\u62e5\u6709\u8005\u62e5\u6709\u7248\u672c\u5e93\u7684\u6240\u6709\u7ba1\u7406\u6743\u9650\uff0c\u4f46\u662f\u53ea\u5141\u8bb8\u4fee\u6539\u79c1\u6709\u7248\u672c\u5e93\u7684\u540d\u79f0\u3002 +gb.userPermissionsDescription = \u60a8\u53ef\u4ee5\u8bbe\u7f6e\u79c1\u6709\u7528\u6237\u6743\u9650\u3002 \u6b64\u8bbe\u7f6e\u4f1a\u8986\u76d6\u56e2\u961f\u6743\u9650\u4ee5\u53caregex\u6743\u9650\u3002 +gb.teamPermissionsDescription = \u60a8\u53ef\u4ee5\u8bbe\u7f6e\u79c1\u6709\u56e2\u961f\u6743\u9650\u3002 \u6b64\u8bbe\u7f6e\u4f1a\u8986\u76d6regex\u6743\u9650\u3002 +gb.ticketSettings = \u5de5\u5355\u8bbe\u7f6e +gb.receiveSettings = Receive \u8bbe\u7f6e +gb.receiveSettingsDescription = Receive\u8bbe\u7f6e\u8bbe\u5b9a\u63a8\u9001\u81f3\u7248\u672c\u5e93\u65f6\u7684\u884c\u4e3a\u3002 +gb.preReceiveDescription = Pre-receive hooks \u4f1a\u5728\u63a8\u9001\u63a5\u6536\u540e\uff0c refs \u66f4\u65b0<em>\u4e4b\u524d</em>\u6267\u884c\u3002<p>\u8fd9\u79cdhook\u662f\u8fdb\u884c\u63a8\u9001\u62d2\u7edd\u7684\u597d\u5de5\u5177\u3002</p> +gb.postReceiveDescription = Post-receive hooks \u4f1a\u5728\u63a8\u9001\u63a5\u6536\u540e\uff0c refs \u66f4\u65b0<em>\u4e4b\u540e</em>\u6267\u884c\u3002<p>\u8fd9\u79cdhook\u662f\u8fdb\u884c\u901a\u77e5\uff0c\u4f7f\u7528\u6784\u5efa\u89e6\u53d1\u5668\u7b49\u7684\u597d\u5de5\u5177\u3002</p> +gb.federationStrategyDescription = \u8bbe\u7f6e\u662f\u5426\u4ee5\u53ca\u5982\u4f55\u5c06\u5f53\u524d\u7248\u672c\u5e93\u4e0e\u5176\u4ed6Gitblit\u8fdb\u884cfederate\u3002 +gb.federationSetsDescription = \u5f53\u524d\u7248\u672c\u5e93\u5c06\u4f1a\u88ab\u5305\u542b\u8fdb\u9009\u5b9a\u7684federation\u96c6\u4e2d\u3002 +gb.miscellaneous = miscellaneous +gb.originDescription = \u5f53\u524d\u7248\u672c\u5e93\u7684\u514b\u9686\u6e90\u5730\u5740\u3002 +gb.gc = GC +gb.garbageCollection = \u5783\u573e\u6536\u96c6 +gb.garbageCollectionDescription = \u5783\u573e\u6536\u96c6\u5668\u4f1a\u5c06\u5ba2\u6237\u7aef\u6240\u53d1\u9001\u7684\u677e\u6563\u6587\u4ef6\u6253\u5305\u5e76\u5220\u9664\u5f53\u524d\u7248\u672c\u5e93\u4e2d\u672a\u88ab\u5f15\u7528\u7684\u5bf9\u8c61\u3002 +gb.commitMessageRendererDescription = \u53ef\u4ee5\u5c06\u63d0\u4ea4\u4fe1\u606f\u663e\u793a\u4e3a\u7eaf\u6587\u672c\u6216\u8005\u5df2\u6392\u7248\u7684Markup\u6587\u672c +gb.preferences = \u504f\u597d +gb.accountPreferences = \u7528\u6237\u504f\u597d +gb.accountPreferencesDescription = \u8bbe\u7f6e\u60a8\u7684\u7528\u6237\u504f\u597d +gb.languagePreference = \u8bed\u8a00\u504f\u597d +gb.languagePreferenceDescription = \u9009\u62e9\u60a8\u559c\u6b22\u7684Gitblit\u7ffb\u8bd1 +gb.emailMeOnMyTicketChanges = \u5728\u6211\u7684\u5de5\u5355\u53d1\u751f\u53d8\u5316\u540e\u90ae\u4ef6\u901a\u77e5\u6211 +gb.emailMeOnMyTicketChangesDescription = \u5bf9\u6211\u5728\u5de5\u5355\u4e2d\u4f5c\u51fa\u7684\u4fee\u6539\u53d1\u9001\u90ae\u4ef6\u901a\u77e5\u3002 +gb.displayNameDescription = \u9009\u62e9\u663e\u793a\u540d\u79f0 +gb.emailAddressDescription = \u63a5\u6536\u901a\u77e5\u7684\u4e3b\u8981\u90ae\u7bb1\u5730\u5740 +gb.sshKeys = SSH Keys +gb.sshKeysDescription = SSH \u516c\u7ea6\u8ba4\u8bc1\u662f\u4e00\u79cd\u4e0d\u540c\u4e8e\u5bc6\u7801\u8ba4\u8bc1\u7684\u5b89\u5168\u8ba4\u8bc1\u65b9\u6cd5\u3002 +gb.addSshKey = \u6dfb\u52a0 SSH Key +gb.key = Key +gb.comment = \u53d1\u8868 +gb.sshKeyCommentDescription = \u8f93\u5165\u8bc4\u8bba\uff08\u53ef\u4e3a\u7a7a\uff09. \u5982\u679c\u4e3a\u7a7a\uff0c \u8bc4\u8bba\u5185\u5bb9\u5c06\u4f1a\u4ece\u4e3b\u8981\u6570\u636e\u4e2d\u63d0\u53d6\u3002 +gb.permission = \u6743\u9650 +gb.sshKeyPermissionDescription = \u8bbe\u7f6eSSH key\u7684\u8bbf\u95ee\u6743\u9650 +gb.transportPreference = Transport \u504f\u597d +gb.transportPreferenceDescription = \u9009\u62e9\u60a8\u7528\u6765\u514b\u9686\u7684 Transport
\ 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 new file mode 100644 index 00000000..a56e6996 --- /dev/null +++ b/src/main/java/com/gitblit/wicket/GitblitWicketApp.java @@ -0,0 +1,72 @@ +package com.gitblit.wicket; + +import java.util.Date; +import java.util.TimeZone; + +import org.apache.wicket.markup.html.WebPage; + +import com.gitblit.IStoredSettings; +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.tickets.ITicketService; +import com.gitblit.transport.ssh.IPublicKeyManager; + +public interface GitblitWicketApp { + + public abstract void mount(String location, Class<? extends WebPage> clazz, String... parameters); + + public abstract Class<? extends WebPage> getHomePage(); + + public abstract boolean isCacheablePage(String mountPoint); + + public abstract CacheControl getCacheControl(String mountPoint); + + public abstract IStoredSettings settings(); + + /** + * Is Gitblit running in debug mode? + * + * @return true if Gitblit is running in debug mode + */ + public abstract boolean isDebugMode(); + + /* + * These methods look strange... and they are... but they are the first + * step towards modularization across multiple commits. + */ + public abstract Date getBootDate(); + + public abstract Date getLastActivityDate(); + + public abstract IRuntimeManager runtime(); + + public abstract IPluginManager plugins(); + + public abstract INotificationManager notifier(); + + public abstract IUserManager users(); + + public abstract IAuthenticationManager authentication(); + + public abstract IPublicKeyManager keys(); + + public abstract IRepositoryManager repositories(); + + public abstract IProjectManager projects(); + + public abstract IFederationManager federation(); + + public abstract IGitblit gitblit(); + + public abstract ITicketService tickets(); + + public abstract TimeZone getTimezone(); + +}
\ No newline at end of file diff --git a/src/main/java/com/gitblit/wicket/MarkupProcessor.java b/src/main/java/com/gitblit/wicket/MarkupProcessor.java index ff6fbce2..e7681f2c 100644 --- a/src/main/java/com/gitblit/wicket/MarkupProcessor.java +++ b/src/main/java/com/gitblit/wicket/MarkupProcessor.java @@ -56,11 +56,11 @@ import org.slf4j.LoggerFactory; import com.gitblit.IStoredSettings;
import com.gitblit.Keys;
import com.gitblit.models.PathModel;
+import com.gitblit.servlet.RawServlet;
import com.gitblit.utils.JGitUtils;
import com.gitblit.utils.MarkdownUtils;
import com.gitblit.utils.StringUtils;
import com.gitblit.wicket.pages.DocPage;
-import com.gitblit.wicket.pages.RawPage;
import com.google.common.base.Joiner;
/**
@@ -260,7 +260,8 @@ public class MarkupProcessor { if (imagePath.indexOf("://") == -1) {
// relative image
String path = doc.getRelativePath(imagePath);
- url = getWicketUrl(RawPage.class, repositoryName, commitId, path);
+ String contextUrl = RequestCycle.get().getRequest().getRelativePathPrefixToContextRoot();
+ url = RawServlet.asLink(contextUrl, repositoryName, commitId, path);
} else {
// absolute image
url = imagePath;
@@ -312,7 +313,8 @@ public class MarkupProcessor { if (node.url.indexOf("://") == -1) {
// repository-relative image link
String path = doc.getRelativePath(node.url);
- String url = getWicketUrl(RawPage.class, repositoryName, commitId, path);
+ String contextUrl = RequestCycle.get().getRequest().getRelativePathPrefixToContextRoot();
+ String url = RawServlet.asLink(contextUrl, repositoryName, commitId, path);
return new Rendering(url, text);
}
// absolute image link
@@ -325,7 +327,8 @@ public class MarkupProcessor { if (url.indexOf("://") == -1) {
// repository-relative image link
String path = doc.getRelativePath(url);
- String wurl = getWicketUrl(RawPage.class, repositoryName, commitId, path);
+ String contextUrl = RequestCycle.get().getRequest().getRelativePathPrefixToContextRoot();
+ String wurl = RawServlet.asLink(contextUrl, repositoryName, commitId, path);
rendering = new Rendering(wurl, alt);
} else {
// absolute image link
diff --git a/src/main/java/com/gitblit/wicket/PageRegistration.java b/src/main/java/com/gitblit/wicket/PageRegistration.java deleted file mode 100644 index 1b98f2c7..00000000 --- a/src/main/java/com/gitblit/wicket/PageRegistration.java +++ /dev/null @@ -1,243 +0,0 @@ -/*
- * Copyright 2011 gitblit.com.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.gitblit.wicket;
-
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.wicket.PageParameters;
-import org.apache.wicket.markup.html.WebPage;
-
-import com.gitblit.utils.StringUtils;
-
-/**
- * Represents a page link registration for the topbar.
- *
- * @author James Moger
- *
- */
-public class PageRegistration implements Serializable {
- private static final long serialVersionUID = 1L;
-
- public final String translationKey;
- public final Class<? extends WebPage> pageClass;
- public final PageParameters params;
- public final boolean hiddenPhone;
-
- public PageRegistration(String translationKey, Class<? extends WebPage> pageClass) {
- this(translationKey, pageClass, null);
- }
-
- public PageRegistration(String translationKey, Class<? extends WebPage> pageClass,
- PageParameters params) {
- this(translationKey, pageClass, params, false);
- }
-
- public PageRegistration(String translationKey, Class<? extends WebPage> pageClass,
- PageParameters params, boolean hiddenPhone) {
- this.translationKey = translationKey;
- this.pageClass = pageClass;
- this.params = params;
- this.hiddenPhone = hiddenPhone;
- }
-
- /**
- * Represents a page link to a non-Wicket page. Might be external.
- *
- * @author James Moger
- *
- */
- public static class OtherPageLink extends PageRegistration {
-
- private static final long serialVersionUID = 1L;
-
- public final String url;
-
- public OtherPageLink(String translationKey, String url) {
- super(translationKey, null);
- this.url = url;
- }
-
- public OtherPageLink(String translationKey, String url, boolean hiddenPhone) {
- super(translationKey, null, null, hiddenPhone);
- this.url = url;
- }
- }
-
- /**
- * Represents a DropDownMenu for the topbar
- *
- * @author James Moger
- *
- */
- public static class DropDownMenuRegistration extends PageRegistration {
-
- private static final long serialVersionUID = 1L;
-
- public final List<DropDownMenuItem> menuItems;
-
- public DropDownMenuRegistration(String translationKey, Class<? extends WebPage> pageClass) {
- super(translationKey, pageClass);
- menuItems = new ArrayList<DropDownMenuItem>();
- }
- }
-
- /**
- * A MenuItem for the DropDownMenu.
- *
- * @author James Moger
- *
- */
- public static class DropDownMenuItem implements Serializable {
-
- private static final long serialVersionUID = 1L;
-
- final PageParameters parameters;
- final String displayText;
- final String parameter;
- final String value;
- final boolean isSelected;
-
- /**
- * Divider constructor.
- */
- public DropDownMenuItem() {
- this(null, null, null, null);
- }
-
- /**
- * Standard Menu Item constructor.
- *
- * @param displayText
- * @param parameter
- * @param value
- */
- public DropDownMenuItem(String displayText, String parameter, String value) {
- this(displayText, parameter, value, null);
- }
-
- /**
- * Standard Menu Item constructor that preserves aggregate parameters.
- *
- * @param displayText
- * @param parameter
- * @param value
- */
- public DropDownMenuItem(String displayText, String parameter, String value,
- PageParameters params) {
- this.displayText = displayText;
- this.parameter = parameter;
- this.value = value;
-
- if (params == null) {
- // no parameters specified
- parameters = new PageParameters();
- setParameter(parameter, value);
- isSelected = false;
- } else {
- parameters = new PageParameters(params);
- if (parameters.containsKey(parameter)) {
- isSelected = params.getString(parameter).equals(value);
- // set the new selection value
- setParameter(parameter, value);
- } else {
- // not currently selected
- isSelected = false;
- setParameter(parameter, value);
- }
- }
- }
-
- protected void setParameter(String parameter, String value) {
- if (!StringUtils.isEmpty(parameter)) {
- if (StringUtils.isEmpty(value)) {
- this.parameters.remove(parameter);
- } else {
- this.parameters.put(parameter, value);
- }
- }
- }
-
- public String formatParameter() {
- if (StringUtils.isEmpty(parameter) || StringUtils.isEmpty(value)) {
- return "";
- }
- return parameter + "=" + value;
- }
-
- public PageParameters getPageParameters() {
- return parameters;
- }
-
- public boolean isDivider() {
- return displayText == null && value == null && parameter == null;
- }
-
- public boolean isSelected() {
- return isSelected;
- }
-
- @Override
- public int hashCode() {
- if (isDivider()) {
- // divider menu item
- return super.hashCode();
- }
- if (StringUtils.isEmpty(displayText)) {
- return value.hashCode() + parameter.hashCode();
- }
- return displayText.hashCode();
- }
-
- @Override
- public boolean equals(Object o) {
- if (o instanceof DropDownMenuItem) {
- return hashCode() == o.hashCode();
- }
- return false;
- }
-
- @Override
- public String toString() {
- if (StringUtils.isEmpty(displayText)) {
- return formatParameter();
- }
- return displayText;
- }
- }
-
- public static class DropDownToggleItem extends DropDownMenuItem {
-
- private static final long serialVersionUID = 1L;
-
- /**
- * Toggle Menu Item constructor that preserves aggregate parameters.
- *
- * @param displayText
- * @param parameter
- * @param value
- */
- public DropDownToggleItem(String displayText, String parameter, String value,
- PageParameters params) {
- super(displayText, parameter, value, params);
- if (isSelected) {
- // already selected, so remove this enables toggling
- parameters.remove(parameter);
- }
- }
- }
-}
\ No newline at end of file diff --git a/src/main/java/com/gitblit/wicket/PluginClassResolver.java b/src/main/java/com/gitblit/wicket/PluginClassResolver.java new file mode 100644 index 00000000..476f9611 --- /dev/null +++ b/src/main/java/com/gitblit/wicket/PluginClassResolver.java @@ -0,0 +1,120 @@ +/* + * 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.wicket; + +import java.io.IOException; +import java.net.URL; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; +import java.util.TreeSet; + +import org.apache.wicket.WicketRuntimeException; +import org.apache.wicket.application.IClassResolver; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import ro.fortsoft.pf4j.PluginState; +import ro.fortsoft.pf4j.PluginWrapper; + +import com.gitblit.manager.IPluginManager; + +/** + * Resolves plugin classes and resources. + */ +public class PluginClassResolver implements IClassResolver { + private static final Logger logger = LoggerFactory.getLogger(PluginClassResolver.class); + + private final IClassResolver coreResolver; + private final IPluginManager pluginManager; + + public PluginClassResolver(IClassResolver coreResolver, IPluginManager pluginManager) { + this.coreResolver = coreResolver; + this.pluginManager = pluginManager; + } + + @Override + public Class<?> resolveClass(final String className) throws ClassNotFoundException { + boolean debugEnabled = logger.isDebugEnabled(); + + for (PluginWrapper plugin : pluginManager.getPlugins()) { + if (PluginState.STARTED != plugin.getPluginState()) { + // ignore this plugin + continue; + } + + try { + return plugin.getPluginClassLoader().loadClass(className); + } catch (ClassNotFoundException cnfx) { + if (debugEnabled) { + logger.debug("ClassResolver '{}' cannot find class: '{}'", plugin.getPluginId(), className); + } + } + } + + return coreResolver.resolveClass(className); + } + + @Override + public Iterator<URL> getResources(final String name) { + Set<URL> urls = new TreeSet<URL>(new UrlExternalFormComparator()); + + for (PluginWrapper plugin : pluginManager.getPlugins()) { + if (PluginState.STARTED != plugin.getPluginState()) { + // ignore this plugin + continue; + } + + Iterator<URL> it = getResources(name, plugin); + while (it.hasNext()) { + URL url = it.next(); + urls.add(url); + } + } + + Iterator<URL> it = coreResolver.getResources(name); + while (it.hasNext()) { + URL url = it.next(); + urls.add(url); + } + return urls.iterator(); + } + + protected Iterator<URL> getResources(String name, PluginWrapper plugin) { + HashSet<URL> loadedFiles = new HashSet<URL>(); + try { + // Try the classloader for the wicket jar/bundle + Enumeration<URL> resources = plugin.getPluginClassLoader().getResources(name); + loadResources(resources, loadedFiles); + } catch (IOException e) { + throw new WicketRuntimeException(e); + } + + return loadedFiles.iterator(); + } + + private void loadResources(Enumeration<URL> resources, Set<URL> loadedFiles) { + if (resources != null) { + while (resources.hasMoreElements()) { + final URL url = resources.nextElement(); + if (!loadedFiles.contains(url)) { + loadedFiles.add(url); + } + } + } + } +}
\ No newline at end of file diff --git a/src/main/java/com/gitblit/wicket/SessionlessForm.java b/src/main/java/com/gitblit/wicket/SessionlessForm.java index d228a2e1..6f790717 100644 --- a/src/main/java/com/gitblit/wicket/SessionlessForm.java +++ b/src/main/java/com/gitblit/wicket/SessionlessForm.java @@ -22,6 +22,7 @@ import org.apache.wicket.PageParameters; import org.apache.wicket.markup.ComponentTag; import org.apache.wicket.markup.MarkupStream; import org.apache.wicket.markup.html.form.StatelessForm; +import org.apache.wicket.protocol.http.RequestUtils; import org.apache.wicket.protocol.http.WicketURLDecoder; import org.apache.wicket.protocol.http.request.WebRequestCodingStrategy; import org.apache.wicket.util.string.AppendingStringBuffer; @@ -53,9 +54,9 @@ public class SessionlessForm<T> extends StatelessForm<T> { private static final String HIDDEN_DIV_START = "<div style=\"width:0px;height:0px;position:absolute;left:-100px;top:-100px;overflow:hidden\">"; - private final Class<? extends BasePage> pageClass; + protected final Class<? extends BasePage> pageClass; - private final PageParameters pageParameters; + protected final PageParameters pageParameters; private final Logger log = LoggerFactory.getLogger(SessionlessForm.class); @@ -145,4 +146,14 @@ public class SessionlessForm<T> extends StatelessForm<T> { String un = WicketURLDecoder.QUERY_INSTANCE.decode(s); return Strings.escapeMarkup(un).toString(); } + + protected String getAbsoluteUrl() { + return getAbsoluteUrl(pageClass, pageParameters); + } + + protected String getAbsoluteUrl(Class<? extends BasePage> pageClass, PageParameters pageParameters) { + String relativeUrl = urlFor(pageClass, pageParameters).toString(); + String absoluteUrl = RequestUtils.toAbsolutePath(relativeUrl); + return absoluteUrl; + } } diff --git a/src/main/java/com/gitblit/wicket/TicketsUI.java b/src/main/java/com/gitblit/wicket/TicketsUI.java new file mode 100644 index 00000000..a243a7b3 --- /dev/null +++ b/src/main/java/com/gitblit/wicket/TicketsUI.java @@ -0,0 +1,211 @@ +/* + * 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.wicket; + +import java.io.Serializable; +import java.text.MessageFormat; + +import org.apache.wicket.markup.html.basic.Label; + +import com.gitblit.models.TicketModel; +import com.gitblit.models.TicketModel.Status; +import com.gitblit.models.TicketModel.Type; +import com.gitblit.utils.StringUtils; + +/** + * Common tickets ui methods and classes. + * + * @author James Moger + * + */ +public class TicketsUI { + + public static final String [] openStatii = new String [] { Status.New.name().toLowerCase(), Status.Open.name().toLowerCase() }; + + 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); + } + + public static Label getStateIcon(String wicketId, Type type, Status state) { + Label label = new Label(wicketId); + if (type == null) { + type = Type.defaultType; + } + switch (type) { + case Proposal: + WicketUtils.setCssClass(label, "fa fa-code-fork"); + break; + case Bug: + WicketUtils.setCssClass(label, "fa fa-bug"); + break; + case Enhancement: + WicketUtils.setCssClass(label, "fa fa-magic"); + break; + case Question: + WicketUtils.setCssClass(label, "fa fa-question"); + break; + default: + // standard ticket + WicketUtils.setCssClass(label, "fa fa-ticket"); + } + WicketUtils.setHtmlTooltip(label, getTypeState(type, state)); + return label; + } + + public static String getTypeState(Type type, Status state) { + return state.toString() + " " + type.toString(); + } + + public static String getLozengeClass(Status status, boolean subtle) { + if (status == null) { + status = Status.New; + } + String css = ""; + switch (status) { + case Declined: + case Duplicate: + case Invalid: + case Wontfix: + case Abandoned: + css = "aui-lozenge-error"; + break; + case Fixed: + case Merged: + case Resolved: + css = "aui-lozenge-success"; + break; + case New: + css = "aui-lozenge-complete"; + break; + case On_Hold: + css = "aui-lozenge-current"; + break; + default: + css = ""; + break; + } + + return "aui-lozenge" + (subtle ? " aui-lozenge-subtle": "") + (css.isEmpty() ? "" : " ") + css; + } + + public static String getStatusClass(Status status) { + String css = ""; + switch (status) { + case Declined: + case Duplicate: + case Invalid: + case Wontfix: + case Abandoned: + css = "resolution-error"; + break; + case Fixed: + case Merged: + case Resolved: + css = "resolution-success"; + break; + case New: + css = "resolution-complete"; + break; + case On_Hold: + css = "resolution-current"; + break; + default: + css = ""; + break; + } + + return "resolution" + (css.isEmpty() ? "" : " ") + css; + } + + public static class TicketSort implements Serializable { + + private static final long serialVersionUID = 1L; + + public final String name; + public final String sortBy; + public final boolean desc; + + public TicketSort(String name, String sortBy, boolean desc) { + this.name = name; + this.sortBy = sortBy; + this.desc = desc; + } + } + + public static class Indicator implements Serializable { + + private static final long serialVersionUID = 1L; + + public final String css; + public final int count; + public final String tooltip; + + public Indicator(String css, String tooltip) { + this.css = css; + this.tooltip = tooltip; + this.count = 0; + } + + public Indicator(String css, int count, String pattern) { + this.css = css; + this.count = count; + this.tooltip = StringUtils.isEmpty(pattern) ? "" : MessageFormat.format(pattern, count); + } + + public String getTooltip() { + return tooltip; + } + } + + public static class TicketQuery implements Serializable, Comparable<TicketQuery> { + + private static final long serialVersionUID = 1L; + + public final String name; + public final String query; + public String color; + + public TicketQuery(String name, String query) { + this.name = name; + this.query = query; + } + + public TicketQuery color(String value) { + this.color = value; + return this; + } + + @Override + public boolean equals(Object o) { + if (o instanceof TicketQuery) { + return ((TicketQuery) o).query.equals(query); + } + return false; + } + + @Override + public int hashCode() { + return query.hashCode(); + } + + @Override + public int compareTo(TicketQuery o) { + return query.compareTo(o.query); + } + } +} diff --git a/src/main/java/com/gitblit/wicket/UrlExternalFormComparator.java b/src/main/java/com/gitblit/wicket/UrlExternalFormComparator.java new file mode 100644 index 00000000..90f4b320 --- /dev/null +++ b/src/main/java/com/gitblit/wicket/UrlExternalFormComparator.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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; + +import java.net.URL; +import java.util.Comparator; + +/** + * A comparator of URL instances. + * + * Comparing URLs with their implementation of #equals() is + * bad because it may cause problems like DNS resolving, or other + * slow checks. This comparator uses the external form of an URL + * to make a simple comparison of two Strings. + * + * @since 1.5.6 + */ +public class UrlExternalFormComparator implements Comparator<URL> +{ + @Override + public int compare(URL url1, URL url2) + { + return url1.toExternalForm().compareTo(url2.toExternalForm()); + } +}
\ No newline at end of file diff --git a/src/main/java/com/gitblit/wicket/WicketUtils.java b/src/main/java/com/gitblit/wicket/WicketUtils.java index 2a34ca80..687f0105 100644 --- a/src/main/java/com/gitblit/wicket/WicketUtils.java +++ b/src/main/java/com/gitblit/wicket/WicketUtils.java @@ -300,7 +300,9 @@ public class WicketUtils { public static PageParameters newRepositoryParameter(String repositoryName) {
Map<String, String> parameterMap = new HashMap<String, String>();
- parameterMap.put("r", repositoryName);
+ if (!StringUtils.isEmpty(repositoryName)) {
+ parameterMap.put("r", repositoryName);
+ }
return new PageParameters(parameterMap);
}
@@ -443,6 +445,20 @@ public class WicketUtils { return new PageParameters(parameterMap);
}
+ public static PageParameters newTicketsParameters(String repositoryName, String... states) {
+ PageParameters tParams = newRepositoryParameter(repositoryName);
+ if (states != null) {
+ for (String state : states) {
+ tParams.add("status", state);
+ }
+ }
+ return tParams;
+ }
+
+ public static PageParameters newOpenTicketsParameter(String repositoryName) {
+ return newTicketsParameters(repositoryName, TicketsUI.openStatii);
+ }
+
public static String getProjectName(PageParameters params) {
return params.getString("p", "");
}
diff --git a/src/main/java/com/gitblit/wicket/pages/ActivityPage.java b/src/main/java/com/gitblit/wicket/pages/ActivityPage.java index f0e390dc..c505a666 100644 --- a/src/main/java/com/gitblit/wicket/pages/ActivityPage.java +++ b/src/main/java/com/gitblit/wicket/pages/ActivityPage.java @@ -31,15 +31,15 @@ import org.apache.wicket.markup.html.panel.Fragment; import com.gitblit.Keys;
import com.gitblit.models.Activity;
+import com.gitblit.models.Menu.ParameterMenuItem;
+import com.gitblit.models.NavLink.DropDownPageMenuNavLink;
import com.gitblit.models.Metric;
+import com.gitblit.models.NavLink;
import com.gitblit.models.RepositoryModel;
import com.gitblit.utils.ActivityUtils;
import com.gitblit.utils.StringUtils;
import com.gitblit.wicket.CacheControl;
import com.gitblit.wicket.CacheControl.LastModified;
-import com.gitblit.wicket.PageRegistration;
-import com.gitblit.wicket.PageRegistration.DropDownMenuItem;
-import com.gitblit.wicket.PageRegistration.DropDownMenuRegistration;
import com.gitblit.wicket.WicketUtils;
import com.gitblit.wicket.charting.Chart;
import com.gitblit.wicket.charting.Charts;
@@ -135,8 +135,8 @@ public class ActivityPage extends RootPage { }
@Override
- protected void addDropDownMenus(List<PageRegistration> pages) {
- DropDownMenuRegistration filters = new DropDownMenuRegistration("gb.filters",
+ protected void addDropDownMenus(List<NavLink> navLinks) {
+ DropDownPageMenuNavLink filters = new DropDownPageMenuNavLink("gb.filters",
ActivityPage.class);
PageParameters currentParameters = getPageParameters();
@@ -153,9 +153,9 @@ public class ActivityPage extends RootPage { if (filters.menuItems.size() > 0) {
// Reset Filter
- filters.menuItems.add(new DropDownMenuItem(getString("gb.reset"), null, null));
+ filters.menuItems.add(new ParameterMenuItem(getString("gb.reset")));
}
- pages.add(filters);
+ navLinks.add(filters);
}
/**
@@ -209,7 +209,7 @@ public class ActivityPage extends RootPage { }
charts.addChart(chart);
- // active repositories pie chart
+ // active repositories pie chart
chart = charts.createPieChart("chartRepositories", getString("gb.activeRepositories"),
getString("gb.repository"), getString("gb.commits"));
for (Metric metric : repositoryMetrics.values()) {
diff --git a/src/main/java/com/gitblit/wicket/pages/BasePage.java b/src/main/java/com/gitblit/wicket/pages/BasePage.java index 7d3d3a24..49710397 100644 --- a/src/main/java/com/gitblit/wicket/pages/BasePage.java +++ b/src/main/java/com/gitblit/wicket/pages/BasePage.java @@ -98,6 +98,10 @@ public abstract class BasePage extends SessionPage { }
}
+ protected String getContextUrl() {
+ return getRequest().getRelativePathPrefixToContextRoot();
+ }
+
protected String getCanonicalUrl() {
return getCanonicalUrl(getClass(), getPageParameters());
}
diff --git a/src/main/java/com/gitblit/wicket/pages/BlobPage.java b/src/main/java/com/gitblit/wicket/pages/BlobPage.java index 299d8dbc..f3d0bc92 100644 --- a/src/main/java/com/gitblit/wicket/pages/BlobPage.java +++ b/src/main/java/com/gitblit/wicket/pages/BlobPage.java @@ -24,10 +24,12 @@ import org.apache.wicket.PageParameters; import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.image.Image;
import org.apache.wicket.markup.html.link.BookmarkablePageLink;
+import org.apache.wicket.markup.html.link.ExternalLink;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import com.gitblit.Keys;
+import com.gitblit.servlet.RawServlet;
import com.gitblit.utils.JGitUtils;
import com.gitblit.utils.StringUtils;
import com.gitblit.wicket.CacheControl;
@@ -57,8 +59,8 @@ public class BlobPage extends RepositoryPage { WicketUtils.newPathParameter(repositoryName, objectId, blobPath))
.setEnabled(false));
add(new BookmarkablePageLink<Void>("historyLink", HistoryPage.class).setEnabled(false));
- add(new BookmarkablePageLink<Void>("rawLink", RawPage.class,
- WicketUtils.newPathParameter(repositoryName, objectId, blobPath)));
+ String rawUrl = RawServlet.asLink(getContextUrl(), repositoryName, objectId, blobPath);
+ add(new ExternalLink("rawLink", rawUrl));
add(new CommitHeaderPanel("commitHeader", objectId));
add(new PathBreadcrumbsPanel("breadcrumbs", repositoryName, blobPath, objectId));
Component c = new Label("blobText", JGitUtils.getStringContent(r, objectId, encodings));
@@ -87,8 +89,8 @@ public class BlobPage extends RepositoryPage { WicketUtils.newPathParameter(repositoryName, objectId, blobPath)));
add(new BookmarkablePageLink<Void>("historyLink", HistoryPage.class,
WicketUtils.newPathParameter(repositoryName, objectId, blobPath)));
- add(new BookmarkablePageLink<Void>("rawLink", RawPage.class,
- WicketUtils.newPathParameter(repositoryName, objectId, blobPath)));
+ String rawUrl = RawServlet.asLink(getContextUrl(), repositoryName, objectId, blobPath);
+ add(new ExternalLink("rawLink", rawUrl));
add(new CommitHeaderPanel("commitHeader", repositoryName, commit));
@@ -115,7 +117,7 @@ public class BlobPage extends RepositoryPage { case 2:
// image blobs
add(new Label("blobText").setVisible(false));
- add(new ExternalImage("blobImage", urlFor(RawPage.class, WicketUtils.newPathParameter(repositoryName, objectId, blobPath)).toString()));
+ add(new ExternalImage("blobImage", rawUrl));
break;
case 3:
// binary blobs
diff --git a/src/main/java/com/gitblit/wicket/pages/CommitDiffPage.java b/src/main/java/com/gitblit/wicket/pages/CommitDiffPage.java index 7f2a8a61..71a5ea67 100644 --- a/src/main/java/com/gitblit/wicket/pages/CommitDiffPage.java +++ b/src/main/java/com/gitblit/wicket/pages/CommitDiffPage.java @@ -34,6 +34,7 @@ import com.gitblit.Constants; import com.gitblit.models.GitNote; import com.gitblit.models.PathModel.PathChangeModel; import com.gitblit.models.SubmoduleModel; +import com.gitblit.servlet.RawServlet; import com.gitblit.utils.DiffUtils; import com.gitblit.utils.DiffUtils.DiffOutput; import com.gitblit.utils.DiffUtils.DiffOutputType; @@ -170,8 +171,8 @@ public class CommitDiffPage extends RepositoryPage { item.add(new BookmarkablePageLink<Void>("view", BlobPage.class, WicketUtils .newPathParameter(repositoryName, entry.commitId, entry.path)) .setEnabled(!entry.changeType.equals(ChangeType.DELETE))); - item.add(new BookmarkablePageLink<Void>("raw", RawPage.class, WicketUtils - .newPathParameter(repositoryName, entry.commitId, entry.path)) + String rawUrl = RawServlet.asLink(getContextUrl(), repositoryName, entry.commitId, entry.path); + item.add(new ExternalLink("raw", rawUrl) .setEnabled(!entry.changeType.equals(ChangeType.DELETE))); item.add(new BookmarkablePageLink<Void>("blame", BlamePage.class, WicketUtils .newPathParameter(repositoryName, entry.commitId, entry.path)) diff --git a/src/main/java/com/gitblit/wicket/pages/CommitPage.java b/src/main/java/com/gitblit/wicket/pages/CommitPage.java index 8bc98489..6fadec5b 100644 --- a/src/main/java/com/gitblit/wicket/pages/CommitPage.java +++ b/src/main/java/com/gitblit/wicket/pages/CommitPage.java @@ -35,6 +35,7 @@ import com.gitblit.Constants; import com.gitblit.models.GitNote;
import com.gitblit.models.PathModel.PathChangeModel;
import com.gitblit.models.SubmoduleModel;
+import com.gitblit.servlet.RawServlet;
import com.gitblit.utils.JGitUtils;
import com.gitblit.wicket.CacheControl;
import com.gitblit.wicket.CacheControl.LastModified;
@@ -222,8 +223,8 @@ public class CommitPage extends RepositoryPage { item.add(new BookmarkablePageLink<Void>("view", BlobPage.class, WicketUtils
.newPathParameter(repositoryName, entry.commitId, entry.path))
.setEnabled(!entry.changeType.equals(ChangeType.DELETE)));
- item.add(new BookmarkablePageLink<Void>("raw", RawPage.class, WicketUtils
- .newPathParameter(repositoryName, entry.commitId, entry.path))
+ String rawUrl = RawServlet.asLink(getContextUrl(), repositoryName, entry.commitId, entry.path);
+ item.add(new ExternalLink("raw", rawUrl)
.setEnabled(!entry.changeType.equals(ChangeType.DELETE)));
item.add(new BookmarkablePageLink<Void>("blame", BlamePage.class, WicketUtils
.newPathParameter(repositoryName, entry.commitId, entry.path))
diff --git a/src/main/java/com/gitblit/wicket/pages/ComparePage.java b/src/main/java/com/gitblit/wicket/pages/ComparePage.java index 2024bf17..1ec66133 100644 --- a/src/main/java/com/gitblit/wicket/pages/ComparePage.java +++ b/src/main/java/com/gitblit/wicket/pages/ComparePage.java @@ -41,6 +41,7 @@ import com.gitblit.models.PathModel.PathChangeModel; import com.gitblit.models.RefModel; import com.gitblit.models.RepositoryModel; import com.gitblit.models.SubmoduleModel; +import com.gitblit.servlet.RawServlet; import com.gitblit.utils.DiffUtils; import com.gitblit.utils.DiffUtils.DiffOutput; import com.gitblit.utils.DiffUtils.DiffOutputType; @@ -184,8 +185,8 @@ public class ComparePage extends RepositoryPage { item.add(new BookmarkablePageLink<Void>("view", BlobPage.class, WicketUtils .newPathParameter(repositoryName, endId, entry.path)) .setEnabled(!entry.changeType.equals(ChangeType.DELETE))); - item.add(new BookmarkablePageLink<Void>("raw", RawPage.class, WicketUtils - .newPathParameter(repositoryName, endId, entry.path)) + String rawUrl = RawServlet.asLink(getContextUrl(), repositoryName, endId, entry.path); + item.add(new ExternalLink("raw", rawUrl) .setEnabled(!entry.changeType.equals(ChangeType.DELETE))); item.add(new BookmarkablePageLink<Void>("blame", BlamePage.class, WicketUtils .newPathParameter(repositoryName, endId, entry.path)) diff --git a/src/main/java/com/gitblit/wicket/pages/DashboardPage.java b/src/main/java/com/gitblit/wicket/pages/DashboardPage.java index 9853449e..9c10e01b 100644 --- a/src/main/java/com/gitblit/wicket/pages/DashboardPage.java +++ b/src/main/java/com/gitblit/wicket/pages/DashboardPage.java @@ -36,7 +36,10 @@ import org.eclipse.jgit.lib.Repository; import com.gitblit.Keys;
import com.gitblit.models.DailyLogEntry;
+import com.gitblit.models.Menu.ParameterMenuItem;
+import com.gitblit.models.NavLink.DropDownPageMenuNavLink;
import com.gitblit.models.Metric;
+import com.gitblit.models.NavLink;
import com.gitblit.models.RefLogEntry;
import com.gitblit.models.RepositoryCommit;
import com.gitblit.models.RepositoryModel;
@@ -45,9 +48,6 @@ import com.gitblit.utils.ArrayUtils; import com.gitblit.utils.RefLogUtils;
import com.gitblit.utils.StringUtils;
import com.gitblit.wicket.GitBlitWebApp;
-import com.gitblit.wicket.PageRegistration;
-import com.gitblit.wicket.PageRegistration.DropDownMenuItem;
-import com.gitblit.wicket.PageRegistration.DropDownMenuRegistration;
import com.gitblit.wicket.charting.Chart;
import com.gitblit.wicket.charting.Charts;
import com.gitblit.wicket.charting.Flotr2Charts;
@@ -141,10 +141,10 @@ public abstract class DashboardPage extends RootPage { }
@Override
- protected void addDropDownMenus(List<PageRegistration> pages) {
+ protected void addDropDownMenus(List<NavLink> navLinks) {
PageParameters params = getPageParameters();
- DropDownMenuRegistration menu = new DropDownMenuRegistration("gb.filters",
+ DropDownPageMenuNavLink menu = new DropDownPageMenuNavLink("gb.filters",
GitBlitWebApp.get().getHomePage());
// preserve repository filter option on time choices
@@ -152,10 +152,10 @@ public abstract class DashboardPage extends RootPage { if (menu.menuItems.size() > 0) {
// Reset Filter
- menu.menuItems.add(new DropDownMenuItem(getString("gb.reset"), null, null));
+ menu.menuItems.add(new ParameterMenuItem(getString("gb.reset")));
}
- pages.add(menu);
+ navLinks.add(menu);
}
diff --git a/src/main/java/com/gitblit/wicket/pages/DocPage.java b/src/main/java/com/gitblit/wicket/pages/DocPage.java index bf99978c..c06d8065 100644 --- a/src/main/java/com/gitblit/wicket/pages/DocPage.java +++ b/src/main/java/com/gitblit/wicket/pages/DocPage.java @@ -20,10 +20,12 @@ 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.apache.wicket.markup.html.link.ExternalLink;
import org.apache.wicket.markup.html.panel.Fragment;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
+import com.gitblit.servlet.RawServlet;
import com.gitblit.utils.BugtraqProcessor;
import com.gitblit.utils.JGitUtils;
import com.gitblit.utils.StringUtils;
@@ -87,8 +89,8 @@ public class DocPage extends RepositoryPage { WicketUtils.newPathParameter(repositoryName, objectId, documentPath)));
fragment.add(new BookmarkablePageLink<Void>("historyLink", HistoryPage.class,
WicketUtils.newPathParameter(repositoryName, objectId, documentPath)));
- fragment.add(new BookmarkablePageLink<Void>("rawLink", RawPage.class, WicketUtils.newPathParameter(
- repositoryName, objectId, documentPath)));
+ String rawUrl = RawServlet.asLink(getContextUrl(), repositoryName, objectId, documentPath);
+ fragment.add(new ExternalLink("rawLink", rawUrl));
fragment.add(new Label("content", markupDoc.html).setEscapeModelStrings(false));
add(fragment);
diff --git a/src/main/java/com/gitblit/wicket/pages/DocsPage.java b/src/main/java/com/gitblit/wicket/pages/DocsPage.java index 907dd6e2..fc56ee07 100644 --- a/src/main/java/com/gitblit/wicket/pages/DocsPage.java +++ b/src/main/java/com/gitblit/wicket/pages/DocsPage.java @@ -31,6 +31,7 @@ import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevCommit;
import com.gitblit.models.PathModel;
+import com.gitblit.servlet.RawServlet;
import com.gitblit.utils.ByteFormat;
import com.gitblit.utils.JGitUtils;
import com.gitblit.utils.StringUtils;
@@ -103,8 +104,8 @@ public class DocsPage extends RepositoryPage { WicketUtils.newPathParameter(repositoryName, commitId, doc.documentPath)));
item.add(new BookmarkablePageLink<Void>("historyLink", HistoryPage.class,
WicketUtils.newPathParameter(repositoryName, commitId, doc.documentPath)));
- item.add(new BookmarkablePageLink<Void>("rawLink", RawPage.class, WicketUtils.newPathParameter(
- repositoryName, commitId, doc.documentPath)));
+ String rawUrl = RawServlet.asLink(getContextUrl(), repositoryName, commitId, doc.documentPath);
+ item.add(new ExternalLink("rawLink", rawUrl));
// document content
String file = StringUtils.getLastPathElement(doc.documentPath);
@@ -145,8 +146,8 @@ public class DocsPage extends RepositoryPage { // links
item.add(new BookmarkablePageLink<Void>("view", DocPage.class, WicketUtils
.newPathParameter(repositoryName, id, entry.path)));
- item.add(new BookmarkablePageLink<Void>("raw", RawPage.class, WicketUtils
- .newPathParameter(repositoryName, id, entry.path)));
+ String rawUrl = RawServlet.asLink(getContextUrl(), repositoryName, id, entry.path);
+ item.add(new ExternalLink("raw", rawUrl));
item.add(new BookmarkablePageLink<Void>("blame", BlamePage.class, WicketUtils
.newPathParameter(repositoryName, id, entry.path)));
item.add(new BookmarkablePageLink<Void>("history", HistoryPage.class, WicketUtils
diff --git a/src/main/java/com/gitblit/wicket/pages/EditMilestonePage.html b/src/main/java/com/gitblit/wicket/pages/EditMilestonePage.html new file mode 100644 index 00000000..0897ebee --- /dev/null +++ b/src/main/java/com/gitblit/wicket/pages/EditMilestonePage.html @@ -0,0 +1,39 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+
+<wicket:extend>
+<body onload="document.getElementById('name').focus();">
+
+<div class="container">
+ <!-- page header -->
+ <div class="title" style="font-size: 22px; color: rgb(0, 32, 96);padding: 3px 0px 7px;">
+ <span class="project"><wicket:message key="gb.editMilestone"></wicket:message></span>
+ </div>
+
+ <form style="padding-top:5px;" wicket:id="editForm">
+ <div class="row">
+ <div class="span12">
+ <!-- Edit Milestone Table -->
+ <table class="ticket">
+ <tr><th><wicket:message key="gb.milestone"></wicket:message></th><td class="edit"><input class="input-large" type="text" wicket:id="name" id="name"></input></td></tr>
+ <tr><th><wicket:message key="gb.due"></wicket:message></th><td class="edit"><input class="input-large" type="text" wicket:id="due"></input> <span class="help-inline" wicket:id="dueFormat"></span></td></tr>
+ <tr><th><wicket:message key="gb.status"></wicket:message><span style="color:red;">*</span></th><td class="edit"><select class="input-large" wicket:id="status"></select></td></tr>
+ <tr><th></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="notify" /> <span class="help-inline"><wicket:message key="gb.notifyChangedOpenTickets"></wicket:message></span></label></td></tr>
+ </table>
+ </div>
+ </div>
+
+ <div class="row">
+ <div class="span12">
+ <div class="form-actions"><input class="btn btn-appmenu" type="submit" value="Save" wicket:message="value:gb.save" wicket:id="save" /> <input class="btn" type="submit" value="Cancel" wicket:message="value:gb.cancel" wicket:id="cancel" /> <input class="btn btn-danger" type="submit" value="Delete" wicket:message="value:gb.delete" wicket:id="delete" /></div>
+ </div>
+ </div>
+ </form>
+</div>
+</body>
+
+</wicket:extend>
+</html>
\ No newline at end of file diff --git a/src/main/java/com/gitblit/wicket/pages/EditMilestonePage.java b/src/main/java/com/gitblit/wicket/pages/EditMilestonePage.java new file mode 100644 index 00000000..4ed77d83 --- /dev/null +++ b/src/main/java/com/gitblit/wicket/pages/EditMilestonePage.java @@ -0,0 +1,196 @@ +/*
+ * 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.wicket.pages;
+
+import java.text.MessageFormat;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+
+import org.apache.wicket.PageParameters;
+import org.apache.wicket.RestartResponseException;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.markup.html.form.AjaxButton;
+import org.apache.wicket.extensions.markup.html.form.DateTextField;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.form.Button;
+import org.apache.wicket.markup.html.form.CheckBox;
+import org.apache.wicket.markup.html.form.DropDownChoice;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.markup.html.form.TextField;
+import org.apache.wicket.markup.html.link.Link;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+
+import com.gitblit.models.RepositoryModel;
+import com.gitblit.models.TicketModel;
+import com.gitblit.models.TicketModel.Status;
+import com.gitblit.models.UserModel;
+import com.gitblit.tickets.TicketMilestone;
+import com.gitblit.utils.StringUtils;
+import com.gitblit.wicket.GitBlitWebSession;
+import com.gitblit.wicket.WicketUtils;
+import com.gitblit.wicket.panels.BasePanel.JavascriptEventConfirmation;
+
+/**
+ * Page for creating a new milestone.
+ *
+ * @author James Moger
+ *
+ */
+public class EditMilestonePage extends RepositoryPage {
+
+ private final String oldName;
+
+ private IModel<String> nameModel;
+
+ private IModel<Date> dueModel;
+
+ private IModel<Status> statusModel;
+
+ private IModel<Boolean> notificationModel;
+
+ public EditMilestonePage(PageParameters params) {
+ super(params);
+
+ RepositoryModel model = getRepositoryModel();
+ if (!app().tickets().isAcceptingTicketUpdates(model)) {
+ // ticket service is read-only
+ throw new RestartResponseException(TicketsPage.class, WicketUtils.newOpenTicketsParameter(repositoryName));
+ }
+
+ UserModel currentUser = GitBlitWebSession.get().getUser();
+ if (currentUser == null) {
+ currentUser = UserModel.ANONYMOUS;
+ }
+
+ if (!currentUser.isAuthenticated || !currentUser.canAdmin(model)) {
+ // administration prohibited
+ throw new RestartResponseException(TicketsPage.class, WicketUtils.newOpenTicketsParameter(repositoryName));
+ }
+
+ oldName = WicketUtils.getObject(params);
+ if (StringUtils.isEmpty(oldName)) {
+ // milestone not specified
+ throw new RestartResponseException(TicketsPage.class, WicketUtils.newOpenTicketsParameter(repositoryName));
+ }
+
+ TicketMilestone tm = app().tickets().getMilestone(getRepositoryModel(), oldName);
+ if (tm == null) {
+ // milestone does not exist
+ throw new RestartResponseException(TicketsPage.class, WicketUtils.newOpenTicketsParameter(repositoryName));
+ }
+
+ setStatelessHint(false);
+ setOutputMarkupId(true);
+
+ Form<Void> form = new Form<Void>("editForm");
+ add(form);
+
+ nameModel = Model.of(tm.name);
+ dueModel = Model.of(tm.due);
+ statusModel = Model.of(tm.status);
+ notificationModel = Model.of(true);
+
+ form.add(new TextField<String>("name", nameModel));
+ form.add(new DateTextField("due", dueModel, "yyyy-MM-dd"));
+ form.add(new Label("dueFormat", "yyyy-MM-dd"));
+ form.add(new CheckBox("notify", notificationModel));
+
+ List<Status> statusChoices = Arrays.asList(Status.Open, Status.Closed);
+ form.add(new DropDownChoice<TicketModel.Status>("status", statusModel, statusChoices));
+
+ form.add(new AjaxButton("save") {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
+ String name = nameModel.getObject();
+ if (StringUtils.isEmpty(name)) {
+ return;
+ }
+
+ Date due = dueModel.getObject();
+ Status status = statusModel.getObject();
+ boolean rename = !name.equals(oldName);
+ boolean notify = notificationModel.getObject();
+
+ UserModel currentUser = GitBlitWebSession.get().getUser();
+ String createdBy = currentUser.username;
+
+ TicketMilestone tm = app().tickets().getMilestone(getRepositoryModel(), oldName);
+ tm.setName(name);
+ tm.setDue(due);
+ tm.status = status;
+
+ boolean success = true;
+ if (rename) {
+ success = app().tickets().renameMilestone(getRepositoryModel(), oldName, name, createdBy, notify);
+ }
+
+ if (success && app().tickets().updateMilestone(getRepositoryModel(), tm, createdBy)) {
+ setResponsePage(TicketsPage.class, WicketUtils.newOpenTicketsParameter(repositoryName));
+ } else {
+ // TODO error
+ }
+ }
+ });
+ Button cancel = new Button("cancel") {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public void onSubmit() {
+ setResponsePage(TicketsPage.class, WicketUtils.newOpenTicketsParameter(repositoryName));
+ }
+ };
+ cancel.setDefaultFormProcessing(false);
+ form.add(cancel);
+
+ Link<Void> delete = new Link<Void>("delete") {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public void onClick() {
+ UserModel currentUser = GitBlitWebSession.get().getUser();
+ String createdBy = currentUser.username;
+ boolean notify = notificationModel.getObject();
+
+ if (app().tickets().deleteMilestone(getRepositoryModel(), oldName, createdBy, notify)) {
+ setResponsePage(TicketsPage.class, WicketUtils.newOpenTicketsParameter(repositoryName));
+ } else {
+ error(MessageFormat.format(getString("gb.milestoneDeleteFailed"), oldName));
+ }
+ }
+ };
+
+ delete.add(new JavascriptEventConfirmation("onclick", MessageFormat.format(
+ getString("gb.deleteMilestone"), oldName)));
+
+ form.add(delete);
+ }
+
+ @Override
+ protected String getPageName() {
+ return getString("gb.editMilestone");
+ }
+
+ @Override
+ protected Class<? extends BasePage> getRepoNavPageClass() {
+ return TicketsPage.class;
+ }
+}
diff --git a/src/main/java/com/gitblit/wicket/pages/EditRepositoryPage.html b/src/main/java/com/gitblit/wicket/pages/EditRepositoryPage.html index f82ac3d5..1e683b4f 100644 --- a/src/main/java/com/gitblit/wicket/pages/EditRepositoryPage.html +++ b/src/main/java/com/gitblit/wicket/pages/EditRepositoryPage.html @@ -9,14 +9,17 @@ <form style="padding-top:5px;" wicket:id="editForm">
<div class="row">
<div class="span12">
-<div class="tabbable">
+<div class="tabbable tabs-left">
<!-- tab titles -->
<ul class="nav nav-tabs">
<li class="active"><a href="#general" data-toggle="tab"><wicket:message key="gb.general"></wicket:message></a></li>
- <li><a href="#permissions" data-toggle="tab"><wicket:message key="gb.accessPermissions"></wicket:message></a></li>
+ <li><a href="#permissions" data-toggle="tab"><wicket:message key="gb.permissions"></wicket:message></a></li>
+ <li><a href="#receive" data-toggle="tab"><wicket:message key="gb.receive"></wicket:message></a></li>
+ <li><a href="#tickets" data-toggle="tab"><wicket:message key="gb.tickets"></wicket:message></a></li>
<li><a href="#federation" data-toggle="tab"><wicket:message key="gb.federation"></wicket:message></a></li>
<li><a href="#search" data-toggle="tab"><wicket:message key="gb.search"></wicket:message></a></li>
- <li><a href="#hooks" data-toggle="tab"><wicket:message key="gb.hookScripts"></wicket:message></a></li>
+ <li><a href="#gc" data-toggle="tab"><wicket:message key="gb.gc"></wicket:message></a></li>
+ <li><a href="#misc" data-toggle="tab"><wicket:message key="gb.miscellaneous"></wicket:message></a></li>
</ul>
<!-- tab content -->
@@ -24,99 +27,167 @@ <!-- general tab -->
<div class="tab-pane active" id="general">
- <table class="plain">
- <tbody class="settings">
- <tr><th><wicket:message key="gb.name"></wicket:message></th><td class="edit"><input class="span4" type="text" wicket:id="name" id="name" size="40" tabindex="1" /> <span class="help-inline"><wicket:message key="gb.nameDescription"></wicket:message></span></td></tr>
- <tr><th><wicket:message key="gb.description"></wicket:message></th><td class="edit"><input class="span4" type="text" wicket:id="description" size="40" tabindex="2" /></td></tr>
- <tr><th colspan="2"><hr/></th></tr>
- <tr><th><wicket:message key="gb.origin"></wicket:message></th><td class="edit"><input class="span5" type="text" wicket:id="origin" size="80" tabindex="3" /></td></tr>
- <tr><th><wicket:message key="gb.headRef"></wicket:message></th><td class="edit"><select class="span3" wicket:id="HEAD" tabindex="4" /> <span class="help-inline"><wicket:message key="gb.headRefDescription"></wicket:message></span></td></tr>
- <tr><th><wicket:message key="gb.gcPeriod"></wicket:message></th><td class="edit"><select class="span2" wicket:id="gcPeriod" tabindex="5" /> <span class="help-inline"><wicket:message key="gb.gcPeriodDescription"></wicket:message></span></td></tr>
- <tr><th><wicket:message key="gb.gcThreshold"></wicket:message></th><td class="edit"><input class="span1" type="text" wicket:id="gcThreshold" tabindex="6" /> <span class="help-inline"><wicket:message key="gb.gcThresholdDescription"></wicket:message></span></td></tr>
- <tr><th colspan="2"><hr/></th></tr>
- <tr><th><wicket:message key="gb.acceptNewTickets"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="acceptNewTickets" tabindex="7" /> <span class="help-inline"><wicket:message key="gb.acceptNewTicketsDescription"></wicket:message></span></label></td></tr>
- <tr><th><wicket:message key="gb.acceptNewPatchsets"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="acceptNewPatchsets" tabindex="8" /> <span class="help-inline"><wicket:message key="gb.acceptNewPatchsetsDescription"></wicket:message></span></label></td></tr>
- <tr><th><wicket:message key="gb.requireApproval"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="requireApproval" tabindex="9" /> <span class="help-inline"><wicket:message key="gb.requireApprovalDescription"></wicket:message></span></label></td></tr>
- <tr><th><wicket:message key="gb.mergeTo"></wicket:message></th><td class="edit"><select class="span2" wicket:id="mergeTo" tabindex="10" /> <span class="help-inline"><wicket:message key="gb.mergeToDescription"></wicket:message></span></td></tr>
- <tr><th colspan="2"><hr/></th></tr>
- <tr><th><wicket:message key="gb.enableIncrementalPushTags"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="useIncrementalPushTags" tabindex="11" /> <span class="help-inline"><wicket:message key="gb.useIncrementalPushTagsDescription"></wicket:message></span></label></td></tr>
- <tr><th><wicket:message key="gb.showRemoteBranches"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="showRemoteBranches" tabindex="12" /> <span class="help-inline"><wicket:message key="gb.showRemoteBranchesDescription"></wicket:message></span></label></td></tr>
- <tr><th><wicket:message key="gb.skipSizeCalculation"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="skipSizeCalculation" tabindex="13" /> <span class="help-inline"><wicket:message key="gb.skipSizeCalculationDescription"></wicket:message></span></label></td></tr>
- <tr><th><wicket:message key="gb.skipSummaryMetrics"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="skipSummaryMetrics" tabindex="14" /> <span class="help-inline"><wicket:message key="gb.skipSummaryMetricsDescription"></wicket:message></span></label></td></tr>
- <tr><th><wicket:message key="gb.maxActivityCommits"></wicket:message></th><td class="edit"><select class="span2" wicket:id="maxActivityCommits" tabindex="15" /> <span class="help-inline"><wicket:message key="gb.maxActivityCommitsDescription"></wicket:message></span></td></tr>
- <tr><th><wicket:message key="gb.metricAuthorExclusions"></wicket:message></th><td class="edit"><input class="span8" type="text" wicket:id="metricAuthorExclusions" size="40" tabindex="16" /></td></tr>
- <tr><th><wicket:message key="gb.commitMessageRenderer"></wicket:message></th><td class="edit"><select class="span2" wicket:id="commitMessageRenderer" tabindex="17" /></td></tr>
- <tr><th colspan="2"><hr/></th></tr>
- <tr><th><wicket:message key="gb.mailingLists"></wicket:message></th><td class="edit"><input class="span8" type="text" wicket:id="mailingLists" size="40" tabindex="18" /></td></tr>
- </tbody>
- </table>
+
+ <div wicket:id="namePanel"></div>
+
+ <hr/>
+
+ <div wicket:id="accessPolicyPanel"></div>
+
</div>
<!-- access permissions -->
<div class="tab-pane" id="permissions">
+
+ <h4><wicket:message key="gb.owners"></wicket:message></h4>
+ <p><wicket:message key="gb.ownersDescription"></wicket:message></p>
+ <div wicket:id="owners"></div>
+
+ <hr />
+
+ <h4><wicket:message key="gb.userPermissions"></wicket:message></h4>
+ <p><wicket:message key="gb.userPermissionsDescription"></wicket:message></p>
+ <div wicket:id="users"></div>
+
+ <hr />
+
+ <h4><wicket:message key="gb.teamPermissions"></wicket:message></h4>
+ <p><wicket:message key="gb.teamPermissionsDescription"></wicket:message></p>
+ <div wicket:id="teams"></div>
+
+ </div>
+
+ <!-- receive -->
+ <div class="tab-pane" id="receive">
+ <h4><wicket:message key="gb.receiveSettings"></wicket:message></h4>
+ <p><wicket:message key="gb.receiveSettingsDescription"></wicket:message></p>
+
+ <hr/>
+
+ <div wicket:id="isFrozen"></div>
+ <div wicket:id="verifyCommitter"></div>
+ <div wicket:id="incrementalPushTags"></div>
+
+ <hr />
+
+ <h4><wicket:message key="gb.preReceiveScripts"></wicket:message></h4>
+ <p><wicket:message key="gb.preReceiveDescription"></wicket:message></p>
+
<table class="plain">
<tbody class="settings">
- <tr><th><wicket:message key="gb.owners"></wicket:message></th><td class="edit"><span wicket:id="owners" tabindex="19" /> </td></tr>
- <tr><th colspan="2"><hr/></th></tr>
- <tr><th><wicket:message key="gb.accessRestriction"></wicket:message></th><td class="edit"><select class="span4" wicket:id="accessRestriction" tabindex="20" /></td></tr>
- <tr><th colspan="2"><hr/></th></tr>
- <tr><th><wicket:message key="gb.authorizationControl"></wicket:message></th><td style="padding:2px;"><span class="authorizationControl" wicket:id="authorizationControl"></span></td></tr>
- <tr><th colspan="2"><hr/></th></tr>
- <tr><th><wicket:message key="gb.isFrozen"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="isFrozen" tabindex="21" /> <span class="help-inline"><wicket:message key="gb.isFrozenDescription"></wicket:message></span></label></td></tr>
- <tr><th><wicket:message key="gb.allowForks"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="allowForks" tabindex="22" /> <span class="help-inline"><wicket:message key="gb.allowForksDescription"></wicket:message></span></label></td></tr>
- <tr><th><wicket:message key="gb.verifyCommitter"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="verifyCommitter" tabindex="23" /> <span class="help-inline"><wicket:message key="gb.verifyCommitterDescription"></wicket:message></span><br/><span class="help-inline" style="padding-left:10px;"><wicket:message key="gb.verifyCommitterNote"></wicket:message></span></label></td></tr>
- <tr><th colspan="2"><hr/></th></tr>
- <tr><th><wicket:message key="gb.userPermissions"></wicket:message></th><td style="padding:2px;"><span wicket:id="users"></span></td></tr>
- <tr><th colspan="2"><hr/></th></tr>
- <tr><th><wicket:message key="gb.teamPermissions"></wicket:message></th><td style="padding:2px;"><span wicket:id="teams"></span></td></tr>
+ <tr>
+ <td style="padding:2px;"><span wicket:id="preReceiveScripts"></span></td>
+ <td style="vertical-align: top;"><span wicket:id="inheritedPreReceive"></span></td>
+ </tr>
</tbody>
</table>
- </div>
- <!-- federation -->
- <div class="tab-pane" id="federation">
+ <hr />
+
+ <h4><wicket:message key="gb.postReceiveScripts"></wicket:message></h4>
+ <p><wicket:message key="gb.postReceiveDescription"></wicket:message></p>
+
<table class="plain">
<tbody class="settings">
- <tr><th><wicket:message key="gb.federationStrategy"></wicket:message></th><td class="edit"><select class="span4" wicket:id="federationStrategy" tabindex="24" /></td></tr>
- <tr><th><wicket:message key="gb.federationSets"></wicket:message></th><td style="padding:2px;"><span wicket:id="federationSets"></span></td></tr>
+ <tr>
+ <td style="padding:2px;"><span wicket:id="postReceiveScripts"></span></td>
+ <td style="vertical-align: top;"><span wicket:id="inheritedPostReceive"></span></td>
+ </tr>
</tbody>
</table>
+
+ <div wicket:id="customFieldsSection">
+ <hr />
+
+ <h4><wicket:message key="gb.customFields"></wicket:message></h4>
+ <p><wicket:message key="gb.customFieldsDescription"></wicket:message></p>
+ <table class="plain">
+ <tbody class="settings">
+ <tr wicket:id="customFieldsListView"><th><span wicket:id="customFieldLabel"></span></th><td class="edit"><input class="span8" type="text" wicket:id="customFieldValue" /></td></tr>
+ </tbody>
+ </table>
+ </div>
+
+ </div>
+
+ <!-- tickets tab -->
+ <div class="tab-pane" id="tickets">
+
+ <h4><wicket:message key="gb.ticketSettings"></wicket:message></h4>
+ <p><wicket:message key="gb.ticketsWelcome"></wicket:message></p>
+
+ <hr/>
+
+ <div wicket:id="acceptNewPatchsets"></div>
+ <div wicket:id="acceptNewTickets"></div>
+ <div wicket:id="requireApproval"></div>
+ <div wicket:id="mergeTo"></div>
+
+ </div>
+
+ <!-- federation -->
+ <div class="tab-pane" id="federation">
+ <h4><wicket:message key="gb.federation"></wicket:message></h4>
+ <p><wicket:message key="gb.federationRepositoryDescription"></wicket:message></p>
+
+ <hr/>
+
+ <div wicket:id="federationStrategy"></div>
+
+ <hr />
+
+ <h4><wicket:message key="gb.federationSets"></wicket:message></h4>
+ <p><wicket:message key="gb.federationSetsDescription"></wicket:message></p>
+
+ <div wicket:id="federationSets"></div>
</div>
<!-- search -->
<div class="tab-pane" id="search">
- <table class="plain">
- <tbody class="settings">
- <tr><th><wicket:message key="gb.indexedBranches"></wicket:message></th><td style="padding:2px;"><span wicket:id="indexedBranches"></span></td></tr>
- </tbody>
- </table>
+
+ <h4><wicket:message key="gb.indexedBranches"></wicket:message></h4>
+ <p><wicket:message key="gb.indexedBranchesDescription"></wicket:message></p>
+
+ <div wicket:id="indexedBranches"></div>
</div>
- <!-- hooks -->
- <div class="tab-pane" id="hooks">
- <table class="plain">
- <tbody class="settings">
- <tr><th><wicket:message key="gb.preReceiveScripts"></wicket:message><p></p><span wicket:id="inheritedPreReceive"></span></th><td style="padding:2px;"><span wicket:id="preReceiveScripts"></span></td></tr>
- <tr><th><wicket:message key="gb.postReceiveScripts"></wicket:message><p></p><span wicket:id="inheritedPostReceive"></span></th><td style="padding:2px;"><span wicket:id="postReceiveScripts"></span></td></tr>
- <div wicket:id="customFieldsSection">
- <tr><td colspan="2"><h3><wicket:message key="gb.customFields"></wicket:message> <small><wicket:message key="gb.customFieldsDescription"></wicket:message></small></h3></td></tr>
- <tr wicket:id="customFieldsListView"><th><span wicket:id="customFieldLabel"></span></th><td class="edit"><input class="span8" type="text" wicket:id="customFieldValue" /></td></tr>
- </div>
- </tbody>
- </table>
+ <!-- garbage collection -->
+ <div class="tab-pane" id="gc">
+
+ <h4><wicket:message key="gb.garbageCollection"></wicket:message></h4>
+ <p><wicket:message key="gb.garbageCollectionDescription"></wicket:message></p>
+
+ <div wicket:id="gcPeriod"></div>
+ <div wicket:id="gcThreshold"></div>
</div>
- </div>
-</div>
-</div>
-</div>
+
+ <!-- misc -->
+ <div class="tab-pane" id="misc">
-<div class="row">
-<div class="span12">
- <div class="form-actions"><input class="btn btn-appmenu" type="submit" value="Save" wicket:message="value:gb.save" wicket:id="save" /> <input class="btn" type="submit" value="Cancel" wicket:message="value:gb.cancel" wicket:id="cancel" /></div>
+ <div wicket:id="origin"></div>
+ <div wicket:id="head"></div>
+
+ <hr/>
+
+ <div wicket:id="showRemoteBranches"></div>
+ <div wicket:id="skipSizeCalculation"></div>
+ <div wicket:id="skipSummaryMetrics"></div>
+ <div wicket:id="maxActivityCommits"></div>
+ <div wicket:id="commitMessageRenderer"></div>
+ <div wicket:id="metricAuthorExclusions"></div>
+ <div wicket:id="mailingLists"></div>
+
+ </div>
+
+ <div class="form-actions"><input class="btn btn-appmenu" type="submit" value="Save" wicket:message="value:gb.save" wicket:id="save" /> <input class="btn" type="submit" value="Cancel" wicket:message="value:gb.cancel" wicket:id="cancel" /> <input class="btn btn-danger" type="submit" value="Delete" wicket:message="value:gb.delete" wicket:id="delete" /></div>
+
+ </div>
+ </div>
</div>
</div>
</form>
</body>
+
</wicket:extend>
</html>
\ No newline at end of file diff --git a/src/main/java/com/gitblit/wicket/pages/EditRepositoryPage.java b/src/main/java/com/gitblit/wicket/pages/EditRepositoryPage.java index cd0da5f4..a1c37385 100644 --- a/src/main/java/com/gitblit/wicket/pages/EditRepositoryPage.java +++ b/src/main/java/com/gitblit/wicket/pages/EditRepositoryPage.java @@ -29,7 +29,6 @@ import java.util.Set; import org.apache.wicket.PageParameters;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.form.AjaxFormChoiceComponentUpdatingBehavior;
-import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
import org.apache.wicket.behavior.SimpleAttributeModifier;
import org.apache.wicket.extensions.markup.html.form.palette.Palette;
import org.apache.wicket.markup.html.WebMarkupContainer;
@@ -40,13 +39,14 @@ import org.apache.wicket.markup.html.form.ChoiceRenderer; import org.apache.wicket.markup.html.form.DropDownChoice;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.IChoiceRenderer;
-import org.apache.wicket.markup.html.form.RadioChoice;
import org.apache.wicket.markup.html.form.TextField;
+import org.apache.wicket.markup.html.link.Link;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.markup.html.list.ListView;
import org.apache.wicket.model.CompoundPropertyModel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
+import org.apache.wicket.model.PropertyModel;
import org.apache.wicket.model.util.CollectionModel;
import org.apache.wicket.model.util.ListModel;
import org.eclipse.jgit.lib.Repository;
@@ -68,13 +68,23 @@ import com.gitblit.utils.StringUtils; import com.gitblit.wicket.GitBlitWebSession;
import com.gitblit.wicket.StringChoiceRenderer;
import com.gitblit.wicket.WicketUtils;
+import com.gitblit.wicket.panels.AccessPolicyPanel;
+import com.gitblit.wicket.panels.BasePanel.JavascriptEventConfirmation;
+import com.gitblit.wicket.panels.BooleanOption;
import com.gitblit.wicket.panels.BulletListPanel;
+import com.gitblit.wicket.panels.ChoiceOption;
import com.gitblit.wicket.panels.RegistrantPermissionsPanel;
+import com.gitblit.wicket.panels.RepositoryNamePanel;
+import com.gitblit.wicket.panels.TextOption;
public class EditRepositoryPage extends RootSubPage {
private final boolean isCreate;
+ RepositoryNamePanel namePanel;
+
+ AccessPolicyPanel accessPolicyPanel;
+
private boolean isAdmin;
RepositoryModel repositoryModel;
@@ -194,7 +204,7 @@ public class EditRepositoryPage extends RootSubPage { }
}
final Palette<UserChoice> ownersPalette = new Palette<UserChoice>("owners", new ListModel<UserChoice>(owners), new CollectionModel<UserChoice>(
- persons), new ChoiceRenderer<UserChoice>(null, "userId"), 12, true);
+ persons), new ChoiceRenderer<UserChoice>(null, "userId"), 12, false);
// indexed local branches palette
List<String> allLocalBranches = new ArrayList<String>();
@@ -261,60 +271,9 @@ public class EditRepositoryPage extends RootSubPage { @Override
protected void onSubmit() {
try {
- // confirm a repository name was entered
- if (repositoryModel.name == null && StringUtils.isEmpty(repositoryModel.name)) {
- error(getString("gb.pleaseSetRepositoryName"));
- return;
- }
-
- // ensure name is trimmed
- repositoryModel.name = repositoryModel.name.trim();
-
- // automatically convert backslashes to forward slashes
- repositoryModel.name = repositoryModel.name.replace('\\', '/');
- // Automatically replace // with /
- repositoryModel.name = repositoryModel.name.replace("//", "/");
-
- // prohibit folder paths
- if (repositoryModel.name.startsWith("/")) {
- error(getString("gb.illegalLeadingSlash"));
+ if (!namePanel.updateModel(repositoryModel)) {
return;
}
- if (repositoryModel.name.startsWith("../")) {
- error(getString("gb.illegalRelativeSlash"));
- return;
- }
- if (repositoryModel.name.contains("/../")) {
- error(getString("gb.illegalRelativeSlash"));
- return;
- }
- if (repositoryModel.name.endsWith("/")) {
- repositoryModel.name = repositoryModel.name.substring(0, repositoryModel.name.length() - 1);
- }
-
- // confirm valid characters in repository name
- Character c = StringUtils.findInvalidCharacter(repositoryModel.name);
- if (c != null) {
- error(MessageFormat.format(getString("gb.illegalCharacterRepositoryName"),
- c));
- return;
- }
-
- if (user.canCreate() && !user.canAdmin() && allowEditName) {
- // ensure repository name begins with the user's path
- if (!repositoryModel.name.startsWith(user.getPersonalPath())) {
- error(MessageFormat.format(getString("gb.illegalPersonalRepositoryLocation"),
- user.getPersonalPath()));
- return;
- }
-
- if (repositoryModel.name.equals(user.getPersonalPath())) {
- // reset path prefix and show error
- repositoryModel.name = user.getPersonalPath() + "/";
- error(getString("gb.pleaseSetRepositoryName"));
- return;
- }
- }
// confirm access restriction selection
if (repositoryModel.accessRestriction == null) {
@@ -427,30 +386,11 @@ public class EditRepositoryPage extends RootSubPage { return;
}
setRedirect(false);
- if (isCreate) {
- setResponsePage(RepositoriesPage.class);
- } else {
- setResponsePage(SummaryPage.class, WicketUtils.newRepositoryParameter(repositoryModel.name));
- }
+ setResponsePage(SummaryPage.class, WicketUtils.newRepositoryParameter(repositoryModel.name));
}
};
- // do not let the browser pre-populate these fields
- form.add(new SimpleAttributeModifier("autocomplete", "off"));
-
- // field names reflective match RepositoryModel fields
- form.add(new TextField<String>("name").setEnabled(allowEditName));
- form.add(new TextField<String>("description"));
- form.add(ownersPalette);
- form.add(new CheckBox("allowForks").setEnabled(app().settings().getBoolean(Keys.web.allowForking, true)));
- DropDownChoice<AccessRestrictionType> accessRestriction = new DropDownChoice<AccessRestrictionType>("accessRestriction",
- AccessRestrictionType.choices(app().settings().getBoolean(Keys.git.allowAnonymousPushes, false)), new AccessRestrictionRenderer());
- form.add(accessRestriction);
- form.add(new CheckBox("isFrozen"));
- // TODO enable origin definition
- form.add(new TextField<String>("origin").setEnabled(false/* isCreate */));
-
- // allow relinking HEAD to a branch or tag other than master on edit repository
+ // Determine available refs & branches
List<String> availableRefs = new ArrayList<String>();
List<String> availableBranches = new ArrayList<String>();
if (!ArrayUtils.isEmpty(repositoryModel.availableRefs)) {
@@ -463,58 +403,79 @@ public class EditRepositoryPage extends RootSubPage { }
}
}
- form.add(new DropDownChoice<String>("HEAD", availableRefs).setEnabled(availableRefs.size() > 0));
- boolean gcEnabled = app().settings().getBoolean(Keys.git.enableGarbageCollection, false);
- int defaultGcPeriod = app().settings().getInteger(Keys.git.defaultGarbageCollectionPeriod, 7);
- if (repositoryModel.gcPeriod == 0) {
- repositoryModel.gcPeriod = defaultGcPeriod;
- }
- List<Integer> gcPeriods = Arrays.asList(1, 2, 3, 4, 5, 7, 10, 14 );
- form.add(new DropDownChoice<Integer>("gcPeriod", gcPeriods, new GCPeriodRenderer()).setEnabled(gcEnabled));
- form.add(new TextField<String>("gcThreshold").setEnabled(gcEnabled));
+ // do not let the browser pre-populate these fields
+ form.add(new SimpleAttributeModifier("autocomplete", "off"));
- // federation strategies - remove ORIGIN choice if this repository has
- // no origin.
- List<FederationStrategy> federationStrategies = new ArrayList<FederationStrategy>(
- Arrays.asList(FederationStrategy.values()));
- if (StringUtils.isEmpty(repositoryModel.origin)) {
- federationStrategies.remove(FederationStrategy.FEDERATE_ORIGIN);
- }
- form.add(new DropDownChoice<FederationStrategy>("federationStrategy", federationStrategies,
- new FederationTypeRenderer()));
- form.add(new CheckBox("acceptNewPatchsets"));
- form.add(new CheckBox("acceptNewTickets")); - form.add(new CheckBox("requireApproval"));
- form.add(new DropDownChoice<String>("mergeTo", availableBranches).setEnabled(availableBranches.size() > 0));
- form.add(new CheckBox("useIncrementalPushTags"));
- form.add(new CheckBox("showRemoteBranches"));
- form.add(new CheckBox("skipSizeCalculation"));
- form.add(new CheckBox("skipSummaryMetrics"));
- List<Integer> maxActivityCommits = Arrays.asList(-1, 0, 25, 50, 75, 100, 150, 200, 250, 500);
- form.add(new DropDownChoice<Integer>("maxActivityCommits", maxActivityCommits, new MaxActivityCommitsRenderer()));
- metricAuthorExclusions = new Model<String>(ArrayUtils.isEmpty(repositoryModel.metricAuthorExclusions) ? ""
- : StringUtils.flattenStrings(repositoryModel.metricAuthorExclusions, " "));
- form.add(new TextField<String>("metricAuthorExclusions", metricAuthorExclusions));
+ //
+ //
+ // GENERAL
+ //
+ namePanel = new RepositoryNamePanel("namePanel", repositoryModel);
+ namePanel.setEditable(allowEditName);
+ form.add(namePanel);
- mailingLists = new Model<String>(ArrayUtils.isEmpty(repositoryModel.mailingLists) ? ""
- : StringUtils.flattenStrings(repositoryModel.mailingLists, " "));
- form.add(new TextField<String>("mailingLists", mailingLists));
- form.add(indexedBranchesPalette);
+ // XXX AccessPolicyPanel is defined later.
- List<AuthorizationControl> acList = Arrays.asList(AuthorizationControl.values());
- final RadioChoice<AuthorizationControl> authorizationControl = new RadioChoice<Constants.AuthorizationControl>(
- "authorizationControl", acList, new AuthorizationControlRenderer());
- form.add(authorizationControl);
+ form.add(new ChoiceOption<String>("head",
+ getString("gb.headRef"),
+ getString("gb.headRefDescription"),
+ new PropertyModel<String>(repositoryModel, "HEAD"),
+ availableRefs));
- final CheckBox verifyCommitter = new CheckBox("verifyCommitter");
- verifyCommitter.setOutputMarkupId(true);
- form.add(verifyCommitter);
+ //
+ // PERMISSIONS
+ //
+ form.add(ownersPalette);
form.add(usersPalette);
form.add(teamsPalette);
- form.add(federationSetsPalette);
+
+ //
+ // TICKETS
+ //
+ form.add(new BooleanOption("acceptNewPatchsets",
+ getString("gb.acceptNewPatchsets"),
+ getString("gb.acceptNewPatchsetsDescription"),
+ new PropertyModel<Boolean>(repositoryModel, "acceptNewPatchsets")));
+
+ form.add(new BooleanOption("acceptNewTickets",
+ getString("gb.acceptNewTickets"),
+ getString("gb.acceptNewTicketsDescription"),
+ new PropertyModel<Boolean>(repositoryModel, "acceptNewPatchsets")));
+
+ form.add(new BooleanOption("requireApproval",
+ getString("gb.requireApproval"),
+ getString("gb.requireApprovalDescription"),
+ new PropertyModel<Boolean>(repositoryModel, "requireApproval")));
+
+ form.add(new ChoiceOption<String>("mergeTo",
+ getString("gb.mergeTo"),
+ getString("gb.mergeToDescription"),
+ new PropertyModel<String>(repositoryModel, "mergeTo"),
+ availableBranches));
+
+ //
+ // RECEIVE
+ //
+ form.add(new BooleanOption("isFrozen",
+ getString("gb.isFrozen"),
+ getString("gb.isFrozenDescription"),
+ new PropertyModel<Boolean>(repositoryModel, "isFrozen")));
+
+ form.add(new BooleanOption("incrementalPushTags",
+ getString("gb.enableIncrementalPushTags"),
+ getString("gb.useIncrementalPushTagsDescription"),
+ new PropertyModel<Boolean>(repositoryModel, "useIncrementalPushTags")));
+
+ final CheckBox verifyCommitter = new CheckBox("checkbox", new PropertyModel<Boolean>(repositoryModel, "verifyCommitter"));
+ verifyCommitter.setOutputMarkupId(true);
+ form.add(new BooleanOption("verifyCommitter",
+ getString("gb.verifyCommitter"),
+ getString("gb.verifyCommitterDescription") + "<br/>" + getString("gb.verifyCommitterNote"),
+ verifyCommitter).setIsHtmlDescription(true));
+
form.add(preReceivePalette);
form.add(new BulletListPanel("inheritedPreReceive", getString("gb.inherited"), app().repositories()
.getPreReceiveScriptsInherited(repositoryModel)));
@@ -526,17 +487,125 @@ public class EditRepositoryPage extends RootSubPage { customFieldsSection.add(customFieldsListView);
form.add(customFieldsSection.setVisible(!app().settings().getString(Keys.groovy.customFields, "").isEmpty()));
+ //
+ // FEDERATION
+ //
+ List<FederationStrategy> federationStrategies = new ArrayList<FederationStrategy>(
+ Arrays.asList(FederationStrategy.values()));
+ // federation strategies - remove ORIGIN choice if this repository has no origin.
+ if (StringUtils.isEmpty(repositoryModel.origin)) {
+ federationStrategies.remove(FederationStrategy.FEDERATE_ORIGIN);
+ }
+
+ form.add(new ChoiceOption<FederationStrategy>("federationStrategy",
+ getString("gb.federationStrategy"),
+ getString("gb.federationStrategyDescription"),
+ new DropDownChoice<FederationStrategy>(
+ "choice",
+ new PropertyModel<FederationStrategy>(repositoryModel, "federationStrategy"),
+ federationStrategies,
+ new FederationTypeRenderer())));
+
+ form.add(federationSetsPalette);
+
+ //
+ // SEARCH
+ //
+ form.add(indexedBranchesPalette);
+
+ //
+ // GARBAGE COLLECTION
+ //
+ boolean gcEnabled = app().settings().getBoolean(Keys.git.enableGarbageCollection, false);
+ int defaultGcPeriod = app().settings().getInteger(Keys.git.defaultGarbageCollectionPeriod, 7);
+ if (repositoryModel.gcPeriod == 0) {
+ repositoryModel.gcPeriod = defaultGcPeriod;
+ }
+ List<Integer> gcPeriods = Arrays.asList(1, 2, 3, 4, 5, 7, 10, 14 );
+ form.add(new ChoiceOption<Integer>("gcPeriod",
+ getString("gb.gcPeriod"),
+ getString("gb.gcPeriodDescription"),
+ new DropDownChoice<Integer>("choice",
+ new PropertyModel<Integer>(repositoryModel, "gcPeriod"),
+ gcPeriods,
+ new GCPeriodRenderer())).setEnabled(gcEnabled));
+
+ form.add(new TextOption("gcThreshold",
+ getString("gb.gcThreshold"),
+ getString("gb.gcThresholdDescription"),
+ "span1",
+ new PropertyModel<String>(repositoryModel, "gcThreshold")).setEnabled(gcEnabled));
+
+ //
+ // MISCELLANEOUS
+ //
+
+ form.add(new TextOption("origin",
+ getString("gb.origin"),
+ getString("gb.originDescription"),
+ "span6",
+ new PropertyModel<String>(repositoryModel, "origin")).setEnabled(false));
+
+ form.add(new BooleanOption("showRemoteBranches",
+ getString("gb.showRemoteBranches"),
+ getString("gb.showRemoteBranchesDescription"),
+ new PropertyModel<Boolean>(repositoryModel, "showRemoteBranches")));
+
+ form.add(new BooleanOption("skipSizeCalculation",
+ getString("gb.skipSizeCalculation"),
+ getString("gb.skipSizeCalculationDescription"),
+ new PropertyModel<Boolean>(repositoryModel, "skipSizeCalculation")));
+
+ form.add(new BooleanOption("skipSummaryMetrics",
+ getString("gb.skipSummaryMetrics"),
+ getString("gb.skipSummaryMetricsDescription"),
+ new PropertyModel<Boolean>(repositoryModel, "skipSummaryMetrics")));
+
+ List<Integer> maxActivityCommits = Arrays.asList(-1, 0, 25, 50, 75, 100, 150, 200, 250, 500);
+ form.add(new ChoiceOption<Integer>("maxActivityCommits",
+ getString("gb.maxActivityCommits"),
+ getString("gb.maxActivityCommitsDescription"),
+ new DropDownChoice<Integer>("choice",
+ new PropertyModel<Integer>(repositoryModel, "maxActivityCommits"),
+ maxActivityCommits,
+ new MaxActivityCommitsRenderer())));
+
+ List<CommitMessageRenderer> renderers = Arrays.asList(CommitMessageRenderer.values());
+ form.add(new ChoiceOption<CommitMessageRenderer>("commitMessageRenderer",
+ getString("gb.commitMessageRenderer"),
+ getString("gb.commitMessageRendererDescription"),
+ new DropDownChoice<CommitMessageRenderer>("choice",
+ new PropertyModel<CommitMessageRenderer>(repositoryModel, "commitMessageRenderer"),
+ renderers)));
+
+ metricAuthorExclusions = new Model<String>(ArrayUtils.isEmpty(repositoryModel.metricAuthorExclusions) ? ""
+ : StringUtils.flattenStrings(repositoryModel.metricAuthorExclusions, " "));
+
+ form.add(new TextOption("metricAuthorExclusions",
+ getString("gb.metricAuthorExclusions"),
+ getString("gb.metricAuthorExclusions"),
+ "span6",
+ metricAuthorExclusions));
+
+ mailingLists = new Model<String>(ArrayUtils.isEmpty(repositoryModel.mailingLists) ? ""
+ : StringUtils.flattenStrings(repositoryModel.mailingLists, " "));
+
+ form.add(new TextOption("mailingLists",
+ getString("gb.mailingLists"),
+ getString("gb.mailingLists"),
+ "span6",
+ mailingLists));
+
+
// initial enable/disable of permission controls
if (repositoryModel.accessRestriction.equals(AccessRestrictionType.NONE)) {
// anonymous everything, disable all controls
usersPalette.setEnabled(false);
teamsPalette.setEnabled(false);
- authorizationControl.setEnabled(false);
verifyCommitter.setEnabled(false);
} else {
// authenticated something
// enable authorization controls
- authorizationControl.setEnabled(true);
verifyCommitter.setEnabled(true);
boolean allowFineGrainedControls = repositoryModel.authorizationControl.equals(AuthorizationControl.NAMED);
@@ -544,15 +613,18 @@ public class EditRepositoryPage extends RootSubPage { teamsPalette.setEnabled(allowFineGrainedControls);
}
- accessRestriction.add(new AjaxFormComponentUpdatingBehavior("onchange") {
+ //
+ // ACCESS POLICY PANEL (GENERAL)
+ //
+ AjaxFormChoiceComponentUpdatingBehavior callback = new AjaxFormChoiceComponentUpdatingBehavior() {
private static final long serialVersionUID = 1L;
@Override
protected void onUpdate(AjaxRequestTarget target) {
- // enable/disable permissions panel based on access restriction
+ accessPolicyPanel.updateModel(repositoryModel);
+
boolean allowAuthorizationControl = repositoryModel.accessRestriction.exceeds(AccessRestrictionType.NONE);
- authorizationControl.setEnabled(allowAuthorizationControl);
verifyCommitter.setEnabled(allowAuthorizationControl);
boolean allowFineGrainedControls = allowAuthorizationControl && repositoryModel.authorizationControl.equals(AuthorizationControl.NAMED);
@@ -563,41 +635,19 @@ public class EditRepositoryPage extends RootSubPage { repositoryModel.authorizationControl = AuthorizationControl.NAMED;
}
- target.addComponent(authorizationControl);
target.addComponent(verifyCommitter);
target.addComponent(usersPalette);
target.addComponent(teamsPalette);
}
- });
-
- authorizationControl.add(new AjaxFormChoiceComponentUpdatingBehavior() {
-
- private static final long serialVersionUID = 1L;
-
- @Override
- protected void onUpdate(AjaxRequestTarget target) {
- // enable/disable permissions panel based on access restriction
- boolean allowAuthorizationControl = repositoryModel.accessRestriction.exceeds(AccessRestrictionType.NONE);
- authorizationControl.setEnabled(allowAuthorizationControl);
-
- boolean allowFineGrainedControls = allowAuthorizationControl && repositoryModel.authorizationControl.equals(AuthorizationControl.NAMED);
- usersPalette.setEnabled(allowFineGrainedControls);
- teamsPalette.setEnabled(allowFineGrainedControls);
+ };
- if (allowFineGrainedControls) {
- repositoryModel.authorizationControl = AuthorizationControl.NAMED;
- }
+ accessPolicyPanel = new AccessPolicyPanel("accessPolicyPanel", repositoryModel, callback);
+ form.add(accessPolicyPanel);
- target.addComponent(authorizationControl);
- target.addComponent(usersPalette);
- target.addComponent(teamsPalette);
- }
- });
-
- List<CommitMessageRenderer> renderers = Arrays.asList(CommitMessageRenderer.values());
- DropDownChoice<CommitMessageRenderer> messageRendererChoice = new DropDownChoice<CommitMessageRenderer>("commitMessageRenderer", renderers);
- form.add(messageRendererChoice);
+ //
+ // FORM CONTROLS
+ //
form.add(new Button("save"));
Button cancel = new Button("cancel") {
private static final long serialVersionUID = 1L;
@@ -614,6 +664,46 @@ public class EditRepositoryPage extends RootSubPage { cancel.setDefaultFormProcessing(false);
form.add(cancel);
+ // the user can delete if deletions are allowed AND the user is an admin or the personal owner
+ // assigned ownership is not sufficient to allow deletion
+ boolean canDelete = !isCreate && app().repositories().canDelete(repositoryModel)
+ && (user.canAdmin() || user.isMyPersonalRepository(repositoryModel.name));
+
+ Link<Void> delete = new Link<Void>("delete") {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public void onClick() {
+ RepositoryModel latestModel = app().repositories().getRepositoryModel(repositoryModel.name);
+ boolean canDelete = app().repositories().canDelete(latestModel);
+ if (canDelete) {
+ if (app().repositories().deleteRepositoryModel(latestModel)) {
+ info(MessageFormat.format(getString("gb.repositoryDeleted"), latestModel));
+ if (latestModel.isPersonalRepository()) {
+ // redirect to user's profile page
+ String prefix = app().settings().getString(Keys.git.userRepositoryPrefix, "~");
+ String username = latestModel.projectPath.substring(prefix.length());
+ setResponsePage(UserPage.class, WicketUtils.newUsernameParameter(username));
+ } else {
+ // redirect to server repositories page
+ setResponsePage(RepositoriesPage.class);
+ }
+ } else {
+ error(MessageFormat.format(getString("gb.repositoryDeleteFailed"), latestModel));
+ }
+ } else {
+ error(MessageFormat.format(getString("gb.repositoryDeleteFailed"), latestModel));
+ }
+ }
+ };
+
+ if (canDelete) {
+ delete.add(new JavascriptEventConfirmation("onclick", MessageFormat.format(
+ getString("gb.deleteRepository"), repositoryModel)));
+ }
+ form.add(delete.setVisible(canDelete));
+
add(form);
}
@@ -663,26 +753,6 @@ public class EditRepositoryPage extends RootSubPage { }
}
- private class AccessRestrictionRenderer implements IChoiceRenderer<AccessRestrictionType> {
-
- private static final long serialVersionUID = 1L;
-
- private final Map<AccessRestrictionType, String> map;
-
- public AccessRestrictionRenderer() {
- map = getAccessRestrictions();
- }
-
- @Override
- public String getDisplayValue(AccessRestrictionType type) {
- return map.get(type);
- }
-
- @Override
- public String getIdValue(AccessRestrictionType type, int index) {
- return Integer.toString(index);
- }
- }
private class FederationTypeRenderer implements IChoiceRenderer<FederationStrategy> {
@@ -705,27 +775,6 @@ public class EditRepositoryPage extends RootSubPage { }
}
- private class AuthorizationControlRenderer implements IChoiceRenderer<AuthorizationControl> {
-
- private static final long serialVersionUID = 1L;
-
- private final Map<AuthorizationControl, String> map;
-
- public AuthorizationControlRenderer() {
- map = getAuthorizationControls();
- }
-
- @Override
- public String getDisplayValue(AuthorizationControl type) {
- return map.get(type);
- }
-
- @Override
- public String getIdValue(AuthorizationControl type, int index) {
- return Integer.toString(index);
- }
- }
-
private class GCPeriodRenderer implements IChoiceRenderer<Integer> {
private static final long serialVersionUID = 1L;
diff --git a/src/main/java/com/gitblit/wicket/pages/EditTeamPage.java b/src/main/java/com/gitblit/wicket/pages/EditTeamPage.java index a0d11a05..f537f33d 100644 --- a/src/main/java/com/gitblit/wicket/pages/EditTeamPage.java +++ b/src/main/java/com/gitblit/wicket/pages/EditTeamPage.java @@ -86,7 +86,7 @@ public class EditTeamPage extends RootSubPage { @Override
protected Class<? extends BasePage> getRootNavPageClass() {
- return UsersPage.class;
+ return TeamsPage.class;
}
protected void setupPage(final TeamModel teamModel) {
@@ -214,7 +214,7 @@ public class EditTeamPage extends RootSubPage { teamModel.name));
}
// back to users page
- setResponsePage(UsersPage.class);
+ setResponsePage(TeamsPage.class);
}
};
@@ -249,7 +249,7 @@ public class EditTeamPage extends RootSubPage { @Override
public void onSubmit() {
- setResponsePage(UsersPage.class);
+ setResponsePage(TeamsPage.class);
}
};
cancel.setDefaultFormProcessing(false);
diff --git a/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage.html b/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage.html index c5f9bc95..31226ff5 100644 --- a/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage.html +++ b/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage.html @@ -7,52 +7,53 @@ <body>
<wicket:extend>
<div class="container">
- <h2>Empty Repository</h2>
- <p></p>
- <div class="row">
- <div class="span10">
- <div class="alert alert-success">
- <span wicket:id="repository" style="font-weight: bold;">[repository]</span> is an empty repository and can not be viewed by Gitblit.
- <p></p>
- Please push some commits to <span wicket:id="pushurl"></span>
- <p></p>
- <hr/>
- After you have pushed commits you may <b>refresh</b> this page to view your repository.
- </div>
- </div>
- </div>
+<div class="markdown">
+<div class="row">
+<div class="span10 offset1">
+ <h3><center>Empty Repository</center></h3>
+ <div class="alert alert-info">
+ <span wicket:id="repository" style="font-weight: bold;">[repository]</span> is an empty repository and can not be viewed by Gitblit.
+ <p></p>
+ Please push some commits to <span wicket:id="pushurl"></span>
+ <hr/>
+ After you have pushed commits you may <b>refresh</b> this page to view your repository.
+ </div>
- <h3>Git Command-Line Syntax</h3>
- <span style="padding-bottom:5px;">If you do not have a local Git repository, then you should clone this repository, commit some files, and then push your commits back to Gitblit.</span>
- <p></p>
- <pre style="padding: 5px 30px;" wicket:id="cloneSyntax"></pre>
- <p></p>
- <span style="padding-bottom:5px;">If you already have a local Git repository with commits, then you may add this repository as a remote and push to it.</span>
- <p></p>
- <pre wicket:id="remoteSyntax" style="padding: 5px 30px;"></pre>
- <p></p>
- <span style="padding-bottom:5px;">If your repository is meant to be kept in sync with an upstream repository, then you may add it.</span>
- <p></p>
- <pre wicket:id="upstreamSyntax" style="padding: 5px 30px;"></pre>
- <p></p>
- <h3>Learn Git</h3>
- If you are unsure how to use this information, consider reviewing the <a href="http://book.git-scm.com">Git Community Book</a> or <a href="http://progit.org/book" target="_blank">Pro Git</a> for a better understanding on how to use Git.
- <p></p>
+ <h3><center>Create a new repository on the command-line</center></h3>
+
+ <pre wicket:id="createSyntax"></pre>
+
+ <h3><center>Push an existing repository from the command-line</center></h3>
+
+ <pre wicket:id="existingSyntax"></pre>
+
+ <div class="span8 offset1">
+ <h2><center>Learn Git</center></h2>
+ <p>If you are unsure how to use this information, consider reviewing the <a href="http://book.git-scm.com">Git Community Book</a> for a better understanding on how to use Git.</p>
+
<h4>Open Source Git Clients</h4>
- <ul>
- <li><a href="http://git-scm.com">Git</a> - the official, command-line Git</li>
- <li><a href="http://tortoisegit.googlecode.com">TortoiseGit</a> - Windows file explorer integration (requires official, command-line Git)</li>
- <li><a href="http://eclipse.org/egit">Eclipse/EGit</a> - Git for the Eclipse IDE (based on JGit, like Gitblit)</li>
- <li><a href="https://code.google.com/p/gitextensions/">Git Extensions</a> - C# frontend for Git that features Windows Explorer and Visual Studio integration</li>
- <li><a href="http://rowanj.github.io/gitx/">GitX-dev</a> - a Mac OS X Git client</li>
- </ul>
- <p></p>
+ <table>
+ <tbody>
+ <tr><td><a href="http://git-scm.com">Git</a></td><td>the official, command-line Git</td></tr>
+ <tr><td><a href="http://tortoisegit.googlecode.com">TortoiseGit</a></td><td>Windows file explorer integration (requires official, command-line Git)</td></tr>
+ <tr><td><a href="http://eclipse.org/egit">Eclipse/EGit</a></td><td>Git for the Eclipse IDE (based on JGit, like Gitblit)</td></tr>
+ <tr><td><a href="https://code.google.com/p/gitextensions/">Git Extensions</a></td><td>C# frontend for Git that features Windows Explorer and Visual Studio integration</td></tr>
+ <tr><td><a href="http://rowanj.github.io/gitx/">GitX-dev</a></td><td>a Mac OS X Git client</td></tr>
+ </tbody>
+ </table>
+
<h4>Commercial/Closed-Source Git Clients</h4>
- <ul>
- <li><a href="http://www.syntevo.com/smartgithg">SmartGit/Hg</a> - A Java Git and Mercurial client for Windows, Mac, and Linux</li>
- <li><a href="http://www.sourcetreeapp.com/">SourceTree</a> - A free Git and Mercurial client for Windows & Mac</li>
- <li><a href="http://www.git-tower.com/">Tower</a> - a Mac OS X Git client</li>
- </ul>
+ <table>
+ <tbody>
+ <tr><td><a href="http://www.syntevo.com/smartgithg">SmartGit/Hg</a></td><td>A Java Git and Mercurial client for Windows, Mac, and Linux</td></tr>
+ <tr><td><a href="http://www.sourcetreeapp.com/">SourceTree</a></td><td>A free Git and Mercurial client for Windows & Mac</td></tr>
+ <tr><td><a href="http://www.git-tower.com/">Tower</a></td><td>a Mac OS X Git client</td></tr>
+ </tbody>
+ </table>
+ </div>
+</div>
+</div>
+</div>
</div>
</wicket:extend>
</body>
diff --git a/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage.java b/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage.java index 00bac634..b3c52436 100644 --- a/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage.java +++ b/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage.java @@ -15,7 +15,6 @@ */
package com.gitblit.wicket.pages;
-import java.text.MessageFormat;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
@@ -32,7 +31,7 @@ import com.gitblit.wicket.GitblitRedirectException; import com.gitblit.wicket.WicketUtils;
import com.gitblit.wicket.panels.RepositoryUrlPanel;
-public class EmptyRepositoryPage extends RootPage {
+public class EmptyRepositoryPage extends RepositoryPage {
public EmptyRepositoryPage(PageParameters params) {
super(params);
@@ -50,8 +49,6 @@ public class EmptyRepositoryPage extends RootPage { throw new GitblitRedirectException(SummaryPage.class, params);
}
- setupPage(repositoryName, getString("gb.emptyRepository"));
-
UserModel user = GitBlitWebSession.get().getUser();
if (user == null) {
user = UserModel.ANONYMOUS;
@@ -62,15 +59,17 @@ public class EmptyRepositoryPage extends RootPage { RepositoryUrl primaryUrl = repositoryUrls.size() == 0 ? null : repositoryUrls.get(0);
String url = primaryUrl != null ? primaryUrl.url : "";
+ String createSyntax = readResource("create_git.md").replace("${primaryUrl}", url);
+ String existingSyntax = readResource("existing_git.md").replace("${primaryUrl}", url);
+
add(new Label("repository", repositoryName));
add(new RepositoryUrlPanel("pushurl", false, user, repository));
- add(new Label("cloneSyntax", MessageFormat.format("git clone {0}", url)));
- add(new Label("remoteSyntax", MessageFormat.format("git remote add origin {0}\ngit push -u origin --all\ngit push -u origin --tags", url)));
- add(new Label("upstreamSyntax", "git remote add upstream <upstream repository url>"));
+ add(new Label("createSyntax", createSyntax));
+ add(new Label("existingSyntax", existingSyntax));
}
@Override
- protected Class<? extends BasePage> getRootNavPageClass() {
- return RepositoriesPage.class;
+ protected String getPageName() {
+ return getString("gb.summary");
}
}
diff --git a/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_es.html b/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_es.html index f98cb050..af75b4ee 100644 --- a/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_es.html +++ b/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_es.html @@ -9,52 +9,54 @@ <body>
<wicket:extend>
<div class="container">
- <h2>Repositorio Vacío</h2>
- <p></p>
- <div class="row">
- <div class="span7">
- <div class="alert alert-success">
- <span wicket:id="repository" style="font-weight: bold;">[repository]</span> es un repositorio vacío y no puede ser visto en Gitblit.
- <p></p>
- Por favor, empuja algunas consignas a <span wicket:id="pushurl"></span>
- <p></p>
- <hr/>
- Después de empujar tus consignas puedes <b>refrescar</b> ésta página para ver tu repositorio.
- </div>
- </div>
- </div>
+<div class="markdown">
+<div class="row">
+<div class="span10 offset1">
+ <h3><center>Repositorio Vacío</center></h3>
+ <div class="alert alert-info">
+ <span wicket:id="repository" style="font-weight: bold;">[repository]</span> es un repositorio vacío y no puede ser visto en Gitblit.
+ <p></p>
+ Por favor, empuja algunas consignas a <span wicket:id="pushurl"></span>
+ <hr/>
+ Después de empujar tus consignas puedes <b>refrescar</b> ésta página para ver tu repositorio.
+ </div>
- <h3>Sintaxis de la Línea de Comandos de Git</h3>
- <span style="padding-bottom:5px;">Si no tienes un repositiorio local Git, puedes clonar éste, consignar algunos archivos, y después empujar las consignas de vuelta a Gitblit.</span>
- <p></p>
- <pre style="padding: 5px 30px;" wicket:id="cloneSyntax"></pre>
- <p></p>
- <span style="padding-bottom:5px;">Si ya tienes un repositorio local Git con algunas consignas, puedes añadir éste como remoto y empujar desde allí.</span>
- <p></p>
- <pre style="padding: 5px 30px;" wicket:id="remoteSyntax"></pre>
- <span style="padding-bottom:5px;">Si el repositorio está pensado para mantenerse sincronizado con otro repositorio corriente-arriba, entonces puedes añadirlo.</span>
- <p></p>
- <pre wicket:id="upstreamSyntax" style="padding: 5px 30px;"></pre>
- <p></p>
- <h3>Aprender sobre Git</h3>
- Si no estás seguro de como usar esta información, échale un vistazo al <a href="http://book.git-scm.com">Libro de la cominidad Git</a> o <a href="http://progit.org/book" target="_blank">Pro Git</a> para una mejor compresión de como usar Git.
- <p></p>
+ <h3><center>Create a new repository on the command-line</center></h3>
+
+ <pre wicket:id="createSyntax"></pre>
+
+ <h3><center>Push an existing repository from the command-line</center></h3>
+
+ <pre wicket:id="existingSyntax"></pre>
+
+ <div class="span8 offset1">
+ <h2><center>Aprender sobre Git</center></h2>
+ <p>Si no estás seguro de como usar esta información, échale un vistazo al <a href="http://book.git-scm.com/es">Libro de la cominidad Git</a> para una mejor compresión de como usar Git.</p>
+
<h4>Clientes Git de Código abierto.</h4>
- <ul>
- <li><a href="http://git-scm.com">Git</a> - El Git oficial en línea de comandos</li>
- <li><a href="http://tortoisegit.googlecode.com">TortoiseGit</a> - Explorador de archivos integrado en Windows (necesita Git oficial en línea de comandos)</li>
- <li><a href="http://eclipse.org/egit">Eclipse/EGit</a> - Git para el IDE de Eclipse (basado en JGit, como Gitblit)</li>
- <li><a href="https://code.google.com/p/gitextensions/">Git Extensions</a> - Interfaz de usuario gráfico Git en C# con integración en IE y en Visual Studio</li>
- <li><a href="http://rowanj.github.io/gitx/">GitX-dev</a> - Cliente Git para Mac OS X</li>
- </ul>
- <p></p>
+ <table>
+ <tbody>
+ <tr><td><a href="http://git-scm.com">Git</a></td><td>El Git oficial en línea de comandos</td></tr>
+ <tr><td><a href="http://tortoisegit.googlecode.com">TortoiseGit</a></td><td>Explorador de archivos integrado en Windows (necesita Git oficial en línea de comandos)</td></tr>
+ <tr><td><a href="http://eclipse.org/egit">Eclipse/EGit</a></td><td>Git para el IDE de Eclipse (basado en JGit, como Gitblit)</td></tr>
+ <tr><td><a href="https://code.google.com/p/gitextensions/">Git Extensions</a></td><td>Interfaz de usuario gráfico Git en C# con integración en IE y en Visual Studio</td></tr>
+ <tr><td><a href="http://rowanj.github.io/gitx/">GitX-dev</a></td><td>Cliente Git para Mac OS X</td></tr>
+ </tbody>
+ </table>
+
<h4>Clientes Git comerciales</h4>
- <ul>
- <li><a href="http://www.syntevo.com/smartgithg">SmartGit/Hg</a> - aplicación Java (necesita Git oficial en línea de comandos)</li>
- <li><a href="http://www.sourcetreeapp.com/">SourceTree</a> - Un cliente Git gratuito para Mac, Mercurial, y SVN</li>
- <li><a href="http://www.git-tower.com/">Tower</a> - Cliente Git para Mac OS X</li>
- </ul>
-</div>
+ <table>
+ <tbody>
+ <tr><td><a href="http://www.syntevo.com/smartgithg">SmartGit/Hg</a></td><td>aplicación Java (necesita Git oficial en línea de comandos)</td></tr>
+ <tr><td><a href="http://www.sourcetreeapp.com/">SourceTree</a></td><td>Un cliente Git gratuito para Mac, Mercurial, y SVN</td></tr>
+ <tr><td><a href="http://www.git-tower.com/">Tower</a></td><td>Cliente Git para Mac OS X</td></tr>
+ </tbody>
+ </table>
+ </div>
+</div>
+</div>
+</div>
+</div>
</wicket:extend>
</body>
</html>
diff --git a/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_ko.html b/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_ko.html index 4027f0e3..7fbcee13 100644 --- a/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_ko.html +++ b/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_ko.html @@ -7,55 +7,54 @@ <body> <wicket:extend> <div class="container"> - <h2>비어있는 ì €ìž¥ì†Œ</h2> - <p></p> - <div class="row"> - <div class="span10"> - <div class="alert alert-success"> - <span wicket:id="repository" style="font-weight: bold;">[repository]</span> ì €ìž¥ì†ŒëŠ” 비어 있어서 Gitblit ì—ì„œ ë³¼ 수 없습니다. - <p></p> - ì´ Git url ì— ì»¤ë°‹í•´ 주세요. <span wicket:id="pushurl"></span> - <p></p> - <hr/> - After you have pushed commits you may <b>refresh</b> this page to view your repository. - </div> - </div> - </div> +<div class="markdown"> +<div class="row"> +<div class="span10 offset1"> + <h3><center>비어있는 ì €ìž¥ì†Œ</center></h3> + <div class="alert alert-info"> + <span wicket:id="repository" style="font-weight: bold;">[repository]</span> ì €ìž¥ì†ŒëŠ” 비어 있어서 Gitblit ì—ì„œ ë³¼ 수 없습니다. + <p></p> + ì´ Git url ì— ì»¤ë°‹í•´ 주세요. <span wicket:id="pushurl"></span> + <hr/> + After you have pushed commits you may <b>refresh</b> this page to view your repository. + </div> + + <h3><center>Create a new repository on the command-line</center></h3> + + <pre wicket:id="createSyntax"></pre> + + <h3><center>Push an existing repository from the command-line</center></h3> + + <pre wicket:id="existingSyntax"></pre> + + <div class="span8 offset1"> + <h2><center>Git 배우기</center></h2> + <p>만약 ì‚¬ìš©ë²•ì— ìžì‹ ì´ ì—†ë‹¤ë©´, Git ì‚¬ìš©ë²•ì„ ë” ìž˜ ì´í•´í•˜ê¸° 위해 + <a href="http://book.git-scm.com/ko">Git Community Book</a> ë˜ëŠ” ì„ ë³¼ ê²ƒì„ ê³ ë ¤í•´ 보세요.</p> - <p></p> - <h3>Git ëª…ë ¹ì–´</h3> - <span style="padding-bottom:5px;">로컬 Git ì €ìž¥ì†Œê°€ 없다면, ì´ ì €ìž¥ì†Œë¥¼ í´ë¡ (clone) í•œ 후, 몇 파ì¼ì„ ì»¤ë°‹í•˜ê³ , ê·¸ ì»¤ë°‹ì„ Gitblit ì— í‘¸ì‹œ(push) 하세요.</span> - <p></p> - <pre style="padding: 5px 30px;" wicket:id="cloneSyntax"></pre> - <p></p> - <span style="padding-bottom:5px;">만약 ì»¤ë°‹ëœ ë¡œì»¬ Git ì €ìž¥ì†Œê°€ 있다면, 다ìŒê³¼ ê°™ì´ ì €ìž¥ì†Œì— ë¦¬ëª¨íŠ¸ë¥¼ ì¶”ê°€í•˜ê³ í‘¸ì‹œ(push)í• ìˆ˜ 있습니다.</span> - <p></p> - <pre style="padding: 5px 30px;" wicket:id="remoteSyntax"></pre> - <span style="padding-bottom:5px;">If your repository is meant to be kept in sync with an upstream repository, then you may add it.</span> - <p></p> - <pre wicket:id="upstreamSyntax" style="padding: 5px 30px;"></pre> - <p></p> - <h3>Git 배우기</h3> - 만약 ì‚¬ìš©ë²•ì— ìžì‹ ì´ ì—†ë‹¤ë©´, Git ì‚¬ìš©ë²•ì„ ë” ìž˜ ì´í•´í•˜ê¸° 위해 - <a href="http://book.git-scm.com">Git Community Book</a> ë˜ëŠ” - <a href="http://progit.org/book" target="_blank">Pro Git</a>, - <a href="http://dogfeet.github.com/articles/2012/progit.html" target="_blank">Pro Git 한글</a> ì„ ë³¼ ê²ƒì„ ê³ ë ¤í•´ 보세요. - <p></p> <h4>오픈소스 Git í´ë¼ì´ì–¸íŠ¸</h4> - <ul> - <li><a href="http://git-scm.com">Git</a> - ëª…ë ¹ì–´ 기반 ê³µì‹ Git</li> - <li><a href="http://tortoisegit.googlecode.com">TortoiseGit</a> - 윈ë„ì˜ íŒŒì¼ íƒìƒ‰ê¸°ì— í†µí•©ëœ UI í´ë¼ì´ì–¸íŠ¸ (ëª…ë ¹ì–´ 기반 ê³µì‹ Git í•„ìš”)</li> - <li><a href="http://eclipse.org/egit">Eclipse/EGit</a> - ì´í´ë¦½ìŠ¤ IDE í”ŒëŸ¬ê·¸ì¸ (Gitblit ê³¼ ê°™ì€ JGit 기반)</li> - <li><a href="https://code.google.com/p/gitextensions/">Git Extensions</a> - C# frontend for Git that features Windows Explorer and Visual Studio integration</li> - <li><a href="http://rowanj.github.io/gitx/">GitX-dev</a> - a Mac OS X Git client</li> - </ul> - <p></p> + <table> + <tbody> + <tr><td><a href="http://git-scm.com">Git</a></td><td>ëª…ë ¹ì–´ 기반 ê³µì‹ Git</td></tr> + <tr><td><a href="http://tortoisegit.googlecode.com">TortoiseGit</a></td><td>윈ë„ì˜ íŒŒì¼ íƒìƒ‰ê¸°ì— í†µí•©ëœ UI í´ë¼ì´ì–¸íŠ¸ (ëª…ë ¹ì–´ 기반 ê³µì‹ Git í•„ìš”)</td></tr> + <tr><td><a href="http://eclipse.org/egit">Eclipse/EGit</a></td><td>ì´í´ë¦½ìŠ¤ IDE í”ŒëŸ¬ê·¸ì¸ (Gitblit ê³¼ ê°™ì€ JGit 기반)</td></tr> + <tr><td><a href="https://code.google.com/p/gitextensions/">Git Extensions</a></td><td>C# frontend for Git that features Windows Explorer and Visual Studio integration</td></tr> + <tr><td><a href="http://rowanj.github.io/gitx/">GitX-dev</a></td><td>a Mac OS X Git client</td></tr> + </tbody> + </table> + <h4>ìœ ë£Œ Git í´ë¼ì´ì–¸íŠ¸</h4> - <ul> - <li><a href="http://www.syntevo.com/smartgithg">SmartGit/Hg</a> - ìžë°” 어플리케ì´ì…˜ (ëª…ë ¹ì–´ 기반 ê³µì‹ Git í•„ìš”)</li> - <li><a href="http://www.sourcetreeapp.com/">SourceTree</a> - A free Mac Client for Git, Mercurial, and SVN</li> - <li><a href="http://www.git-tower.com/">Tower</a> - a Mac OS X Git client</li> - </ul> + <table> + <tbody> + <tr><td><a href="http://www.syntevo.com/smartgithg">SmartGit/Hg</a></td><td>ìžë°” 어플리케ì´ì…˜ (ëª…ë ¹ì–´ 기반 ê³µì‹ Git í•„ìš”)</td></tr> + <tr><td><a href="http://www.sourcetreeapp.com/">SourceTree</a></td><td>A free Git and Mercurial client for Windows & Mac</td></tr> + <tr><td><a href="http://www.git-tower.com/">Tower</a></td><td>a Mac OS X Git client</td></tr> + </tbody> + </table> + </div> +</div> +</div> +</div> </div> </wicket:extend> </body> diff --git a/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_nl.html b/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_nl.html index 1fc3fe8d..84373d20 100644 --- a/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_nl.html +++ b/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_nl.html @@ -7,52 +7,53 @@ <body>
<wicket:extend>
<div class="container">
- <h2>Lege Repository</h2>
- <p></p>
- <div class="row">
- <div class="span10">
- <div class="alert alert-success">
- <span wicket:id="repository" style="font-weight: bold;">[repository]</span> is een lege repository en kan niet bekeken worden door Gitblit.
- <p></p>
- Push alstublieft een paar commits naar <span wicket:id="pushurl"></span>
- <p></p>
- <hr/>
- Nadat u een commits gepushed heeft, kunt u deze pagina <b>verversen</b> om uw repository te bekijken.
- </div>
- </div>
- </div>
+<div class="markdown">
+<div class="row">
+<div class="span10 offset1">
+ <h3><center>Lege Repository</center></h3>
+ <div class="alert alert-info">
+ <span wicket:id="repository" style="font-weight: bold;">[repository]</span> is een lege repository en kan niet bekeken worden door Gitblit.
+ <p></p>
+ Push alstublieft een paar commits naar <span wicket:id="pushurl"></span>
+ <hr/>
+ Nadat u een commits gepushed heeft, kunt u deze pagina <b>verversen</b> om uw repository te bekijken.
+ </div>
- <h3>Git Command-Line Syntax</h3>
- <span style="padding-bottom:5px;">Als u geen lokale Git repository heeft, kunt u deze repository clonen, er bestanden naar committen en dan uw commits terug pushen naar Gitblit.</span>
- <p></p>
- <pre style="padding: 5px 30px;" wicket:id="cloneSyntax"></pre>
- <p></p>
- <span style="padding-bottom:5px;">Als u al een lokale Git repository met commits heeft, kunt u deze repository als een remote toevoegen en er naar pushen.</span>
- <p></p>
- <pre wicket:id="remoteSyntax" style="padding: 5px 30px;"></pre>
- <p></p>
- <span style="padding-bottom:5px;">Indien uw repository bedoeld is om synchroon gehouden te worden met een upstream repository, dan kunt u deze toevoegen.</span>
- <p></p>
- <pre wicket:id="upstreamSyntax" style="padding: 5px 30px;"></pre>
- <p></p>
- <h3>Leer Git</h3>
- Als u niet goed weet wat u met deze informatie aan moet, raden we u aan om het <a href="http://book.git-scm.com">Git Community Book</a> of <a href="http://progit.org/book" target="_blank">Pro Git</a> te bestuderen voor een beter begrip over hoe u Git kunt gebruiken.
- <p></p>
+ <h3><center>Create a new repository on the command-line</center></h3>
+
+ <pre wicket:id="createSyntax"></pre>
+
+ <h3><center>Push an existing repository from the command-line</center></h3>
+
+ <pre wicket:id="existingSyntax"></pre>
+
+ <div class="span8 offset1">
+ <h2><center>Leer Git</center></h2>
+ <p>Als u niet goed weet wat u met deze informatie aan moet, raden we u aan om het <a href="http://book.git-scm.com/nl">Git Community Book</a> te bestuderen voor een beter begrip over hoe u Git kunt gebruiken.</p>
+
<h4>Open Source Git Clients</h4>
- <ul>
- <li><a href="http://git-scm.com">Git</a> - de officiele, command-line Git</li>
- <li><a href="http://tortoisegit.googlecode.com">TortoiseGit</a> - Windows bestandsverkenner ingetratie (officiele command-line Git is wel nodig)</li>
- <li><a href="http://eclipse.org/egit">Eclipse/EGit</a> - Git voor de Eclipse IDE (gebaseerd op JGit, zoals Gitblit)</li>
- <li><a href="https://code.google.com/p/gitextensions/">Git Extensions</a> - C# frontend voor Git met Windows Explorer en Visual Studio integratie</li>
- <li><a href="http://rowanj.github.io/gitx/">GitX-dev</a> - een Mac OS X Git client</li>
- </ul>
- <p></p>
+ <table>
+ <tbody>
+ <tr><td><a href="http://git-scm.com">Git</a></td><td>de officiele, command-line Git</td></tr>
+ <tr><td><a href="http://tortoisegit.googlecode.com">TortoiseGit</a></td><td>Windows bestandsverkenner ingetratie (officiele command-line Git is wel nodig)</td></tr>
+ <tr><td><a href="http://eclipse.org/egit">Eclipse/EGit</a></td><td>Git voor de Eclipse IDE (gebaseerd op JGit, zoals Gitblit)</td></tr>
+ <tr><td><a href="https://code.google.com/p/gitextensions/">Git Extensions</a></td><td>C# frontend voor Git met Windows Explorer en Visual Studio integratie</td></tr>
+ <tr><td><a href="http://rowanj.github.io/gitx/">GitX-dev</a></td><td>een Mac OS X Git client</td></tr>
+ </tbody>
+ </table>
+
<h4>Commercial/Closed-Source Git Clients</h4>
- <ul>
- <li><a href="http://www.syntevo.com/smartgithg">SmartGit/Hg</a> - Een Java Git, Mercurial, en SVN client applicatie (officiele command-line Git is wel nodig)</li>
- <li><a href="http://www.sourcetreeapp.com/">SourceTree</a> - Een gratis Mac Client voor Git, Mercurial, en SVN</li>
- <li><a href="http://www.git-tower.com/">Tower</a> - een Mac OS X Git client</li>
- </ul>
+ <table>
+ <tbody>
+ <tr><td><a href="http://www.syntevo.com/smartgithg">SmartGit/Hg</a></td><td>Een Java Git, Mercurial, en SVN client applicatie (officiele command-line Git is wel nodig)</td></tr>
+ <tr><td><a href="http://www.sourcetreeapp.com/">SourceTree</a></td><td>Een gratis Mac Client voor Git en Mercurial</td></tr>
+ <tr><td><a href="http://www.git-tower.com/">Tower</a></td><td>een Mac OS X Git client</td></tr>
+ </tbody>
+ </table>
+ </div>
+</div>
+</div>
+</div>
</div>
</wicket:extend>
</body>
diff --git a/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_no.html b/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_no.html new file mode 100644 index 00000000..273e15f9 --- /dev/null +++ b/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_no.html @@ -0,0 +1,60 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" + xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd" + xml:lang="en" + lang="en"> + +<body> +<wicket:extend> +<div class="container"> +<div class="markdown"> +<div class="row"> + <div class="span10 offset1"> + <h3><center>Tomt Repository</center></h3> + <div class="alert alert-info"> + <span wicket:id="repository" style="font-weight: bold;">[repository]</span> er et tomt repository som ikke kan vises av Gitblit. + <p></p> + Vennligst push noen endringer inn til <span wicket:id="pushurl"></span> + <hr/> + Etter at du har pushet noen endringer, kan du <b>oppfriske</b> denne siden for \u00e5 se repositoriet. + </div> + + <h3><center>Opprett et nytt repository fra kommandolinjen</center></h3> + + <pre wicket:id="createSyntax"></pre> + + <h3><center>Push et eksisternde repository fra kommandolinjen</center></h3> + + <pre wicket:id="existingSyntax"></pre> + + <div class="span8 offset1"> + <h2><center>L\u00e6r Git</center></h2> + <p>Hvis du er usikker p\u00e5 hvordan du kan benytte denne informasjonen, vurder \u00e5 lese <a href="http://book.git-scm.com">Git Community Book</a> eller <a href="http://progit.org/book" target="_blank">Pro Git</a> for \u00e5 f\u00e5 en bedre forst\u00e5else av hvordan du kan bruke Git. </p> + + <h4>Open Source Git Clients</h4> + <table> + <tbody> + <tr><td>a href="http://git-scm.com">Git</a> - den offisielle, kommando-linje git</td></tr> + <tr><td>a href="http://tortoisegit.googlecode.com">TortoiseGit</a> - Windows filutforsker integrasjon (krever den offisielle kommando-linje git versjonen installert</td></tr> + <tr><td><a href="http://eclipse.org/egit">Eclipse/EGit</a> - Git for Eclipse IDE (basert p\u00e5 JGit, akkurat som Gitblit er)</tr> + <tr><td><a href="https://code.google.com/p/gitextensions/">Git Extensions</a> - En C# frontend for Git som integrerer med filutforskeren og Visual Studio.</td></tr> + <tr><td><a href="http://rowanj.github.io/gitx/">GitX-dev</a> - En git klient for OS X</td></tr> + </tbody> + </table> + + <h4>Commercial/Closed-Source Git Clients</h4> + <table> + <tbody> + <tr><td><a href="http://www.syntevo.com/smartgithg">SmartGit/Hg</a> - En Git og Mercurial klient for Windows, Mac, og Linux</td></tr> + <tr><td><a href="http://www.sourcetreeapp.com/">SourceTree</a> - En gratis Git og Mercurial klient for Windows og Mac</td></tr> + <tr><td>a href="http://www.git-tower.com/">Tower</a> - En git klient for Mac OS X </td></tr> + </tbody> + </table> + </div> +</div> +</div> +</div> +</div> +</wicket:extend> +</body> +</html> diff --git a/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_pl.html b/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_pl.html index 3f9db908..b50bdac3 100644 --- a/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_pl.html +++ b/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_pl.html @@ -9,51 +9,53 @@ <body> <wicket:extend> <div class="container"> - <h2>Puste repozytorium</h2> - <p></p> - <div class="row"> - <div class="span10"> - <div class="alert alert-success"> - <span wicket:id="repository" style="font-weight: bold;">[repository]</span> jest pustym repozytorium i nie może być zaprezentowane przez Gitblit. - <p></p> - Wgraj, poprzez push, dowolne zmiany do lokalizacji <span wicket:id="pushurl"></span> - <p></p> - <hr/> - Po wgraniu zmian <b>odśwież</b> stronę, aby podejrzeć repozytorium. - </div> - </div> - </div> +<div class="markdown"> +<div class="row"> +<div class="span10 offset1"> + <h3><center>Puste repozytorium</center></h3> + <div class="alert alert-info"> + <span wicket:id="repository" style="font-weight: bold;">[repository]</span> jest pustym repozytorium i nie może być zaprezentowane przez Gitblit. + <p></p> + Wgraj, poprzez push, dowolne zmiany do lokalizacji <span wicket:id="pushurl"></span> + <hr/> + Po wgraniu zmian <b>odśwież</b> stronę, aby podejrzeć repozytorium. + </div> - <h3>Składnia linii poleceń GITa</h3> - <span style="padding-bottom:5px;">Jeśli nie posiadasz lokalnego repozytorium GITa to sklonuj to repozytorium, wgraj dowolne pliki, a następnie wyślij poprzez push zmiany na Gitblit.</span> - <p></p> - <pre style="padding: 5px 30px;" wicket:id="cloneSyntax"></pre> - <p></p> - <span style="padding-bottom:5px;">Gdy posiadasz lokalne repozytorium GITa z dowolnymi zmianami, to możesz dodać to repozytorium jako remote i wysłać do niego zmiany poprzez push.</span> - <p></p> - <pre style="padding: 5px 30px;" wicket:id="remoteSyntax"></pre> - <span style="padding-bottom:5px;">If your repository is meant to be kept in sync with an upstream repository, then you may add it.</span> - <p></p> - <pre wicket:id="upstreamSyntax" style="padding: 5px 30px;"></pre> - <p></p> - <h3>Nauka GITa</h3> - Jeżeli powyższe informacje są dla Ciebie niezrozumiałe, zapoznaj się z książką <a href="http://git-scm.com/book/pl" target="_blank">Pro Git - Wersja PL</a> dla lepszego zrozumienia, jak poprawnie używać GITa. - <p></p> + <h3><center>Create a new repository on the command-line</center></h3> + + <pre wicket:id="createSyntax"></pre> + + <h3><center>Push an existing repository from the command-line</center></h3> + + <pre wicket:id="existingSyntax"></pre> + + <div class="span8 offset1"> + <h2><center>Nauka GITa</center></h2> + <p>Jeżeli powyższe informacje są dla Ciebie niezrozumiałe, zapoznaj się z książką <a href="http://git-scm.com/book/pl" target="_blank">Pro Git - Wersja PL</a> dla lepszego zrozumienia, jak poprawnie używać GITa.</p> + <h4>Darmowi klienci GITa</h4> - <ul> - <li><a href="http://git-scm.com">Git</a> - Oficjalny klient, dostępny przez linię poleceń</li> - <li><a href="http://tortoisegit.googlecode.com">TortoiseGit</a> - Rozszerzenie eksploratora Windows (wymaga oficjalnego, dostępnego przez linię poleceń klienta)</li> - <li><a href="http://eclipse.org/egit">Eclipse/EGit</a> - GIT dla edytora Eclipse (oparty o JGit, podobnie jak Gitblit)</li> - <li><a href="https://code.google.com/p/gitextensions/">Git Extensions</a> - napisana w C# fasada na GIT, udostępniająca integrację dla Windows Explorer oraz Visual Studio</li> - <li><a href="http://rowanj.github.io/gitx/">GitX-dev</a> - klient GIT na Mac OS X</li> - </ul> - <p></p> + <table> + <tbody> + <tr><td><a href="http://git-scm.com">Git</a></td><td>Oficjalny klient, dostępny przez linię poleceń</td></tr> + <tr><td><a href="http://tortoisegit.googlecode.com">TortoiseGit</a></td><td>Rozszerzenie eksploratora Windows (wymaga oficjalnego, dostępnego przez linię poleceń klienta)</td></tr> + <tr><td><a href="http://eclipse.org/egit">Eclipse/EGit</a></td><td>GIT dla edytora Eclipse (oparty o JGit, podobnie jak Gitblit)</td></tr> + <tr><td><a href="https://code.google.com/p/gitextensions/">Git Extensions</a></td><td>napisana w C# fasada na GIT, udostępniająca integrację dla Windows Explorer oraz Visual Studio</td></tr> + <tr><td><a href="http://rowanj.github.io/gitx/">GitX-dev</a></td><td>klient GIT na Mac OS X</td></tr> + </tbody> + </table> + <h4>Komercyjni klienci GITa</h4> - <ul> - <li><a href="http://www.syntevo.com/smartgithg">SmartGit/Hg</a> - aplikacja napisana w Javie (wymaga oficjalnego, dostępnego przez linię poleceń klienta)</li> - <li><a href="http://www.sourcetreeapp.com/">SourceTree</a> - darmowy klient GIT, Mercurial i SVN na Mac OS X</li> - <li><a href="http://www.git-tower.com/">Tower</a> - klient GIT na Mac OS X</li> - </ul> + <table> + <tbody> + <tr><td><a href="http://www.syntevo.com/smartgithg">SmartGit/Hg</a></td><td>aplikacja napisana w Javie (wymaga oficjalnego, dostępnego przez linię poleceń klienta)</td></tr> + <tr><td><a href="http://www.sourcetreeapp.com/">SourceTree</a></td><td>darmowy klient GIT, Mercurial i SVN na Mac OS X</td></tr> + <tr><td><a href="http://www.git-tower.com/">Tower</a></td><td>klient GIT na Mac OS X</td></tr> + </tbody> + </table> + </div> +</div> +</div> +</div> </div> </wicket:extend> </body> diff --git a/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_pt_BR.html b/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_pt_BR.html index 351fe872..fc201210 100644 --- a/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_pt_BR.html +++ b/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_pt_BR.html @@ -7,51 +7,53 @@ <body>
<wicket:extend>
<div class="container">
- <h2>Repositório Vazio</h2>
- <p></p>
- <div class="row">
- <div class="span10">
- <div class="alert alert-success">
- <span wicket:id="repository" style="font-weight: bold;">[repository]</span> é um repositório vazio e não pode ser visualizado pelo Gitblit.
- <p></p>
- Por favor faça o push de alguns commits para <span wicket:id="pushurl"></span>
- <p></p>
- <hr/>
- Depois de ter feito push você poderá <b>atualizar</b> esta página para visualizar seu repositório.
- </div>
- </div>
- </div>
+<div class="markdown">
+<div class="row">
+<div class="span10 offset1">
+ <h3><center>Repositório Vazio</center></h3>
+ <div class="alert alert-info">
+ <span wicket:id="repository" style="font-weight: bold;">[repository]</span> é um repositório vazio e não pode ser visualizado pelo Gitblit.
+ <p></p>
+ Por favor faça o push de alguns commits para <span wicket:id="pushurl"></span>
+ <hr/>
+ Depois de ter feito push você poderá <b>atualizar</b> esta página para visualizar seu repositório.
+ </div>
- <h3>Sintaxe dos comandos do Git</h3>
- <span style="padding-bottom:5px;">Se você ainda não tem um repositório local do Git, então você deve primeiro clonar este repositório, fazer commit de alguns arquivos e então fazer push desses commits para o Gitblit.</span>
- <p></p>
- <pre style="padding: 5px 30px;" wicket:id="cloneSyntax"></pre>
- <p></p>
- <span style="padding-bottom:5px;">Se você já tem um repositório Git local com alguns commits, então você deve adicionar este repositório como uma referência remota e então fazer push.</span>
- <p></p>
- <pre wicket:id="remoteSyntax" style="padding: 5px 30px;"></pre>
- <span style="padding-bottom:5px;">If your repository is meant to be kept in sync with an upstream repository, then you may add it.</span>
- <p></p>
- <pre wicket:id="upstreamSyntax" style="padding: 5px 30px;"></pre>
- <p></p>
- <h3>Aprenda Git</h3>
- Se você estiver com dúvidas sobre como ultilizar essas informações, uma sugestão seria dar uma olhada no livro <a href="http://book.git-scm.com">Git Community Book</a> ou <a href="http://progit.org/book" target="_blank">Pro Git</a> para entender melhor como usar o Git.
- <p></p>
+ <h3><center>Create a new repository on the command-line</center></h3>
+
+ <pre wicket:id="createSyntax"></pre>
+
+ <h3><center>Push an existing repository from the command-line</center></h3>
+
+ <pre wicket:id="existingSyntax"></pre>
+
+ <div class="span8 offset1">
+ <h2><center>Aprenda Git</center></h2>
+ <p>Se você estiver com dúvidas sobre como ultilizar essas informações, uma sugestão seria dar uma olhada no livro <a href="http://book.git-scm.com/pt-br">Git Community Book</a> para entender melhor como usar o Git.</p>
+
<h4>Alguns clients do Git que são Open Source</h4>
- <ul>
- <li><a href="http://git-scm.com">Git</a> - o Git oficial através de linhas de comando</li>
- <li><a href="http://tortoisegit.googlecode.com">TortoiseGit</a> - Faz integração do Explorer do Windows com o Git (por isso requer o Git Oficial)</li>
- <li><a href="http://eclipse.org/egit">Eclipse/EGit</a> - Git para a IDE Eclipse (baseada no JGit, como o Gitblit)</li>
- <li><a href="https://code.google.com/p/gitextensions/">Git Extensions</a> - Interface (em C#) para o Git cuja a caracterÃstica é a integração com o Windows Explorer e o Visual Studio</li>
- <li><a href="http://rowanj.github.io/gitx/">GitX-dev</a> - um Cliente do Git para Mac OS X</li>
- </ul>
- <p></p>
+ <table>
+ <tbody>
+ <tr><td><a href="http://git-scm.com">Git</a></td><td>o Git oficial através de linhas de comando</td></tr>
+ <tr><td><a href="http://tortoisegit.googlecode.com">TortoiseGit</a></td><td>Faz integração do Explorer do Windows com o Git (por isso requer o Git Oficial)</td></tr>
+ <tr><td><a href="http://eclipse.org/egit">Eclipse/EGit</a></td><td>Git para a IDE Eclipse (baseada no JGit, como o Gitblit)</td></tr>
+ <tr><td><a href="https://code.google.com/p/gitextensions/">Git Extensions</a></td><td>Interface (em C#) para o Git cuja a caracterÃstica é a integração com o Windows Explorer e o Visual Studio</td></tr>
+ <tr><td><a href="http://rowanj.github.io/gitx/">GitX-dev</a></td><td>um Cliente do Git para Mac OS X</td></tr>
+ </tbody>
+ </table>
+
<h4>Clients do Git proprietários ou com Código Proprietário</h4>
- <ul>
- <li><a href="http://www.syntevo.com/smartgithg">SmartGit/Hg</a> - Aplicação Client (em Java) para Git, Mercurial, e SVN (por isso requer o Git Oficial)</li>
- <li><a href="http://www.sourcetreeapp.com/">SourceTree</a> - Client gratuito para o Mac que suporta Git, Mercurial e SVN</li>
- <li><a href="http://www.git-tower.com/">Tower</a> - um Cliente do Git para Mac OS X</li>
- </ul>
+ <table>
+ <tbody>
+ <tr><td><a href="http://www.syntevo.com/smartgithg">SmartGit/Hg</a></td><td>Aplicação Client (em Java) para Git e Mercurial (por isso requer o Git Oficial)</td></tr>
+ <tr><td><a href="http://www.sourcetreeapp.com/">SourceTree</a></td><td>Client gratuito para o Mac que suporta Git e Mercurial</td></tr>
+ <tr><td><a href="http://www.git-tower.com/">Tower</a></td><td>um Cliente do Git para Mac OS X</td></tr>
+ </tbody>
+ </table>
+ </div>
+</div>
+</div>
+</div>
</div>
</wicket:extend>
</body>
diff --git a/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_zh_CN.html b/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_zh_CN.html index 955b4312..72ce051e 100644 --- a/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_zh_CN.html +++ b/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_zh_CN.html @@ -7,53 +7,54 @@ <body> <wicket:extend> <div class="container"> - <h2>空版本库</h2> - <p></p> - <div class="row"> - <div class="span10"> - <div class="alert alert-success"> - <span wicket:id="repository" style="font-weight: bold;">[repository]</span> 版本库目å‰ä¸ºç©ºã€‚ - Gitblit æ— æ³•æŸ¥çœ‹ã€‚ - <p></p> - 请往æ¤ç½‘å€è¿›è¡ŒæŽ¨é€ <span wicket:id="pushurl"></span> - <p></p> - <hr/> - å½“ä½ æŽ¨é€å®Œæ¯•åŽä½ å¯ä»¥ <b>刷新</b> æ¤é¡µé¢é‡æ–°æŸ¥çœ‹æ‚¨çš„版本库。 - </div> - </div> - </div> +<div class="markdown"> +<div class="row"> +<div class="span10 offset1"> + <h3><center>空版本库</center></h3> + <div class="alert alert-info"> + <span wicket:id="repository" style="font-weight: bold;">[repository]</span> 版本库目å‰ä¸ºç©ºã€‚ + Gitblit æ— æ³•æŸ¥çœ‹ã€‚ + <p></p> + 请往æ¤ç½‘å€è¿›è¡ŒæŽ¨é€ <span wicket:id="pushurl"></span> + <hr/> + å½“ä½ æŽ¨é€å®Œæ¯•åŽä½ å¯ä»¥ <b>刷新</b> æ¤é¡µé¢é‡æ–°æŸ¥çœ‹æ‚¨çš„版本库。 + </div> - <h3>Git å‘½ä»¤è¡Œæ ¼å¼</h3> - <span style="padding-bottom:5px;">如果您没有本地 Git 版本库, 您å¯ä»¥å…‹éš†æ¤ç‰ˆæœ¬åº“, æ交一些文件, 然åŽå°†æ‚¨çš„æ交推é€å›žGitblit。</span> - <p></p> - <pre style="padding: 5px 30px;" wicket:id="cloneSyntax"></pre> - <p></p> - <span style="padding-bottom:5px;">如果您已ç»æœ‰ä¸€ä¸ªæœ¬åœ°çš„æ交过的版本库, 那么您å¯ä»¥å°†æ¤ç‰ˆæœ¬åº“åŠ ä¸ºè¿œç¨‹ - 版本库,并进行推é€ã€‚</span> - <p></p> - <pre wicket:id="remoteSyntax" style="padding: 5px 30px;"></pre> - <span style="padding-bottom:5px;">If your repository is meant to be kept in sync with an upstream repository, then you may add it.</span> - <p></p> - <pre wicket:id="upstreamSyntax" style="padding: 5px 30px;"></pre> - <p></p> - <h3>å¦ä¹ Git</h3> - 如果您ä¸æ˜Žç™½è¿™äº›ä¿¡æ¯ä»€ä¹ˆæ„æ€, 您å¯ä»¥å‚考 <a href="http://book.git-scm.com">Git Community Book</a> 或者 <a href="http://progit.org/book" target="_blank">Pro Git</a> åŽ»æ›´åŠ æ·±å…¥çš„å¦ä¹ Git 的用法。 - <p></p> + <h3><center>通过命令行创建一个新的版本库</center></h3> + + <pre wicket:id="createSyntax"></pre> + + <h3><center>通过命令行推é€ä¸€ä¸ªå·²å˜åœ¨çš„版本库</center></h3> + + <pre wicket:id="existingSyntax"></pre> + + <div class="span8 offset1"> + <h2><center>å¦ä¹ Git</center></h2> + <p>如果您ä¸æ˜Žç™½è¿™äº›ä¿¡æ¯ä»€ä¹ˆæ„æ€, 您å¯ä»¥å‚考 <a href="http://book.git-scm.com/zh">Git Community Book</a> åŽ»æ›´åŠ æ·±å…¥çš„å¦ä¹ Git 的用法。</p> + <h4>å¼€æº Git 客户端</h4> - <ul> - <li><a href="http://git-scm.com">Git</a> - 官方, 命令行版本 Git</li> - <li><a href="http://tortoisegit.googlecode.com">TortoiseGit</a> - 与 Windows 资æºç®¡ç†å™¨é›†æˆ (需è¦å®˜æ–¹, 命令行 Git 的支æŒ)</li> - <li><a href="http://eclipse.org/egit">Eclipse/EGit</a> - Git for the Eclipse IDE (基于 JGit, 类似 Gitblit)</li> - <li><a href="https://code.google.com/p/gitextensions/">Git Extensions</a> - C# 版本的 Git å‰ç«¯ï¼Œä¸Ž Windows 资æºç®¡ç†å™¨å’Œ Visual Studio 集æˆ</li> - <li><a href="http://rowanj.github.io/gitx/">GitX-dev</a> - Mac OS X Git 客户端</li> - </ul> - <p></p> + <table> + <tbody> + <tr><td><a href="http://git-scm.com">Git</a></td><td>官方, 命令行版本 Git</td></tr> + <tr><td><a href="http://tortoisegit.googlecode.com">TortoiseGit</a></td><td>与 Windows 资æºç®¡ç†å™¨é›†æˆ (需è¦å®˜æ–¹, 命令行 Git 的支æŒ)</td></tr> + <tr><td><a href="http://eclipse.org/egit">Eclipse/EGit</a></td><td>Git for the Eclipse IDE (基于 JGit, 类似 Gitblit)</td></tr> + <tr><td><a href="https://code.google.com/p/gitextensions/">Git Extensions</a></td><td>C# 版本的 Git å‰ç«¯ï¼Œä¸Ž Windows 资æºç®¡ç†å™¨å’Œ Visual Studio 集æˆ</td></tr> + <tr><td><a href="http://rowanj.github.io/gitx/">GitX-dev</a></td><td>Mac OS X Git 客户端</td></tr> + </tbody> + </table> + <h4>商业/é—æº Git 客户端</h4> - <ul> - <li><a href="http://www.syntevo.com/smartgithg">SmartGit/Hg</a> - Java ç‰ˆæœ¬çš„æ”¯æŒ Git, Mercurial å’Œ SVN 客户端应用 (需è¦å®˜æ–¹, 命令行 Git 的支æŒ)</li> - <li><a href="http://www.sourcetreeapp.com/">SourceTree</a> - å…费的 Mac Git Mercurial ä»¥åŠ SVN 客户端, Mercurial, and SVN</li> - <li><a href="http://www.git-tower.com/">Tower</a> - Mac OS X Git 客户端</li> - </ul> + <table> + <tbody> + <tr><td><a href="http://www.syntevo.com/smartgithg">SmartGit/Hg</a></td><td>Java ç‰ˆæœ¬çš„æ”¯æŒ Git, Mercurial å’Œ SVN 客户端应用 </td></tr> + <tr><td><a href="http://www.sourcetreeapp.com/">SourceTree</a></td><td>å…费的 Mac Git Mercurial ä»¥åŠ SVN 客户端 and Mercurial</td></tr> + <tr><td><a href="http://www.git-tower.com/">Tower</a></td><td>Mac OS X Git 客户端</td></tr> + </tbody> + </table> + </div> +</div> +</div> +</div> </div> </wicket:extend> </body> diff --git a/src/main/java/com/gitblit/wicket/pages/MyTicketsPage.html b/src/main/java/com/gitblit/wicket/pages/MyTicketsPage.html new file mode 100644 index 00000000..b0bc1949 --- /dev/null +++ b/src/main/java/com/gitblit/wicket/pages/MyTicketsPage.html @@ -0,0 +1,84 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" + xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd" + xml:lang="en" + lang="en"> + +<body> + <wicket:extend> + <div class="container"> + <div class="row" style="padding-top:15px;min-height:500px;" > + <div class="tab-pane active" id="tickets"> + <!-- query controls --> + <div class="span3"> + <div class="hidden-phone"> + <div wicket:id="userTitlePanel"></div> + + <!-- search tickets form --> + <form class="form-search" style="margin: 10px 0px;" wicket:id="ticketSearchForm"> + <div class="input-append"> + <input type="text" class="input-medium search-query" style="border-radius: 14px 0 0 14px; padding-left: 14px;" id="ticketSearchBox" wicket:id="ticketSearchBox" value=""/> + <button class="btn" style="border-radius: 0 14px 14px 0px;margin-left:-5px;" type="submit"><i class="icon-search"></i></button> + </div> + </form> + + <!-- 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 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 class="divider"></li> + <li><a wicket:id="resetQuery"><i class="fa fa-bolt"></i> <wicket:message key="gb.reset"></wicket:message></a></li> + </ul> + + </div> + </div> + + <!-- tickets --> + <div class="span9"> + <div class="btn-toolbar" style="margin-top: 0px;"> + <div class="btn-group"> + <a class="btn dropdown-toggle" data-toggle="dropdown" href="#"><wicket:message key="gb.status"></wicket:message>: <span style="font-weight:bold;" wicket:id="selectedStatii"></span> <span class="caret"></span></a> + <ul class="dropdown-menu"> + <li><a wicket:id="openTickets"><wicket:message key="gb.open"></wicket:message></a></li> + <li><a wicket:id="closedTickets"><wicket:message key="gb.closed"></wicket:message></a></li> + <li><a wicket:id="allTickets"><wicket:message key="gb.all"></wicket:message></a></li> + <li class="divider"></li> + <li wicket:id="statii"><span wicket:id="statusLink"></span></li> + </ul> + </div> + <div class="btn-group"> + <a class="btn dropdown-toggle" data-toggle="dropdown" href="#"><i class="fa fa-sort"></i> <wicket:message key="gb.sort"></wicket:message>: <span style="font-weight:bold;" wicket:id="currentSort"></span> <span class="caret"></span></a> + <ul class="dropdown-menu"> + <li wicket:id="sort"><span wicket:id="sortLink"></span></li> + </ul> + </div> + + <div class="btn-group pull-right"> + <div class="pagination pagination-right pagination-small"> + <ul> + <li><a wicket:id="prevLink"><i class="fa fa-angle-double-left"></i></a></li> + <li wicket:id="pageLink"><span wicket:id="page"></span></li> + <li><a wicket:id="nextLink"><i class="fa fa-angle-double-right"></i></a></li> + </ul> + </div> + </div> + </div> + + <div wicket:id="ticketList"></div> + </div> + </div> + </div> + </div> + +</wicket:extend> +</body> +</html>
\ No newline at end of file diff --git a/src/main/java/com/gitblit/wicket/pages/MyTicketsPage.java b/src/main/java/com/gitblit/wicket/pages/MyTicketsPage.java new file mode 100644 index 00000000..c207d561 --- /dev/null +++ b/src/main/java/com/gitblit/wicket/pages/MyTicketsPage.java @@ -0,0 +1,392 @@ +/* + * 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.wicket.pages; + +import java.util.ArrayList; +import java.util.Arrays; +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.apache.wicket.markup.repeater.Item; +import org.apache.wicket.markup.repeater.data.DataView; +import org.apache.wicket.markup.repeater.data.ListDataProvider; + +import com.gitblit.Keys; +import com.gitblit.models.TicketModel; +import com.gitblit.models.TicketModel.Status; +import com.gitblit.models.UserModel; +import com.gitblit.tickets.QueryBuilder; +import com.gitblit.tickets.QueryResult; +import com.gitblit.tickets.TicketIndexer.Lucene; +import com.gitblit.utils.ArrayUtils; +import com.gitblit.utils.StringUtils; +import com.gitblit.wicket.GitBlitWebSession; +import com.gitblit.wicket.TicketsUI; +import com.gitblit.wicket.TicketsUI.TicketSort; +import com.gitblit.wicket.WicketUtils; +import com.gitblit.wicket.panels.LinkPanel; +import com.gitblit.wicket.panels.TicketListPanel; +import com.gitblit.wicket.panels.TicketSearchForm; +import com.gitblit.wicket.panels.UserTitlePanel; + +/** + * My Tickets page + * + * @author Christian Buisson + * @author James Moger + */ +public class MyTicketsPage extends RootPage { + + public MyTicketsPage() { + this(null); + } + + public MyTicketsPage(PageParameters params) { + super(params); + setupPage("", getString("gb.myTickets")); + + UserModel currentUser = GitBlitWebSession.get().getUser(); + if (currentUser == null || UserModel.ANONYMOUS.equals(currentUser)) { + setRedirect(true); + setResponsePage(getApplication().getHomePage()); + return; + } + + final String username = currentUser.getName(); + final String[] statiiParam = (params == null) ? TicketsUI.openStatii : params.getStringArray(Lucene.status.name()); + final String assignedToParam = (params == null) ? "" : params.getString(Lucene.responsible.name(), null); + final String milestoneParam = (params == null) ? "" : params.getString(Lucene.milestone.name(), null); + final String queryParam = (params == null || StringUtils.isEmpty(params.getString("q", null))) ? "watchedby:" + username : params.getString("q", null); + final String searchParam = (params == null) ? "" : params.getString("s", null); + final String sortBy = (params == null) ? "" : Lucene.fromString(params.getString("sort", Lucene.created.name())).name(); + final boolean desc = (params == null) ? true : !"asc".equals(params.getString("direction", "desc")); + + // add the user title panel + add(new UserTitlePanel("userTitlePanel", currentUser, getString("gb.myTickets"))); + + // add search form + add(new TicketSearchForm("ticketSearchForm", null, searchParam, getClass(), params)); + + // standard queries + add(new BookmarkablePageLink<Void>("changesQuery", MyTicketsPage.class, + queryParameters( + Lucene.type.matches(TicketModel.Type.Proposal.name()), + milestoneParam, + statiiParam, + assignedToParam, + sortBy, + desc, + 1))); + + add(new BookmarkablePageLink<Void>("bugsQuery", MyTicketsPage.class, + queryParameters( + Lucene.type.matches(TicketModel.Type.Bug.name()), + milestoneParam, + statiiParam, + assignedToParam, + sortBy, + desc, + 1))); + + add(new BookmarkablePageLink<Void>("enhancementsQuery", MyTicketsPage.class, + queryParameters( + Lucene.type.matches(TicketModel.Type.Enhancement.name()), + milestoneParam, + statiiParam, + assignedToParam, + sortBy, + desc, + 1))); + + add(new BookmarkablePageLink<Void>("tasksQuery", MyTicketsPage.class, + queryParameters( + Lucene.type.matches(TicketModel.Type.Task.name()), + milestoneParam, + statiiParam, + assignedToParam, + sortBy, + desc, + 1))); + + add(new BookmarkablePageLink<Void>("questionsQuery", MyTicketsPage.class, + queryParameters( + Lucene.type.matches(TicketModel.Type.Question.name()), + milestoneParam, + statiiParam, + assignedToParam, + sortBy, + desc, + 1))); + + add(new BookmarkablePageLink<Void>("resetQuery", MyTicketsPage.class, + queryParameters( + null, + milestoneParam, + TicketsUI.openStatii, + null, + null, + true, + 1))); + + add(new Label("userDivider")); + add(new BookmarkablePageLink<Void>("createdQuery", MyTicketsPage.class, + queryParameters( + Lucene.createdby.matches(username), + milestoneParam, + statiiParam, + assignedToParam, + sortBy, + desc, + 1))); + + add(new BookmarkablePageLink<Void>("watchedQuery", MyTicketsPage.class, + queryParameters( + Lucene.watchedby.matches(username), + milestoneParam, + statiiParam, + assignedToParam, + sortBy, + desc, + 1))); + add(new BookmarkablePageLink<Void>("mentionsQuery", MyTicketsPage.class, + queryParameters( + Lucene.mentions.matches(username), + milestoneParam, + statiiParam, + assignedToParam, + sortBy, + desc, + 1))); + add(new BookmarkablePageLink<Void>("responsibleQuery", MyTicketsPage.class, + queryParameters( + Lucene.responsible.matches(username), + milestoneParam, + statiiParam, + assignedToParam, + sortBy, + desc, + 1))); + + // states + if (ArrayUtils.isEmpty(statiiParam)) { + add(new Label("selectedStatii", getString("gb.all"))); + } else { + add(new Label("selectedStatii", StringUtils.flattenStrings(Arrays.asList(statiiParam), ","))); + } + add(new BookmarkablePageLink<Void>("openTickets", MyTicketsPage.class, queryParameters(queryParam, milestoneParam, TicketsUI.openStatii, assignedToParam, sortBy, desc, 1))); + add(new BookmarkablePageLink<Void>("closedTickets", MyTicketsPage.class, queryParameters(queryParam, milestoneParam, TicketsUI.closedStatii, assignedToParam, sortBy, desc, 1))); + add(new BookmarkablePageLink<Void>("allTickets", MyTicketsPage.class, queryParameters(queryParam, milestoneParam, null, assignedToParam, sortBy, desc, 1))); + + // by status + List<Status> statii = new ArrayList<Status>(Arrays.asList(Status.values())); + statii.remove(Status.Closed); + ListDataProvider<Status> resolutionsDp = new ListDataProvider<Status>(statii); + DataView<Status> statiiLinks = new DataView<Status>("statii", resolutionsDp) { + private static final long serialVersionUID = 1L; + + @Override + public void populateItem(final Item<Status> item) { + final Status status = item.getModelObject(); + PageParameters p = queryParameters(queryParam, milestoneParam, new String [] { status.name().toLowerCase() }, assignedToParam, sortBy, desc, 1); + String css = TicketsUI.getStatusClass(status); + item.add(new LinkPanel("statusLink", css, status.toString(), MyTicketsPage.class, p).setRenderBodyOnly(true)); + } + }; + add(statiiLinks); + + List<TicketSort> sortChoices = new ArrayList<TicketSort>(); + sortChoices.add(new TicketSort(getString("gb.sortNewest"), Lucene.created.name(), true)); + sortChoices.add(new TicketSort(getString("gb.sortOldest"), Lucene.created.name(), false)); + sortChoices.add(new TicketSort(getString("gb.sortMostRecentlyUpdated"), Lucene.updated.name(), true)); + sortChoices.add(new TicketSort(getString("gb.sortLeastRecentlyUpdated"), Lucene.updated.name(), false)); + sortChoices.add(new TicketSort(getString("gb.sortMostComments"), Lucene.comments.name(), true)); + sortChoices.add(new TicketSort(getString("gb.sortLeastComments"), Lucene.comments.name(), false)); + sortChoices.add(new TicketSort(getString("gb.sortMostPatchsetRevisions"), Lucene.patchsets.name(), true)); + 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)); + + TicketSort currentSort = sortChoices.get(0); + for (TicketSort ts : sortChoices) { + if (ts.sortBy.equals(sortBy) && desc == ts.desc) { + currentSort = ts; + break; + } + } + add(new Label("currentSort", currentSort.name)); + + ListDataProvider<TicketSort> sortChoicesDp = new ListDataProvider<TicketSort>(sortChoices); + DataView<TicketSort> sortMenu = new DataView<TicketSort>("sort", sortChoicesDp) { + private static final long serialVersionUID = 1L; + + @Override + public void populateItem(final Item<TicketSort> item) { + final TicketSort ts = item.getModelObject(); + PageParameters params = queryParameters(queryParam, milestoneParam, statiiParam, assignedToParam, ts.sortBy, ts.desc, 1); + item.add(new LinkPanel("sortLink", null, ts.name, MyTicketsPage.class, params).setRenderBodyOnly(true)); + } + }; + add(sortMenu); + + // Build Query here + QueryBuilder qb = new QueryBuilder(queryParam); + if (!qb.containsField(Lucene.status.name()) && !ArrayUtils.isEmpty(statiiParam)) { + // specify the states + boolean not = false; + QueryBuilder q = new QueryBuilder(); + for (String state : statiiParam) { + if (state.charAt(0) == '!') { + not = true; + q.and(Lucene.status.doesNotMatch(state.substring(1))); + } else { + q.or(Lucene.status.matches(state)); + } + } + if (not) { + qb.and(q.toString()); + } else { + qb.and(q.toSubquery().toString()); + } + } + + final String luceneQuery; + if (qb.containsField(Lucene.createdby.name()) + || qb.containsField(Lucene.responsible.name()) + || qb.containsField(Lucene.watchedby.name())) { + // focused "my tickets" query + luceneQuery = qb.build(); + } else { + // general "my tickets" query + QueryBuilder myQuery = new QueryBuilder(); + myQuery.or(Lucene.createdby.matches(username)); + myQuery.or(Lucene.responsible.matches(username)); + myQuery.or(Lucene.watchedby.matches(username)); + myQuery.and(qb.toSubquery().toString()); + luceneQuery = myQuery.build(); + } + + // paging links + int page = (params != null) ? Math.max(1, WicketUtils.getPage(params)) : 1; + int pageSize = app().settings().getInteger(Keys.tickets.perPage, 25); + + List<QueryResult> results; + if(StringUtils.isEmpty(searchParam)) { + results = app().tickets().queryFor(luceneQuery, page, pageSize, sortBy, desc); + } else { + results = app().tickets().searchFor(null, searchParam, page, pageSize); + } + + int totalResults = results.size() == 0 ? 0 : results.get(0).totalResults; + buildPager(queryParam, milestoneParam, statiiParam, assignedToParam, sortBy, desc, page, pageSize, results.size(), totalResults); + + final boolean showSwatch = app().settings().getBoolean(Keys.web.repositoryListSwatches, true); + add(new TicketListPanel("ticketList", results, showSwatch, true)); + } + + protected PageParameters queryParameters( + String query, + String milestone, + String[] states, + String assignedTo, + String sort, + boolean descending, + int page) { + + PageParameters params = WicketUtils.newRepositoryParameter(""); + if (!StringUtils.isEmpty(query)) { + params.add("q", query); + } + if (!StringUtils.isEmpty(milestone)) { + params.add(Lucene.milestone.name(), milestone); + } + if (!ArrayUtils.isEmpty(states)) { + for (String state : states) { + params.add(Lucene.status.name(), state); + } + } + if (!StringUtils.isEmpty(assignedTo)) { + params.add(Lucene.responsible.name(), assignedTo); + } + if (!StringUtils.isEmpty(sort)) { + params.add("sort", sort); + } + if (!descending) { + params.add("direction", "asc"); + } + if (page > 1) { + params.add("pg", "" + page); + } + return params; + } + + protected void buildPager( + final String query, + final String milestone, + final String [] states, + final String assignedTo, + final String sort, + final boolean desc, + final int page, + int pageSize, + int count, + int total) { + + boolean showNav = total > (2 * pageSize); + boolean allowPrev = page > 1; + boolean allowNext = (pageSize * (page - 1) + count) < total; + add(new BookmarkablePageLink<Void>("prevLink", MyTicketsPage.class, queryParameters(query, milestone, states, assignedTo, sort, desc, page - 1)).setEnabled(allowPrev).setVisible(showNav)); + add(new BookmarkablePageLink<Void>("nextLink", MyTicketsPage.class, queryParameters(query, milestone, states, assignedTo, sort, desc, page + 1)).setEnabled(allowNext).setVisible(showNav)); + + if (total <= pageSize) { + add(new Label("pageLink").setVisible(false)); + return; + } + + // determine page numbers to display + int pages = count == 0 ? 0 : ((total / pageSize) + (total % pageSize == 0 ? 0 : 1)); + // preferred number of pagelinks + int segments = 5; + if (pages < segments) { + // not enough data for preferred number of page links + segments = pages; + } + int minpage = Math.min(Math.max(1, page - 2), pages - (segments - 1)); + int maxpage = Math.min(pages, minpage + (segments - 1)); + List<Integer> sequence = new ArrayList<Integer>(); + for (int i = minpage; i <= maxpage; i++) { + sequence.add(i); + } + + ListDataProvider<Integer> pagesDp = new ListDataProvider<Integer>(sequence); + DataView<Integer> pagesView = new DataView<Integer>("pageLink", pagesDp) { + private static final long serialVersionUID = 1L; + + @Override + public void populateItem(final Item<Integer> item) { + final Integer i = item.getModelObject(); + LinkPanel link = new LinkPanel("page", null, "" + i, MyTicketsPage.class, queryParameters(query, milestone, states, assignedTo, sort, desc, i)); + link.setRenderBodyOnly(true); + if (i == page) { + WicketUtils.setCssClass(item, "active"); + } + item.add(link); + } + }; + add(pagesView); + } +} diff --git a/src/main/java/com/gitblit/wicket/pages/NewMilestonePage.html b/src/main/java/com/gitblit/wicket/pages/NewMilestonePage.html new file mode 100644 index 00000000..2ba5d5cc --- /dev/null +++ b/src/main/java/com/gitblit/wicket/pages/NewMilestonePage.html @@ -0,0 +1,37 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+
+<wicket:extend>
+<body onload="document.getElementById('name').focus();">
+
+<div class="container">
+ <!-- page header -->
+ <div class="title" style="font-size: 22px; color: rgb(0, 32, 96);padding: 3px 0px 7px;">
+ <span class="project"><wicket:message key="gb.newMilestone"></wicket:message></span>
+ </div>
+
+ <form style="padding-top:5px;" wicket:id="editForm">
+ <div class="row">
+ <div class="span12">
+ <!-- New Milestone Table -->
+ <table class="ticket">
+ <tr><th><wicket:message key="gb.milestone"></wicket:message></th><td class="edit"><input class="input-large" type="text" wicket:id="name" id="name"></input></td></tr>
+ <tr><th><wicket:message key="gb.due"></wicket:message></th><td class="edit"><input class="input-large" type="text" wicket:id="due"></input> <span class="help-inline" wicket:id="dueFormat"></span></td></tr>
+ </table>
+ </div>
+ </div>
+
+ <div class="row">
+ <div class="span12">
+ <div class="form-actions"><input class="btn btn-appmenu" type="submit" value="Create" wicket:message="value:gb.create" wicket:id="create" /> <input class="btn" type="submit" value="Cancel" wicket:message="value:gb.cancel" wicket:id="cancel" /></div>
+ </div>
+ </div>
+ </form>
+</div>
+</body>
+
+</wicket:extend>
+</html>
\ No newline at end of file diff --git a/src/main/java/com/gitblit/wicket/pages/NewMilestonePage.java b/src/main/java/com/gitblit/wicket/pages/NewMilestonePage.java new file mode 100644 index 00000000..cc331903 --- /dev/null +++ b/src/main/java/com/gitblit/wicket/pages/NewMilestonePage.java @@ -0,0 +1,140 @@ +/*
+ * 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.wicket.pages;
+
+import java.util.Date;
+
+import org.apache.wicket.PageParameters;
+import org.apache.wicket.RestartResponseException;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.markup.html.form.AjaxButton;
+import org.apache.wicket.extensions.markup.html.form.DateTextField;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.form.Button;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.markup.html.form.TextField;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+
+import com.gitblit.models.RepositoryModel;
+import com.gitblit.models.UserModel;
+import com.gitblit.tickets.TicketMilestone;
+import com.gitblit.utils.StringUtils;
+import com.gitblit.utils.TimeUtils;
+import com.gitblit.wicket.GitBlitWebSession;
+import com.gitblit.wicket.WicketUtils;
+
+/**
+ * Page for creating a new milestone.
+ *
+ * @author James Moger
+ *
+ */
+public class NewMilestonePage extends RepositoryPage {
+
+ private IModel<String> nameModel;
+
+ private IModel<Date> dueModel;
+
+ public NewMilestonePage(PageParameters params) {
+ super(params);
+
+ RepositoryModel model = getRepositoryModel();
+ if (!app().tickets().isAcceptingTicketUpdates(model)) {
+ // ticket service is read-only
+ throw new RestartResponseException(TicketsPage.class, WicketUtils.newOpenTicketsParameter(repositoryName));
+ }
+
+ UserModel currentUser = GitBlitWebSession.get().getUser();
+ if (currentUser == null) {
+ currentUser = UserModel.ANONYMOUS;
+ }
+
+ if (!currentUser.isAuthenticated || !currentUser.canAdmin(model)) {
+ // administration prohibited
+ throw new RestartResponseException(TicketsPage.class, WicketUtils.newOpenTicketsParameter(repositoryName));
+ }
+
+ setStatelessHint(false);
+ setOutputMarkupId(true);
+
+ Form<Void> form = new Form<Void>("editForm");
+ add(form);
+
+ nameModel = Model.of("");
+ dueModel = Model.of(new Date(System.currentTimeMillis() + TimeUtils.ONEDAY));
+
+ form.add(new TextField<String>("name", nameModel));
+ form.add(new DateTextField("due", dueModel, "yyyy-MM-dd"));
+ form.add(new Label("dueFormat", "yyyy-MM-dd"));
+
+ form.add(new AjaxButton("create") {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
+ String name = nameModel.getObject();
+ if (StringUtils.isEmpty(name)) {
+ // invalid name
+ return;
+ }
+
+ TicketMilestone milestone = app().tickets().getMilestone(getRepositoryModel(), name);
+ if (milestone != null) {
+ // milestone already exists
+ return;
+ }
+
+ Date due = dueModel.getObject();
+
+ UserModel currentUser = GitBlitWebSession.get().getUser();
+ String createdBy = currentUser.username;
+
+ milestone = app().tickets().createMilestone(getRepositoryModel(), name, createdBy);
+ if (milestone != null) {
+ milestone.due = due;
+ app().tickets().updateMilestone(getRepositoryModel(), milestone, createdBy);
+ throw new RestartResponseException(TicketsPage.class, WicketUtils.newOpenTicketsParameter(repositoryName));
+ } else {
+ // TODO error
+ }
+ }
+ });
+
+ Button cancel = new Button("cancel") {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public void onSubmit() {
+ setResponsePage(TicketsPage.class, WicketUtils.newOpenTicketsParameter(repositoryName));
+ }
+ };
+ cancel.setDefaultFormProcessing(false);
+ form.add(cancel);
+
+ }
+
+ @Override
+ protected String getPageName() {
+ return getString("gb.newMilestone");
+ }
+
+ @Override
+ protected Class<? extends BasePage> getRepoNavPageClass() {
+ return TicketsPage.class;
+ }
+}
diff --git a/src/main/java/com/gitblit/wicket/pages/NewRepositoryPage.html b/src/main/java/com/gitblit/wicket/pages/NewRepositoryPage.html new file mode 100644 index 00000000..115b8c10 --- /dev/null +++ b/src/main/java/com/gitblit/wicket/pages/NewRepositoryPage.html @@ -0,0 +1,41 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+
+<wicket:extend>
+<body onload="document.getElementById('name').focus();">
+ <form style="padding-top:5px;" wicket:id="editForm">
+<div class="row">
+ <div class="span12">
+
+ <div wicket:id="namePanel"></div>
+
+ <hr/>
+
+ <div wicket:id="accessPolicyPanel"></div>
+
+ <hr/>
+
+ <h4><wicket:message key="gb.initialCommit"></wicket:message></h4>
+ <p><wicket:message key="gb.initialCommitDescription"></wicket:message></p>
+
+ <div wicket:id="addReadme"></div>
+
+ <div wicket:id="addGitIgnore"></div>
+
+ <div wicket:id="addGitFlow"></div>
+ </div>
+</div>
+
+<div class="row">
+<div class="span12">
+ <div class="form-actions"><input class="btn btn-appmenu" type="submit" value="Create" wicket:message="value:gb.create" wicket:id="create" /></div>
+</div>
+</div>
+
+</form>
+</body>
+</wicket:extend>
+</html>
\ No newline at end of file diff --git a/src/main/java/com/gitblit/wicket/pages/NewRepositoryPage.java b/src/main/java/com/gitblit/wicket/pages/NewRepositoryPage.java new file mode 100644 index 00000000..46877168 --- /dev/null +++ b/src/main/java/com/gitblit/wicket/pages/NewRepositoryPage.java @@ -0,0 +1,372 @@ +/*
+ * 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.wicket.pages;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.wicket.behavior.SimpleAttributeModifier;
+import org.apache.wicket.markup.html.form.Button;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.model.CompoundPropertyModel;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+import org.eclipse.jgit.dircache.DirCache;
+import org.eclipse.jgit.dircache.DirCacheBuilder;
+import org.eclipse.jgit.dircache.DirCacheEntry;
+import org.eclipse.jgit.lib.CommitBuilder;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.RefUpdate.Result;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
+
+import com.gitblit.Constants;
+import com.gitblit.Constants.AccessRestrictionType;
+import com.gitblit.Constants.AuthorizationControl;
+import com.gitblit.GitBlitException;
+import com.gitblit.Keys;
+import com.gitblit.models.RepositoryModel;
+import com.gitblit.models.UserModel;
+import com.gitblit.utils.ArrayUtils;
+import com.gitblit.utils.FileUtils;
+import com.gitblit.utils.StringUtils;
+import com.gitblit.wicket.GitBlitWebSession;
+import com.gitblit.wicket.WicketUtils;
+import com.gitblit.wicket.panels.AccessPolicyPanel;
+import com.gitblit.wicket.panels.BooleanChoiceOption;
+import com.gitblit.wicket.panels.BooleanOption;
+import com.gitblit.wicket.panels.RepositoryNamePanel;
+
+public class NewRepositoryPage extends RootSubPage {
+
+ private final RepositoryModel repositoryModel;
+ private IModel<Boolean> addReadmeModel;
+ private Model<String> gitignoreModel;
+ private IModel<Boolean> addGitflowModel;
+ private IModel<Boolean> addGitignoreModel;
+ private AccessPolicyPanel accessPolicyPanel;
+ private RepositoryNamePanel namePanel;
+
+ public NewRepositoryPage() {
+ // create constructor
+ super();
+ repositoryModel = new RepositoryModel();
+
+ setupPage(getString("gb.newRepository"), "");
+
+ setStatelessHint(false);
+ setOutputMarkupId(true);
+ }
+
+ @Override
+ protected boolean requiresPageMap() {
+ return true;
+ }
+
+ @Override
+ protected Class<? extends BasePage> getRootNavPageClass() {
+ return RepositoriesPage.class;
+ }
+
+ @Override
+ protected void onInitialize() {
+ super.onInitialize();
+
+ CompoundPropertyModel<RepositoryModel> rModel = new CompoundPropertyModel<>(repositoryModel);
+ Form<RepositoryModel> form = new Form<RepositoryModel>("editForm", rModel) {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void onSubmit() {
+ try {
+ if (!namePanel.updateModel(repositoryModel)) {
+ return;
+ }
+ accessPolicyPanel.updateModel(repositoryModel);
+
+ repositoryModel.owners = new ArrayList<String>();
+ repositoryModel.owners.add(GitBlitWebSession.get().getUsername());
+
+ // setup branch defaults
+ boolean useGitFlow = addGitflowModel.getObject();
+
+ repositoryModel.HEAD = Constants.R_MASTER;
+ repositoryModel.mergeTo = Constants.MASTER;
+ if (useGitFlow) {
+ // tickets normally merge to develop unless they are hotfixes
+ repositoryModel.mergeTo = Constants.DEVELOP;
+ }
+
+ repositoryModel.allowForks = app().settings().getBoolean(Keys.web.allowForking, true);
+
+ // optionally generate an initial commit
+ boolean addReadme = addReadmeModel.getObject();
+ String gitignore = null;
+ boolean addGitignore = addGitignoreModel.getObject();
+ if (addGitignore) {
+ gitignore = gitignoreModel.getObject();
+ if (StringUtils.isEmpty(gitignore)) {
+ throw new GitBlitException(getString("gb.pleaseSelectGitIgnore"));
+ }
+ }
+
+ // init the repository
+ app().gitblit().updateRepositoryModel(repositoryModel.name, repositoryModel, true);
+
+ // optionally create an initial commit
+ initialCommit(repositoryModel, addReadme, gitignore, useGitFlow);
+
+ } catch (GitBlitException e) {
+ error(e.getMessage());
+ return;
+ }
+ setRedirect(true);
+ setResponsePage(SummaryPage.class, WicketUtils.newRepositoryParameter(repositoryModel.name));
+ }
+ };
+
+ // do not let the browser pre-populate these fields
+ form.add(new SimpleAttributeModifier("autocomplete", "off"));
+
+ namePanel = new RepositoryNamePanel("namePanel", repositoryModel);
+ form.add(namePanel);
+
+ // prepare the default access controls
+ AccessRestrictionType defaultRestriction = AccessRestrictionType.fromName(
+ app().settings().getString(Keys.git.defaultAccessRestriction, AccessRestrictionType.PUSH.name()));
+ if (AccessRestrictionType.NONE == defaultRestriction) {
+ defaultRestriction = AccessRestrictionType.PUSH;
+ }
+ AuthorizationControl defaultControl = AuthorizationControl.fromName(
+ app().settings().getString(Keys.git.defaultAuthorizationControl, AuthorizationControl.NAMED.name()));
+
+ if (AuthorizationControl.AUTHENTICATED == defaultControl) {
+ defaultRestriction = AccessRestrictionType.PUSH;
+ }
+
+ repositoryModel.authorizationControl = defaultControl;
+ repositoryModel.accessRestriction = defaultRestriction;
+
+ accessPolicyPanel = new AccessPolicyPanel("accessPolicyPanel", repositoryModel);
+ form.add(accessPolicyPanel);
+
+ //
+ // initial commit options
+ //
+
+ // add README
+ addReadmeModel = Model.of(false);
+ form.add(new BooleanOption("addReadme",
+ getString("gb.initWithReadme"),
+ getString("gb.initWithReadmeDescription"),
+ addReadmeModel));
+
+ // add .gitignore
+ File gitignoreDir = app().runtime().getFileOrFolder(Keys.git.gitignoreFolder, "${baseFolder}/gitignore");
+ File [] files = gitignoreDir.listFiles();
+ if (files == null) {
+ files = new File[0];
+ }
+ List<String> gitignores = new ArrayList<String>();
+ for (File file : files) {
+ if (file.isFile() && file.getName().endsWith(".gitignore")) {
+ gitignores.add(StringUtils.stripFileExtension(file.getName()));
+ }
+ }
+ Collections.sort(gitignores);
+
+ gitignoreModel = Model.of("");
+ addGitignoreModel = Model.of(false);
+ form.add(new BooleanChoiceOption<String>("addGitIgnore",
+ getString("gb.initWithGitignore"),
+ getString("gb.initWithGitignoreDescription"),
+ addGitignoreModel,
+ gitignoreModel,
+ gitignores).setVisible(gitignores.size() > 0));
+
+ // TODO consider gitflow at creation (ticket-55)
+ addGitflowModel = Model.of(false);
+ form.add(new BooleanOption("addGitFlow",
+ "Include a .gitflow file",
+ "This will generate a config file which guides Git clients in setting up Gitflow branches.",
+ addGitflowModel).setVisible(false));
+
+ form.add(new Button("create"));
+
+ add(form);
+ }
+
+ /**
+ * Prepare the initial commit for the repository.
+ *
+ * @param repository
+ * @param addReadme
+ * @param gitignore
+ * @param addGitFlow
+ * @return true if an initial commit was created
+ */
+ protected boolean initialCommit(RepositoryModel repository, boolean addReadme, String gitignore,
+ boolean addGitFlow) {
+ boolean initialCommit = addReadme || !StringUtils.isEmpty(gitignore) || addGitFlow;
+ if (!initialCommit) {
+ return false;
+ }
+
+ // build an initial commit
+ boolean success = false;
+ Repository db = app().repositories().getRepository(repositoryModel.name);
+ ObjectInserter odi = db.newObjectInserter();
+ try {
+
+ UserModel user = GitBlitWebSession.get().getUser();
+ PersonIdent author = new PersonIdent(user.getDisplayName(), user.emailAddress);
+
+ DirCache newIndex = DirCache.newInCore();
+ DirCacheBuilder indexBuilder = newIndex.builder();
+
+ if (addReadme) {
+ // insert a README
+ String title = StringUtils.stripDotGit(StringUtils.getLastPathElement(repositoryModel.name));
+ String description = repositoryModel.description == null ? "" : repositoryModel.description;
+ String readme = String.format("## %s\n\n%s\n\n", title, description);
+ byte [] bytes = readme.getBytes(Constants.ENCODING);
+
+ DirCacheEntry entry = new DirCacheEntry("README.md");
+ entry.setLength(bytes.length);
+ entry.setLastModified(System.currentTimeMillis());
+ entry.setFileMode(FileMode.REGULAR_FILE);
+ entry.setObjectId(odi.insert(org.eclipse.jgit.lib.Constants.OBJ_BLOB, bytes));
+
+ indexBuilder.add(entry);
+ }
+
+ if (!StringUtils.isEmpty(gitignore)) {
+ // insert a .gitignore file
+ File dir = app().runtime().getFileOrFolder(Keys.git.gitignoreFolder, "${baseFolder}/gitignore");
+ File file = new File(dir, gitignore + ".gitignore");
+ if (file.exists() && file.length() > 0) {
+ byte [] bytes = FileUtils.readContent(file);
+ if (!ArrayUtils.isEmpty(bytes)) {
+ DirCacheEntry entry = new DirCacheEntry(".gitignore");
+ entry.setLength(bytes.length);
+ entry.setLastModified(System.currentTimeMillis());
+ entry.setFileMode(FileMode.REGULAR_FILE);
+ entry.setObjectId(odi.insert(org.eclipse.jgit.lib.Constants.OBJ_BLOB, bytes));
+
+ indexBuilder.add(entry);
+ }
+ }
+ }
+
+ if (addGitFlow) {
+ // insert a .gitflow file
+ Config config = new Config();
+ config.setString("gitflow", null, "masterBranch", Constants.MASTER);
+ config.setString("gitflow", null, "developBranch", Constants.DEVELOP);
+ config.setString("gitflow", null, "featureBranchPrefix", "feature/");
+ config.setString("gitflow", null, "releaseBranchPrefix", "release/");
+ config.setString("gitflow", null, "hotfixBranchPrefix", "hotfix/");
+ config.setString("gitflow", null, "supportBranchPrefix", "support/");
+ config.setString("gitflow", null, "versionTagPrefix", "");
+
+ byte [] bytes = config.toText().getBytes(Constants.ENCODING);
+
+ DirCacheEntry entry = new DirCacheEntry(".gitflow");
+ entry.setLength(bytes.length);
+ entry.setLastModified(System.currentTimeMillis());
+ entry.setFileMode(FileMode.REGULAR_FILE);
+ entry.setObjectId(odi.insert(org.eclipse.jgit.lib.Constants.OBJ_BLOB, bytes));
+
+ indexBuilder.add(entry);
+ }
+
+ indexBuilder.finish();
+
+ if (newIndex.getEntryCount() == 0) {
+ // nothing to commit
+ return false;
+ }
+
+ ObjectId treeId = newIndex.writeTree(odi);
+
+ // Create a commit object
+ CommitBuilder commit = new CommitBuilder();
+ commit.setAuthor(author);
+ commit.setCommitter(author);
+ commit.setEncoding(Constants.ENCODING);
+ commit.setMessage("Initial commit");
+ commit.setTreeId(treeId);
+
+ // Insert the commit into the repository
+ ObjectId commitId = odi.insert(commit);
+ odi.flush();
+
+ // set the branch refs
+ RevWalk revWalk = new RevWalk(db);
+ try {
+ // set the master branch
+ RevCommit revCommit = revWalk.parseCommit(commitId);
+ RefUpdate masterRef = db.updateRef(Constants.R_MASTER);
+ masterRef.setNewObjectId(commitId);
+ masterRef.setRefLogMessage("commit: " + revCommit.getShortMessage(), false);
+ Result masterRC = masterRef.update();
+ switch (masterRC) {
+ case NEW:
+ success = true;
+ break;
+ default:
+ success = false;
+ }
+
+ if (addGitFlow) {
+ // set the develop branch for git-flow
+ RefUpdate developRef = db.updateRef(Constants.R_DEVELOP);
+ developRef.setNewObjectId(commitId);
+ developRef.setRefLogMessage("commit: " + revCommit.getShortMessage(), false);
+ Result developRC = developRef.update();
+ switch (developRC) {
+ case NEW:
+ success = true;
+ break;
+ default:
+ success = false;
+ }
+ }
+ } finally {
+ revWalk.release();
+ }
+ } catch (UnsupportedEncodingException e) {
+ logger().error(null, e);
+ } catch (IOException e) {
+ logger().error(null, e);
+ } finally {
+ odi.release();
+ db.close();
+ }
+ return success;
+ }
+}
diff --git a/src/main/java/com/gitblit/wicket/pages/ProjectPage.java b/src/main/java/com/gitblit/wicket/pages/ProjectPage.java index b92282b4..d358b775 100644 --- a/src/main/java/com/gitblit/wicket/pages/ProjectPage.java +++ b/src/main/java/com/gitblit/wicket/pages/ProjectPage.java @@ -26,6 +26,11 @@ import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.link.ExternalLink;
import com.gitblit.Keys;
+import com.gitblit.models.Menu.MenuDivider;
+import com.gitblit.models.Menu.MenuItem;
+import com.gitblit.models.Menu.ParameterMenuItem;
+import com.gitblit.models.NavLink.DropDownPageMenuNavLink;
+import com.gitblit.models.NavLink;
import com.gitblit.models.ProjectModel;
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.UserModel;
@@ -37,9 +42,6 @@ import com.gitblit.wicket.CacheControl.LastModified; import com.gitblit.wicket.GitBlitWebApp;
import com.gitblit.wicket.GitBlitWebSession;
import com.gitblit.wicket.GitblitRedirectException;
-import com.gitblit.wicket.PageRegistration;
-import com.gitblit.wicket.PageRegistration.DropDownMenuItem;
-import com.gitblit.wicket.PageRegistration.DropDownMenuRegistration;
import com.gitblit.wicket.WicketUtils;
import com.gitblit.wicket.panels.FilterableRepositoryList;
@@ -159,10 +161,10 @@ public class ProjectPage extends DashboardPage { }
@Override
- protected void addDropDownMenus(List<PageRegistration> pages) {
+ protected void addDropDownMenus(List<NavLink> navLinks) {
PageParameters params = getPageParameters();
- DropDownMenuRegistration menu = new DropDownMenuRegistration("gb.filters",
+ DropDownPageMenuNavLink menu = new DropDownPageMenuNavLink("gb.filters",
ProjectPage.class);
// preserve time filter option on repository choices
menu.menuItems.addAll(getRepositoryFilterItems(params));
@@ -172,15 +174,15 @@ public class ProjectPage extends DashboardPage { if (menu.menuItems.size() > 0) {
// Reset Filter
- menu.menuItems.add(new DropDownMenuItem(getString("gb.reset"), "p", WicketUtils.getProjectName(params)));
+ menu.menuItems.add(new ParameterMenuItem(getString("gb.reset"), "p", WicketUtils.getProjectName(params)));
}
- pages.add(menu);
+ navLinks.add(menu);
- DropDownMenuRegistration projects = new DropDownMenuRegistration("gb.projects",
+ DropDownPageMenuNavLink projects = new DropDownPageMenuNavLink("gb.projects",
ProjectPage.class);
projects.menuItems.addAll(getProjectsMenu());
- pages.add(projects);
+ navLinks.add(projects);
}
@Override
@@ -202,8 +204,8 @@ public class ProjectPage extends DashboardPage { return null;
}
- protected List<DropDownMenuItem> getProjectsMenu() {
- List<DropDownMenuItem> menu = new ArrayList<DropDownMenuItem>();
+ protected List<MenuItem> getProjectsMenu() {
+ List<MenuItem> menu = new ArrayList<MenuItem>();
List<ProjectModel> projects = new ArrayList<ProjectModel>();
for (ProjectModel model : getProjectModels()) {
if (!model.isUserProject()) {
@@ -230,11 +232,11 @@ public class ProjectPage extends DashboardPage { }
for (ProjectModel project : projects) {
- menu.add(new DropDownMenuItem(project.getDisplayName(), "p", project.name));
+ menu.add(new ParameterMenuItem(project.getDisplayName(), "p", project.name));
}
if (showAllProjects) {
- menu.add(new DropDownMenuItem());
- menu.add(new DropDownMenuItem("all projects", null, null));
+ menu.add(new MenuDivider());
+ menu.add(new ParameterMenuItem("all projects"));
}
return menu;
}
diff --git a/src/main/java/com/gitblit/wicket/pages/ProjectsPage.java b/src/main/java/com/gitblit/wicket/pages/ProjectsPage.java index 77d49846..f04fa78a 100644 --- a/src/main/java/com/gitblit/wicket/pages/ProjectsPage.java +++ b/src/main/java/com/gitblit/wicket/pages/ProjectsPage.java @@ -24,11 +24,11 @@ import org.apache.wicket.markup.repeater.data.DataView; import org.apache.wicket.markup.repeater.data.ListDataProvider;
import com.gitblit.Keys;
+import com.gitblit.models.Menu.ParameterMenuItem;
+import com.gitblit.models.NavLink.DropDownPageMenuNavLink;
+import com.gitblit.models.NavLink;
import com.gitblit.models.ProjectModel;
import com.gitblit.wicket.GitBlitWebSession;
-import com.gitblit.wicket.PageRegistration;
-import com.gitblit.wicket.PageRegistration.DropDownMenuItem;
-import com.gitblit.wicket.PageRegistration.DropDownMenuRegistration;
import com.gitblit.wicket.WicketUtils;
import com.gitblit.wicket.panels.LinkPanel;
@@ -115,10 +115,10 @@ public class ProjectsPage extends RootPage { }
@Override
- protected void addDropDownMenus(List<PageRegistration> pages) {
+ protected void addDropDownMenus(List<NavLink> navLinks) {
PageParameters params = getPageParameters();
- DropDownMenuRegistration menu = new DropDownMenuRegistration("gb.filters",
+ DropDownPageMenuNavLink menu = new DropDownPageMenuNavLink("gb.filters",
ProjectsPage.class);
// preserve time filter option on repository choices
menu.menuItems.addAll(getRepositoryFilterItems(params));
@@ -128,9 +128,9 @@ public class ProjectsPage extends RootPage { if (menu.menuItems.size() > 0) {
// Reset Filter
- menu.menuItems.add(new DropDownMenuItem(getString("gb.reset"), null, null));
+ menu.menuItems.add(new ParameterMenuItem(getString("gb.reset")));
}
- pages.add(menu);
+ navLinks.add(menu);
}
}
diff --git a/src/main/java/com/gitblit/wicket/pages/RepositoriesPage.java b/src/main/java/com/gitblit/wicket/pages/RepositoriesPage.java index f4ddf402..660b4ed1 100644 --- a/src/main/java/com/gitblit/wicket/pages/RepositoriesPage.java +++ b/src/main/java/com/gitblit/wicket/pages/RepositoriesPage.java @@ -21,6 +21,7 @@ import java.io.InputStream; import java.io.InputStreamReader;
import java.text.MessageFormat;
import java.util.ArrayList;
+import java.util.Iterator;
import java.util.List;
import org.apache.wicket.Component;
@@ -29,15 +30,15 @@ import org.apache.wicket.markup.html.basic.Label; import org.eclipse.jgit.lib.Constants;
import com.gitblit.Keys;
+import com.gitblit.models.Menu.ParameterMenuItem;
+import com.gitblit.models.NavLink;
+import com.gitblit.models.NavLink.DropDownPageMenuNavLink;
import com.gitblit.models.RepositoryModel;
import com.gitblit.utils.MarkdownUtils;
import com.gitblit.utils.StringUtils;
import com.gitblit.wicket.CacheControl;
import com.gitblit.wicket.CacheControl.LastModified;
import com.gitblit.wicket.GitBlitWebSession;
-import com.gitblit.wicket.PageRegistration;
-import com.gitblit.wicket.PageRegistration.DropDownMenuItem;
-import com.gitblit.wicket.PageRegistration.DropDownMenuRegistration;
import com.gitblit.wicket.WicketUtils;
import com.gitblit.wicket.panels.RepositoriesPanel;
@@ -79,7 +80,17 @@ public class RepositoriesPage extends RootPage { .setEscapeModelStrings(false).setVisible(message.length() > 0);
add(repositoriesMessage);
+ // conditionally include personal repositories in this page
List<RepositoryModel> repositories = getRepositories(params);
+ if (!app().settings().getBoolean(Keys.web.includePersonalRepositories, true)) {
+ Iterator<RepositoryModel> itr = repositories.iterator();
+ while (itr.hasNext()) {
+ RepositoryModel rm = itr.next();
+ if (rm.isPersonalRepository()) {
+ itr.remove();
+ }
+ }
+ }
RepositoriesPanel repositoriesPanel = new RepositoriesPanel("repositoriesPanel", showAdmin,
true, repositories, true, getAccessRestrictions());
@@ -92,10 +103,10 @@ public class RepositoriesPage extends RootPage { }
@Override
- protected void addDropDownMenus(List<PageRegistration> pages) {
+ protected void addDropDownMenus(List<NavLink> navLinks) {
PageParameters params = getPageParameters();
- DropDownMenuRegistration menu = new DropDownMenuRegistration("gb.filters",
+ DropDownPageMenuNavLink menu = new DropDownPageMenuNavLink("gb.filters",
RepositoriesPage.class);
// preserve time filter option on repository choices
menu.menuItems.addAll(getRepositoryFilterItems(params));
@@ -105,10 +116,10 @@ public class RepositoriesPage extends RootPage { if (menu.menuItems.size() > 0) {
// Reset Filter
- menu.menuItems.add(new DropDownMenuItem(getString("gb.reset"), null, null));
+ menu.menuItems.add(new ParameterMenuItem(getString("gb.reset")));
}
- pages.add(menu);
+ navLinks.add(menu);
}
private String readMarkdown(String messageSource, String resource) {
diff --git a/src/main/java/com/gitblit/wicket/pages/RepositoryPage.html b/src/main/java/com/gitblit/wicket/pages/RepositoryPage.html index cb4f1b67..22544bc8 100644 --- a/src/main/java/com/gitblit/wicket/pages/RepositoryPage.html +++ b/src/main/java/com/gitblit/wicket/pages/RepositoryPage.html @@ -17,7 +17,7 @@ <form class="form-search" style="margin: 0px;" wicket:id="searchForm">
<div class="input-append">
<select class="span2" style="border-radius: 4px;" wicket:id="searchType"/>
- <input type="text" class="search-query" style="width: 170px;border-radius: 14px 0 0 14px; padding-left: 14px;" id="searchBox" wicket:id="searchBox" value=""/>
+ <input type="text" class="input-medium search-query" style="border-radius: 14px 0 0 14px; padding-left: 14px;" id="searchBox" wicket:id="searchBox" value=""/>
<button class="btn" style="border-radius: 0 14px 14px 0px;margin-left:-5px;" type="submit"><i class="icon-search"></i></button>
</div>
</form>
diff --git a/src/main/java/com/gitblit/wicket/pages/RepositoryPage.java b/src/main/java/com/gitblit/wicket/pages/RepositoryPage.java index 2b97bc16..fcf659af 100644 --- a/src/main/java/com/gitblit/wicket/pages/RepositoryPage.java +++ b/src/main/java/com/gitblit/wicket/pages/RepositoryPage.java @@ -21,7 +21,6 @@ import java.util.ArrayList; import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
-import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
@@ -49,6 +48,10 @@ import org.slf4j.LoggerFactory; import com.gitblit.Constants;
import com.gitblit.GitBlitException;
import com.gitblit.Keys;
+import com.gitblit.extensions.RepositoryNavLinkExtension;
+import com.gitblit.models.NavLink;
+import com.gitblit.models.NavLink.ExternalNavLink;
+import com.gitblit.models.NavLink.PageNavLink;
import com.gitblit.models.ProjectModel;
import com.gitblit.models.RefModel;
import com.gitblit.models.RepositoryModel;
@@ -57,7 +60,6 @@ import com.gitblit.models.UserModel; import com.gitblit.models.UserRepositoryPreferences;
import com.gitblit.servlet.PagesServlet;
import com.gitblit.servlet.SyndicationServlet;
-import com.gitblit.tickets.TicketIndexer.Lucene;
import com.gitblit.utils.ArrayUtils;
import com.gitblit.utils.BugtraqProcessor;
import com.gitblit.utils.DeepCopier;
@@ -66,8 +68,6 @@ import com.gitblit.utils.RefLogUtils; import com.gitblit.utils.StringUtils;
import com.gitblit.wicket.CacheControl;
import com.gitblit.wicket.GitBlitWebSession;
-import com.gitblit.wicket.PageRegistration;
-import com.gitblit.wicket.PageRegistration.OtherPageLink;
import com.gitblit.wicket.SessionlessForm;
import com.gitblit.wicket.WicketUtils;
import com.gitblit.wicket.panels.LinkPanel;
@@ -90,7 +90,6 @@ public abstract class RepositoryPage extends RootPage { private Map<String, SubmoduleModel> submodules;
- private final Map<String, PageRegistration> registeredPages;
private boolean showAdmin;
private boolean isOwner;
@@ -109,7 +108,7 @@ public abstract class RepositoryPage extends RootPage { error(MessageFormat.format(getString("gb.repositoryNotSpecifiedFor"), getPageName()), true);
}
- if (!getRepositoryModel().hasCommits) {
+ if (!getRepositoryModel().hasCommits && getClass() != EmptyRepositoryPage.class) {
throw new RestartResponseException(EmptyRepositoryPage.class, params);
}
@@ -149,12 +148,21 @@ public abstract class RepositoryPage extends RootPage { }
}
- // register the available page links for this page and user
- registeredPages = registerPages();
+ showAdmin = false;
+ if (app().settings().getBoolean(Keys.web.authenticateAdminPages, true)) {
+ boolean allowAdmin = app().settings().getBoolean(Keys.web.allowAdministration, false);
+ showAdmin = allowAdmin && GitBlitWebSession.get().canAdmin();
+ } else {
+ showAdmin = app().settings().getBoolean(Keys.web.allowAdministration, false);
+ }
+ isOwner = GitBlitWebSession.get().isLoggedIn()
+ && (getRepositoryModel().isOwner(GitBlitWebSession.get().getUsername()));
+
+ // register the available navigation links for this page and user
+ List<NavLink> navLinks = registerNavLinks();
- // standard page links
- List<PageRegistration> pages = new ArrayList<PageRegistration>(registeredPages.values());
- NavigationPanel navigationPanel = new NavigationPanel("repositoryNavPanel", getRepoNavPageClass(), pages);
+ // standard navigation links
+ NavigationPanel navigationPanel = new NavigationPanel("repositoryNavPanel", getRepoNavPageClass(), navLinks);
add(navigationPanel);
add(new ExternalLink("syndication", SyndicationServlet.asLink(getRequest()
@@ -182,59 +190,61 @@ public abstract class RepositoryPage extends RootPage { return new BugtraqProcessor(app().settings());
}
- private Map<String, PageRegistration> registerPages() {
+ private List<NavLink> registerNavLinks() {
PageParameters params = null;
if (!StringUtils.isEmpty(repositoryName)) {
params = WicketUtils.newRepositoryParameter(repositoryName);
}
- Map<String, PageRegistration> pages = new LinkedHashMap<String, PageRegistration>();
+ List<NavLink> navLinks = new ArrayList<NavLink>();
Repository r = getRepository();
RepositoryModel model = getRepositoryModel();
// standard links
if (RefLogUtils.getRefLogBranch(r) == null) {
- pages.put("summary", new PageRegistration("gb.summary", SummaryPage.class, params));
+ navLinks.add(new PageNavLink("gb.summary", SummaryPage.class, params));
} else {
- pages.put("summary", new PageRegistration("gb.summary", SummaryPage.class, params));
-// pages.put("overview", new PageRegistration("gb.overview", OverviewPage.class, params));
- pages.put("reflog", new PageRegistration("gb.reflog", ReflogPage.class, params));
- }
- pages.put("commits", new PageRegistration("gb.commits", LogPage.class, params));
- pages.put("tree", new PageRegistration("gb.tree", TreePage.class, params));
- if (app().tickets().isReady() && (app().tickets().isAcceptingNewTickets(getRepositoryModel()) || app().tickets().hasTickets(getRepositoryModel()))) {
- PageParameters tParams = new PageParameters(params);
- for (String state : TicketsPage.openStatii) {
- tParams.add(Lucene.status.name(), state);
- }
- pages.put("tickets", new PageRegistration("gb.tickets", TicketsPage.class, tParams)); + navLinks.add(new PageNavLink("gb.summary", SummaryPage.class, params));
+ // pages.put("overview", new PageRegistration("gb.overview", OverviewPage.class, params));
+ navLinks.add(new PageNavLink("gb.reflog", ReflogPage.class, params));
+ }
+
+ if (!model.hasCommits) {
+ return navLinks;
+ }
+
+ navLinks.add(new PageNavLink("gb.commits", LogPage.class, params));
+ navLinks.add(new PageNavLink("gb.tree", TreePage.class, params));
+ if (app().tickets().isReady() && (app().tickets().isAcceptingNewTickets(model) || app().tickets().hasTickets(model))) {
+ PageParameters tParams = WicketUtils.newOpenTicketsParameter(repositoryName);
+ navLinks.add(new PageNavLink("gb.tickets", TicketsPage.class, tParams)); } - pages.put("docs", new PageRegistration("gb.docs", DocsPage.class, params, true));
+ navLinks.add(new PageNavLink("gb.docs", DocsPage.class, params, true));
if (app().settings().getBoolean(Keys.web.allowForking, true)) {
- pages.put("forks", new PageRegistration("gb.forks", ForksPage.class, params, true));
+ navLinks.add(new PageNavLink("gb.forks", ForksPage.class, params, true));
}
- pages.put("compare", new PageRegistration("gb.compare", ComparePage.class, params, true)); + navLinks.add(new PageNavLink("gb.compare", ComparePage.class, params, true)); // conditional links
- // per-repository extra page links
+ // per-repository extra navlinks
if (JGitUtils.getPagesBranch(r) != null) {
- OtherPageLink pagesLink = new OtherPageLink("gb.pages", PagesServlet.asLink(
+ ExternalNavLink pagesLink = new ExternalNavLink("gb.pages", PagesServlet.asLink(
getRequest().getRelativePathPrefixToContextRoot(), repositoryName, null), true);
- pages.put("pages", pagesLink);
+ navLinks.add(pagesLink);
}
- // Conditionally add edit link
- showAdmin = false;
- if (app().settings().getBoolean(Keys.web.authenticateAdminPages, true)) {
- boolean allowAdmin = app().settings().getBoolean(Keys.web.allowAdministration, false);
- showAdmin = allowAdmin && GitBlitWebSession.get().canAdmin();
- } else {
- showAdmin = app().settings().getBoolean(Keys.web.allowAdministration, false);
+ UserModel user = UserModel.ANONYMOUS;
+ if (GitBlitWebSession.get().isLoggedIn()) {
+ user = GitBlitWebSession.get().getUser();
}
- isOwner = GitBlitWebSession.get().isLoggedIn()
- && (model.isOwner(GitBlitWebSession.get()
- .getUsername()));
- return pages;
+
+ // add repository nav link extensions
+ List<RepositoryNavLinkExtension> extensions = app().plugins().getExtensions(RepositoryNavLinkExtension.class);
+ for (RepositoryNavLinkExtension ext : extensions) {
+ navLinks.addAll(ext.getNavLinks(user, model));
+ }
+
+ return navLinks;
}
protected boolean allowForkControls() {
@@ -306,7 +316,7 @@ public abstract class RepositoryPage extends RootPage { }
// (un)star link allows a user to star a repository
- if (user.isAuthenticated) {
+ if (user.isAuthenticated && model.hasCommits) {
PageParameters starParams = DeepCopier.copy(getPageParameters());
starParams.put(PARAM_STAR, !user.getPreferences().isStarredRepository(model.name));
String toggleStarUrl = getRequestCycle().urlFor(getClass(), starParams).toString();
@@ -333,7 +343,7 @@ public abstract class RepositoryPage extends RootPage { } else {
String fork = app().repositories().getFork(user.username, model.name);
boolean hasFork = fork != null;
- boolean canFork = user.canFork(model);
+ boolean canFork = user.canFork(model) && model.hasCommits;
if (hasFork || !canFork) {
// user not allowed to fork or fork already exists or repo forbids forking
diff --git a/src/main/java/com/gitblit/wicket/pages/RootPage.html b/src/main/java/com/gitblit/wicket/pages/RootPage.html index 11f7f38d..2ff305f2 100644 --- a/src/main/java/com/gitblit/wicket/pages/RootPage.html +++ b/src/main/java/com/gitblit/wicket/pages/RootPage.html @@ -51,16 +51,18 @@ <li class="dropdown">
<a data-toggle="dropdown" class="dropdown-toggle" style="text-decoration: none;" href="#"><span wicket:id="username"></span> <b class="caret"></b></a>
<ul class="dropdown-menu">
- <li style="color:#ccc;padding-left:15px;font-weight:bold;"><span wicket:id="displayName"></span></li>
- <li class="divider"></li>
- <li><a wicket:id="newRepository"><wicket:message key="gb.newRepository"></wicket:message></a></li>
- <li><a wicket:id="myProfile"><wicket:message key="gb.myProfile"></wicket:message></a></li>
- <li><a wicket:id="changePassword"><wicket:message key="gb.changePassword"></wicket:message></a></li>
- <li class="divider"></li>
+ <span wicket:id="standardMenu"></span>
+ <span wicket:id="adminMenu"></span>
+ <span wicket:id="extensionsMenu"></span>
<li><a wicket:id="logout"><wicket:message key="gb.logout"></wicket:message></a></li>
</ul>
</li>
</wicket:fragment>
+
+ <wicket:fragment wicket:id="submenuFragment">
+ <li style="color:#ccc;padding-left:15px;font-weight:bold;"><span wicket:id="submenuTitle"></span></li>
+ <li wicket:id="submenuItem"><span wicket:id="submenuLink"></span></li>
+ </wicket:fragment>
</wicket:extend>
</body>
diff --git a/src/main/java/com/gitblit/wicket/pages/RootPage.java b/src/main/java/com/gitblit/wicket/pages/RootPage.java index 5ccc3a4c..b1c3639d 100644 --- a/src/main/java/com/gitblit/wicket/pages/RootPage.java +++ b/src/main/java/com/gitblit/wicket/pages/RootPage.java @@ -1,599 +1,708 @@ -/*
- * Copyright 2011 gitblit.com.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.gitblit.wicket.pages;
-
-import java.text.MessageFormat;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Calendar;
-import java.util.Collections;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeSet;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.regex.Pattern;
-
-import org.apache.wicket.MarkupContainer;
-import org.apache.wicket.PageParameters;
-import org.apache.wicket.behavior.HeaderContributor;
-import org.apache.wicket.markup.html.IHeaderContributor;
-import org.apache.wicket.markup.html.IHeaderResponse;
-import org.apache.wicket.markup.html.basic.Label;
-import org.apache.wicket.markup.html.form.PasswordTextField;
-import org.apache.wicket.markup.html.form.TextField;
-import org.apache.wicket.markup.html.link.BookmarkablePageLink;
-import org.apache.wicket.markup.html.panel.Fragment;
-import org.apache.wicket.model.IModel;
-import org.apache.wicket.model.Model;
-import org.apache.wicket.protocol.http.WebResponse;
-
-import com.gitblit.Constants;
-import com.gitblit.Keys;
-import com.gitblit.models.RepositoryModel;
-import com.gitblit.models.TeamModel;
-import com.gitblit.models.UserModel;
-import com.gitblit.utils.ModelUtils;
-import com.gitblit.utils.StringUtils;
-import com.gitblit.wicket.GitBlitWebSession;
-import com.gitblit.wicket.PageRegistration;
-import com.gitblit.wicket.PageRegistration.DropDownMenuItem;
-import com.gitblit.wicket.PageRegistration.DropDownToggleItem;
-import com.gitblit.wicket.SessionlessForm;
-import com.gitblit.wicket.WicketUtils;
-import com.gitblit.wicket.panels.GravatarImage;
-import com.gitblit.wicket.panels.NavigationPanel;
-
-/**
- * Root page is a topbar, navigable page like Repositories, Users, or
- * Federation.
- *
- * @author James Moger
- *
- */
-public abstract class RootPage extends BasePage {
-
- boolean showAdmin;
-
- IModel<String> username = new Model<String>("");
- IModel<String> password = new Model<String>("");
- List<RepositoryModel> repositoryModels = new ArrayList<RepositoryModel>();
-
- public RootPage() {
- super();
- }
-
- public RootPage(PageParameters params) {
- super(params);
- }
-
- @Override
- protected void setupPage(String repositoryName, String pageName) {
-
- // CSS header overrides
- add(new HeaderContributor(new IHeaderContributor() {
- private static final long serialVersionUID = 1L;
-
- @Override
- public void renderHead(IHeaderResponse response) {
- StringBuilder buffer = new StringBuilder();
- buffer.append("<style type=\"text/css\">\n");
- buffer.append(".navbar-inner {\n");
- final String headerBackground = app().settings().getString(Keys.web.headerBackgroundColor, null);
- if (!StringUtils.isEmpty(headerBackground)) {
- buffer.append(MessageFormat.format("background-color: {0};\n", headerBackground));
- }
- final String headerBorder = app().settings().getString(Keys.web.headerBorderColor, null);
- if (!StringUtils.isEmpty(headerBorder)) {
- buffer.append(MessageFormat.format("border-bottom: 1px solid {0} !important;\n", headerBorder));
- }
- buffer.append("}\n");
- final String headerBorderFocus = app().settings().getString(Keys.web.headerBorderFocusColor, null);
- if (!StringUtils.isEmpty(headerBorderFocus)) {
- buffer.append(".navbar ul li:focus, .navbar .active {\n");
- buffer.append(MessageFormat.format("border-bottom: 4px solid {0};\n", headerBorderFocus));
- buffer.append("}\n");
- }
- final String headerForeground = app().settings().getString(Keys.web.headerForegroundColor, null);
- if (!StringUtils.isEmpty(headerForeground)) {
- buffer.append(".navbar ul.nav li a {\n");
- buffer.append(MessageFormat.format("color: {0};\n", headerForeground));
- buffer.append("}\n");
- buffer.append(".navbar ul.nav .active a {\n");
- buffer.append(MessageFormat.format("color: {0};\n", headerForeground));
- buffer.append("}\n");
- }
- final String headerHover = app().settings().getString(Keys.web.headerHoverColor, null);
- if (!StringUtils.isEmpty(headerHover)) {
- buffer.append(".navbar ul.nav li a:hover {\n");
- buffer.append(MessageFormat.format("color: {0} !important;\n", headerHover));
- buffer.append("}\n");
- }
- buffer.append("</style>\n");
- response.renderString(buffer.toString());
- }
- }));
-
- boolean authenticateView = app().settings().getBoolean(Keys.web.authenticateViewPages, false);
- boolean authenticateAdmin = app().settings().getBoolean(Keys.web.authenticateAdminPages, true);
- boolean allowAdmin = app().settings().getBoolean(Keys.web.allowAdministration, true);
-
- if (authenticateAdmin) {
- showAdmin = allowAdmin && GitBlitWebSession.get().canAdmin();
- // authentication requires state and session
- setStatelessHint(false);
- } else {
- showAdmin = allowAdmin;
- if (authenticateView) {
- // authentication requires state and session
- setStatelessHint(false);
- } else {
- // no authentication required, no state and no session required
- setStatelessHint(true);
- }
- }
-
- if (authenticateView || authenticateAdmin) {
- if (GitBlitWebSession.get().isLoggedIn()) {
- UserMenu userFragment = new UserMenu("userPanel", "userMenuFragment", RootPage.this);
- add(userFragment);
- } else {
- LoginForm loginForm = new LoginForm("userPanel", "loginFormFragment", RootPage.this);
- add(loginForm);
- }
- } else {
- add(new Label("userPanel").setVisible(false));
- }
-
- boolean showRegistrations = app().federation().canFederate()
- && app().settings().getBoolean(Keys.web.showFederationRegistrations, false);
-
- // navigation links
- List<PageRegistration> pages = new ArrayList<PageRegistration>();
- if (!authenticateView || (authenticateView && GitBlitWebSession.get().isLoggedIn())) {
- pages.add(new PageRegistration(GitBlitWebSession.get().isLoggedIn() ? "gb.myDashboard" : "gb.dashboard", MyDashboardPage.class,
- getRootPageParameters()));
- pages.add(new PageRegistration("gb.repositories", RepositoriesPage.class,
- getRootPageParameters()));
- pages.add(new PageRegistration("gb.activity", ActivityPage.class, getRootPageParameters()));
- if (app().settings().getBoolean(Keys.web.allowLuceneIndexing, true)) {
- pages.add(new PageRegistration("gb.search", LuceneSearchPage.class));
- }
- if (showAdmin) {
- pages.add(new PageRegistration("gb.users", UsersPage.class));
- }
- if (showAdmin || showRegistrations) {
- pages.add(new PageRegistration("gb.federation", FederationPage.class));
- }
-
- if (!authenticateView || (authenticateView && GitBlitWebSession.get().isLoggedIn())) {
- addDropDownMenus(pages);
- }
- }
-
- NavigationPanel navPanel = new NavigationPanel("navPanel", getRootNavPageClass(), pages);
- add(navPanel);
-
- // display an error message cached from a redirect
- String cachedMessage = GitBlitWebSession.get().clearErrorMessage();
- if (!StringUtils.isEmpty(cachedMessage)) {
- error(cachedMessage);
- } else if (showAdmin) {
- int pendingProposals = app().federation().getPendingFederationProposals().size();
- if (pendingProposals == 1) {
- info(getString("gb.OneProposalToReview"));
- } else if (pendingProposals > 1) {
- info(MessageFormat.format(getString("gb.nFederationProposalsToReview"),
- pendingProposals));
- }
- }
-
- super.setupPage(repositoryName, pageName);
- }
-
- protected Class<? extends BasePage> getRootNavPageClass() {
- return getClass();
- }
-
- private PageParameters getRootPageParameters() {
- if (reusePageParameters()) {
- PageParameters pp = getPageParameters();
- if (pp != null) {
- PageParameters params = new PageParameters(pp);
- // remove named project parameter
- params.remove("p");
-
- // remove named repository parameter
- params.remove("r");
-
- // remove named user parameter
- params.remove("user");
-
- // remove days back parameter if it is the default value
- if (params.containsKey("db")
- && params.getInt("db") == app().settings().getInteger(Keys.web.activityDuration, 7)) {
- params.remove("db");
- }
- return params;
- }
- }
- return null;
- }
-
- protected boolean reusePageParameters() {
- return false;
- }
-
- private void loginUser(UserModel user) {
- if (user != null) {
- // Set the user into the session
- GitBlitWebSession session = GitBlitWebSession.get();
- // issue 62: fix session fixation vulnerability
- session.replaceSession();
- session.setUser(user);
-
- // Set Cookie
- if (app().settings().getBoolean(Keys.web.allowCookieAuthentication, false)) {
- WebResponse response = (WebResponse) getRequestCycle().getResponse();
- app().authentication().setCookie(response.getHttpServletResponse(), user);
- }
-
- if (!session.continueRequest()) {
- PageParameters params = getPageParameters();
- if (params == null) {
- // redirect to this page
- setResponsePage(getClass());
- } else {
- // Strip username and password and redirect to this page
- params.remove("username");
- params.remove("password");
- setResponsePage(getClass(), params);
- }
- }
- }
- }
-
- protected List<RepositoryModel> getRepositoryModels() {
- if (repositoryModels.isEmpty()) {
- final UserModel user = GitBlitWebSession.get().getUser();
- List<RepositoryModel> repositories = app().repositories().getRepositoryModels(user);
- repositoryModels.addAll(repositories);
- Collections.sort(repositoryModels);
- }
- return repositoryModels;
- }
-
- protected void addDropDownMenus(List<PageRegistration> pages) {
-
- }
-
- protected List<DropDownMenuItem> getRepositoryFilterItems(PageParameters params) {
- final UserModel user = GitBlitWebSession.get().getUser();
- Set<DropDownMenuItem> filters = new LinkedHashSet<DropDownMenuItem>();
- List<RepositoryModel> repositories = getRepositoryModels();
-
- // accessible repositories by federation set
- Map<String, AtomicInteger> setMap = new HashMap<String, AtomicInteger>();
- for (RepositoryModel repository : repositories) {
- for (String set : repository.federationSets) {
- String key = set.toLowerCase();
- if (setMap.containsKey(key)) {
- setMap.get(key).incrementAndGet();
- } else {
- setMap.put(key, new AtomicInteger(1));
- }
- }
- }
- if (setMap.size() > 0) {
- List<String> sets = new ArrayList<String>(setMap.keySet());
- Collections.sort(sets);
- for (String set : sets) {
- filters.add(new DropDownToggleItem(MessageFormat.format("{0} ({1})", set,
- setMap.get(set).get()), "set", set, params));
- }
- // divider
- filters.add(new DropDownMenuItem());
- }
-
- // user's team memberships
- if (user != null && user.teams.size() > 0) {
- List<TeamModel> teams = new ArrayList<TeamModel>(user.teams);
- Collections.sort(teams);
- for (TeamModel team : teams) {
- filters.add(new DropDownToggleItem(MessageFormat.format("{0} ({1})", team.name,
- team.repositories.size()), "team", team.name, params));
- }
- // divider
- filters.add(new DropDownMenuItem());
- }
-
- // custom filters
- String customFilters = app().settings().getString(Keys.web.customFilters, null);
- if (!StringUtils.isEmpty(customFilters)) {
- boolean addedExpression = false;
- List<String> expressions = StringUtils.getStringsFromValue(customFilters, "!!!");
- for (String expression : expressions) {
- if (!StringUtils.isEmpty(expression)) {
- addedExpression = true;
- filters.add(new DropDownToggleItem(null, "x", expression, params));
- }
- }
- // if we added any custom expressions, add a divider
- if (addedExpression) {
- filters.add(new DropDownMenuItem());
- }
- }
- return new ArrayList<DropDownMenuItem>(filters);
- }
-
- protected List<DropDownMenuItem> getTimeFilterItems(PageParameters params) {
- // days back choices - additive parameters
- int daysBack = app().settings().getInteger(Keys.web.activityDuration, 7);
- int maxDaysBack = app().settings().getInteger(Keys.web.activityDurationMaximum, 30);
- if (daysBack < 1) {
- daysBack = 7;
- }
- if (daysBack > maxDaysBack) {
- daysBack = maxDaysBack;
- }
- PageParameters clonedParams;
- if (params == null) {
- clonedParams = new PageParameters();
- } else {
- clonedParams = new PageParameters(params);
- }
-
- if (!clonedParams.containsKey("db")) {
- clonedParams.put("db", daysBack);
- }
-
- List<DropDownMenuItem> items = new ArrayList<DropDownMenuItem>();
- Set<Integer> choicesSet = new TreeSet<Integer>(app().settings().getIntegers(Keys.web.activityDurationChoices));
- if (choicesSet.isEmpty()) {
- choicesSet.addAll(Arrays.asList(1, 3, 7, 14, 21, 28));
- }
- List<Integer> choices = new ArrayList<Integer>(choicesSet);
- Collections.sort(choices);
- String lastDaysPattern = getString("gb.lastNDays");
- for (Integer db : choices) {
- if (db == 1) {
- items.add(new DropDownMenuItem(getString("gb.time.today"), "db", db.toString(), clonedParams));
- } else {
- String txt = MessageFormat.format(lastDaysPattern, db);
- items.add(new DropDownMenuItem(txt, "db", db.toString(), clonedParams));
- }
- }
- items.add(new DropDownMenuItem());
- return items;
- }
-
- protected List<RepositoryModel> getRepositories(PageParameters params) {
- if (params == null) {
- return getRepositoryModels();
- }
-
- boolean hasParameter = false;
- String projectName = WicketUtils.getProjectName(params);
- String userName = WicketUtils.getUsername(params);
- if (StringUtils.isEmpty(projectName)) {
- if (!StringUtils.isEmpty(userName)) {
- projectName = ModelUtils.getPersonalPath(userName);
- }
- }
- String repositoryName = WicketUtils.getRepositoryName(params);
- String set = WicketUtils.getSet(params);
- String regex = WicketUtils.getRegEx(params);
- String team = WicketUtils.getTeam(params);
- int daysBack = params.getInt("db", 0);
- int maxDaysBack = app().settings().getInteger(Keys.web.activityDurationMaximum, 30);
-
- List<RepositoryModel> availableModels = getRepositoryModels();
- Set<RepositoryModel> models = new HashSet<RepositoryModel>();
-
- if (!StringUtils.isEmpty(repositoryName)) {
- // try named repository
- hasParameter = true;
- for (RepositoryModel model : availableModels) {
- if (model.name.equalsIgnoreCase(repositoryName)) {
- models.add(model);
- break;
- }
- }
- }
-
- if (!StringUtils.isEmpty(projectName)) {
- // try named project
- hasParameter = true;
- if (projectName.equalsIgnoreCase(app().settings().getString(Keys.web.repositoryRootGroupName, "main"))) {
- // root project/group
- for (RepositoryModel model : availableModels) {
- if (model.name.indexOf('/') == -1) {
- models.add(model);
- }
- }
- } else {
- // named project/group
- String group = projectName.toLowerCase() + "/";
- for (RepositoryModel model : availableModels) {
- if (model.name.toLowerCase().startsWith(group)) {
- models.add(model);
- }
- }
- }
- }
-
- if (!StringUtils.isEmpty(regex)) {
- // filter the repositories by the regex
- hasParameter = true;
- Pattern pattern = Pattern.compile(regex);
- for (RepositoryModel model : availableModels) {
- if (pattern.matcher(model.name).find()) {
- models.add(model);
- }
- }
- }
-
- if (!StringUtils.isEmpty(set)) {
- // filter the repositories by the specified sets
- hasParameter = true;
- List<String> sets = StringUtils.getStringsFromValue(set, ",");
- for (RepositoryModel model : availableModels) {
- for (String curr : sets) {
- if (model.federationSets.contains(curr)) {
- models.add(model);
- }
- }
- }
- }
-
- if (!StringUtils.isEmpty(team)) {
- // filter the repositories by the specified teams
- hasParameter = true;
- List<String> teams = StringUtils.getStringsFromValue(team, ",");
-
- // need TeamModels first
- List<TeamModel> teamModels = new ArrayList<TeamModel>();
- for (String name : teams) {
- TeamModel teamModel = app().users().getTeamModel(name);
- if (teamModel != null) {
- teamModels.add(teamModel);
- }
- }
-
- // brute-force our way through finding the matching models
- for (RepositoryModel repositoryModel : availableModels) {
- for (TeamModel teamModel : teamModels) {
- if (teamModel.hasRepositoryPermission(repositoryModel.name)) {
- models.add(repositoryModel);
- }
- }
- }
- }
-
- if (!hasParameter) {
- models.addAll(availableModels);
- }
-
- // time-filter the list
- if (daysBack > 0) {
- if (maxDaysBack > 0 && daysBack > maxDaysBack) {
- daysBack = maxDaysBack;
- }
- Calendar cal = Calendar.getInstance();
- cal.set(Calendar.HOUR_OF_DAY, 0);
- cal.set(Calendar.MINUTE, 0);
- cal.set(Calendar.SECOND, 0);
- cal.set(Calendar.MILLISECOND, 0);
- cal.add(Calendar.DATE, -1 * daysBack);
- Date threshold = cal.getTime();
- Set<RepositoryModel> timeFiltered = new HashSet<RepositoryModel>();
- for (RepositoryModel model : models) {
- if (model.lastChange.after(threshold)) {
- timeFiltered.add(model);
- }
- }
- models = timeFiltered;
- }
-
- List<RepositoryModel> list = new ArrayList<RepositoryModel>(models);
- Collections.sort(list);
- return list;
- }
-
- /**
- * Inline login form.
- */
- private class LoginForm extends Fragment {
- private static final long serialVersionUID = 1L;
-
- public LoginForm(String id, String markupId, MarkupContainer markupProvider) {
- super(id, markupId, markupProvider);
- setRenderBodyOnly(true);
-
- SessionlessForm<Void> loginForm = new SessionlessForm<Void>("loginForm", RootPage.this.getClass(), getPageParameters()) {
-
- private static final long serialVersionUID = 1L;
-
- @Override
- public void onSubmit() {
- String username = RootPage.this.username.getObject();
- char[] password = RootPage.this.password.getObject().toCharArray();
-
- UserModel user = app().authentication().authenticate(username, password);
- if (user == null) {
- error(getString("gb.invalidUsernameOrPassword"));
- } else if (user.username.equals(Constants.FEDERATION_USER)) {
- // disallow the federation user from logging in via the
- // web ui
- error(getString("gb.invalidUsernameOrPassword"));
- user = null;
- } else {
- loginUser(user);
- }
- }
- };
- TextField<String> unameField = new TextField<String>("username", username);
- WicketUtils.setInputPlaceholder(unameField, markupProvider.getString("gb.username"));
- loginForm.add(unameField);
- PasswordTextField pwField = new PasswordTextField("password", password);
- WicketUtils.setInputPlaceholder(pwField, markupProvider.getString("gb.password"));
- loginForm.add(pwField);
- add(loginForm);
- }
- }
-
- /**
- * Menu for the authenticated user.
- */
- class UserMenu extends Fragment {
-
- private static final long serialVersionUID = 1L;
-
- public UserMenu(String id, String markupId, MarkupContainer markupProvider) {
- super(id, markupId, markupProvider);
- setRenderBodyOnly(true);
-
- GitBlitWebSession session = GitBlitWebSession.get();
- UserModel user = session.getUser();
- boolean editCredentials = app().authentication().supportsCredentialChanges(user);
- boolean standardLogin = session.authenticationType.isStandard();
-
- if (app().settings().getBoolean(Keys.web.allowGravatar, true)) {
- add(new GravatarImage("username", user, "navbarGravatar", 20, false));
- } else {
- add(new Label("username", user.getDisplayName()));
- }
-
- add(new Label("displayName", user.getDisplayName()));
-
- add(new BookmarkablePageLink<Void>("newRepository",
- EditRepositoryPage.class).setVisible(user.canAdmin() || user.canCreate()));
-
- add(new BookmarkablePageLink<Void>("myProfile",
- UserPage.class, WicketUtils.newUsernameParameter(user.username)));
-
- add(new BookmarkablePageLink<Void>("changePassword",
- ChangePasswordPage.class).setVisible(editCredentials));
-
- add(new BookmarkablePageLink<Void>("logout",
- LogoutPage.class).setVisible(standardLogin));
- }
- }
-}
+/* + * Copyright 2011 gitblit.com. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.gitblit.wicket.pages; + +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.regex.Pattern; + +import org.apache.wicket.MarkupContainer; +import org.apache.wicket.PageParameters; +import org.apache.wicket.behavior.HeaderContributor; +import org.apache.wicket.markup.html.IHeaderContributor; +import org.apache.wicket.markup.html.IHeaderResponse; +import org.apache.wicket.markup.html.basic.Label; +import org.apache.wicket.markup.html.form.PasswordTextField; +import org.apache.wicket.markup.html.form.TextField; +import org.apache.wicket.markup.html.link.BookmarkablePageLink; +import org.apache.wicket.markup.html.panel.Fragment; +import org.apache.wicket.markup.repeater.Item; +import org.apache.wicket.markup.repeater.data.DataView; +import org.apache.wicket.markup.repeater.data.ListDataProvider; +import org.apache.wicket.model.IModel; +import org.apache.wicket.model.Model; +import org.apache.wicket.protocol.http.WebResponse; + +import com.gitblit.Constants; +import com.gitblit.Keys; +import com.gitblit.extensions.NavLinkExtension; +import com.gitblit.extensions.UserMenuExtension; +import com.gitblit.models.Menu.ExternalLinkMenuItem; +import com.gitblit.models.Menu.MenuDivider; +import com.gitblit.models.Menu.MenuItem; +import com.gitblit.models.Menu.PageLinkMenuItem; +import com.gitblit.models.Menu.ParameterMenuItem; +import com.gitblit.models.Menu.ToggleMenuItem; +import com.gitblit.models.NavLink; +import com.gitblit.models.NavLink.PageNavLink; +import com.gitblit.models.RepositoryModel; +import com.gitblit.models.TeamModel; +import com.gitblit.models.UserModel; +import com.gitblit.utils.ModelUtils; +import com.gitblit.utils.StringUtils; +import com.gitblit.wicket.GitBlitWebSession; +import com.gitblit.wicket.SessionlessForm; +import com.gitblit.wicket.WicketUtils; +import com.gitblit.wicket.panels.GravatarImage; +import com.gitblit.wicket.panels.LinkPanel; +import com.gitblit.wicket.panels.NavigationPanel; + +/** + * Root page is a topbar, navigable page like Repositories, Users, or + * Federation. + * + * @author James Moger + * + */ +public abstract class RootPage extends BasePage { + + boolean showAdmin; + + IModel<String> username = new Model<String>(""); + IModel<String> password = new Model<String>(""); + List<RepositoryModel> repositoryModels = new ArrayList<RepositoryModel>(); + + public RootPage() { + super(); + } + + public RootPage(PageParameters params) { + super(params); + } + + @Override + protected void setupPage(String repositoryName, String pageName) { + + // CSS header overrides + add(new HeaderContributor(new IHeaderContributor() { + private static final long serialVersionUID = 1L; + + @Override + public void renderHead(IHeaderResponse response) { + StringBuilder buffer = new StringBuilder(); + buffer.append("<style type=\"text/css\">\n"); + buffer.append(".navbar-inner {\n"); + final String headerBackground = app().settings().getString(Keys.web.headerBackgroundColor, null); + if (!StringUtils.isEmpty(headerBackground)) { + buffer.append(MessageFormat.format("background-color: {0};\n", headerBackground)); + } + final String headerBorder = app().settings().getString(Keys.web.headerBorderColor, null); + if (!StringUtils.isEmpty(headerBorder)) { + buffer.append(MessageFormat.format("border-bottom: 1px solid {0} !important;\n", headerBorder)); + } + buffer.append("}\n"); + final String headerBorderFocus = app().settings().getString(Keys.web.headerBorderFocusColor, null); + if (!StringUtils.isEmpty(headerBorderFocus)) { + buffer.append(".navbar ul li:focus, .navbar .active {\n"); + buffer.append(MessageFormat.format("border-bottom: 4px solid {0};\n", headerBorderFocus)); + buffer.append("}\n"); + } + final String headerForeground = app().settings().getString(Keys.web.headerForegroundColor, null); + if (!StringUtils.isEmpty(headerForeground)) { + buffer.append(".navbar ul.nav li a {\n"); + buffer.append(MessageFormat.format("color: {0};\n", headerForeground)); + buffer.append("}\n"); + buffer.append(".navbar ul.nav .active a {\n"); + buffer.append(MessageFormat.format("color: {0};\n", headerForeground)); + buffer.append("}\n"); + } + final String headerHover = app().settings().getString(Keys.web.headerHoverColor, null); + if (!StringUtils.isEmpty(headerHover)) { + buffer.append(".navbar ul.nav li a:hover {\n"); + buffer.append(MessageFormat.format("color: {0} !important;\n", headerHover)); + buffer.append("}\n"); + } + buffer.append("</style>\n"); + response.renderString(buffer.toString()); + } + })); + + boolean authenticateView = app().settings().getBoolean(Keys.web.authenticateViewPages, false); + boolean authenticateAdmin = app().settings().getBoolean(Keys.web.authenticateAdminPages, true); + boolean allowAdmin = app().settings().getBoolean(Keys.web.allowAdministration, true); + boolean allowLucene = app().settings().getBoolean(Keys.web.allowLuceneIndexing, true); + boolean isLoggedIn = GitBlitWebSession.get().isLoggedIn(); + + if (authenticateAdmin) { + showAdmin = allowAdmin && GitBlitWebSession.get().canAdmin(); + // authentication requires state and session + setStatelessHint(false); + } else { + showAdmin = allowAdmin; + if (authenticateView) { + // authentication requires state and session + setStatelessHint(false); + } else { + // no authentication required, no state and no session required + setStatelessHint(true); + } + } + + if (authenticateView || authenticateAdmin) { + if (isLoggedIn) { + UserMenu userFragment = new UserMenu("userPanel", "userMenuFragment", RootPage.this); + add(userFragment); + } else { + LoginForm loginForm = new LoginForm("userPanel", "loginFormFragment", RootPage.this); + add(loginForm); + } + } else { + add(new Label("userPanel").setVisible(false)); + } + + // navigation links + List<NavLink> navLinks = new ArrayList<NavLink>(); + if (!authenticateView || (authenticateView && isLoggedIn)) { + navLinks.add(new PageNavLink(isLoggedIn ? "gb.myDashboard" : "gb.dashboard", MyDashboardPage.class, + getRootPageParameters())); + if (isLoggedIn && app().tickets().isReady()) { + navLinks.add(new PageNavLink("gb.myTickets", MyTicketsPage.class)); + } + navLinks.add(new PageNavLink("gb.repositories", RepositoriesPage.class, + getRootPageParameters())); + navLinks.add(new PageNavLink("gb.activity", ActivityPage.class, getRootPageParameters())); + if (allowLucene) { + navLinks.add(new PageNavLink("gb.search", LuceneSearchPage.class)); + } + + if (!authenticateView || (authenticateView && isLoggedIn)) { + addDropDownMenus(navLinks); + } + + UserModel user = UserModel.ANONYMOUS; + if (isLoggedIn) { + user = GitBlitWebSession.get().getUser(); + } + + // add nav link extensions + List<NavLinkExtension> extensions = app().plugins().getExtensions(NavLinkExtension.class); + for (NavLinkExtension ext : extensions) { + navLinks.addAll(ext.getNavLinks(user)); + } + } + + NavigationPanel navPanel = new NavigationPanel("navPanel", getRootNavPageClass(), navLinks); + add(navPanel); + + // display an error message cached from a redirect + String cachedMessage = GitBlitWebSession.get().clearErrorMessage(); + if (!StringUtils.isEmpty(cachedMessage)) { + error(cachedMessage); + } else if (showAdmin) { + int pendingProposals = app().federation().getPendingFederationProposals().size(); + if (pendingProposals == 1) { + info(getString("gb.OneProposalToReview")); + } else if (pendingProposals > 1) { + info(MessageFormat.format(getString("gb.nFederationProposalsToReview"), + pendingProposals)); + } + } + + super.setupPage(repositoryName, pageName); + } + + protected Class<? extends BasePage> getRootNavPageClass() { + return getClass(); + } + + private PageParameters getRootPageParameters() { + if (reusePageParameters()) { + PageParameters pp = getPageParameters(); + if (pp != null) { + PageParameters params = new PageParameters(pp); + // remove named project parameter + params.remove("p"); + + // remove named repository parameter + params.remove("r"); + + // remove named user parameter + params.remove("user"); + + // remove days back parameter if it is the default value + if (params.containsKey("db") + && params.getInt("db") == app().settings().getInteger(Keys.web.activityDuration, 7)) { + params.remove("db"); + } + return params; + } + } + return null; + } + + protected boolean reusePageParameters() { + return false; + } + + private void loginUser(UserModel user) { + if (user != null) { + // Set the user into the session + GitBlitWebSession session = GitBlitWebSession.get(); + // issue 62: fix session fixation vulnerability + session.replaceSession(); + session.setUser(user); + + // Set Cookie + if (app().settings().getBoolean(Keys.web.allowCookieAuthentication, false)) { + WebResponse response = (WebResponse) getRequestCycle().getResponse(); + app().authentication().setCookie(response.getHttpServletResponse(), user); + } + + if (!session.continueRequest()) { + PageParameters params = getPageParameters(); + if (params == null) { + // redirect to this page + setResponsePage(getClass()); + } else { + // Strip username and password and redirect to this page + params.remove("username"); + params.remove("password"); + setResponsePage(getClass(), params); + } + } + } + } + + protected List<RepositoryModel> getRepositoryModels() { + if (repositoryModels.isEmpty()) { + final UserModel user = GitBlitWebSession.get().getUser(); + List<RepositoryModel> repositories = app().repositories().getRepositoryModels(user); + repositoryModels.addAll(repositories); + Collections.sort(repositoryModels); + } + return repositoryModels; + } + + protected void addDropDownMenus(List<NavLink> navLinks) { + + } + + protected List<com.gitblit.models.Menu.MenuItem> getRepositoryFilterItems(PageParameters params) { + final UserModel user = GitBlitWebSession.get().getUser(); + Set<MenuItem> filters = new LinkedHashSet<MenuItem>(); + List<RepositoryModel> repositories = getRepositoryModels(); + + // accessible repositories by federation set + Map<String, AtomicInteger> setMap = new HashMap<String, AtomicInteger>(); + for (RepositoryModel repository : repositories) { + for (String set : repository.federationSets) { + String key = set.toLowerCase(); + if (setMap.containsKey(key)) { + setMap.get(key).incrementAndGet(); + } else { + setMap.put(key, new AtomicInteger(1)); + } + } + } + if (setMap.size() > 0) { + List<String> sets = new ArrayList<String>(setMap.keySet()); + Collections.sort(sets); + for (String set : sets) { + filters.add(new ToggleMenuItem(MessageFormat.format("{0} ({1})", set, + setMap.get(set).get()), "set", set, params)); + } + // divider + filters.add(new MenuDivider()); + } + + // user's team memberships + if (user != null && user.teams.size() > 0) { + List<TeamModel> teams = new ArrayList<TeamModel>(user.teams); + Collections.sort(teams); + for (TeamModel team : teams) { + filters.add(new ToggleMenuItem(MessageFormat.format("{0} ({1})", team.name, + team.repositories.size()), "team", team.name, params)); + } + // divider + filters.add(new MenuDivider()); + } + + // custom filters + String customFilters = app().settings().getString(Keys.web.customFilters, null); + if (!StringUtils.isEmpty(customFilters)) { + boolean addedExpression = false; + List<String> expressions = StringUtils.getStringsFromValue(customFilters, "!!!"); + for (String expression : expressions) { + if (!StringUtils.isEmpty(expression)) { + addedExpression = true; + filters.add(new ToggleMenuItem(null, "x", expression, params)); + } + } + // if we added any custom expressions, add a divider + if (addedExpression) { + filters.add(new MenuDivider()); + } + } + return new ArrayList<MenuItem>(filters); + } + + protected List<MenuItem> getTimeFilterItems(PageParameters params) { + // days back choices - additive parameters + int daysBack = app().settings().getInteger(Keys.web.activityDuration, 7); + int maxDaysBack = app().settings().getInteger(Keys.web.activityDurationMaximum, 30); + if (daysBack < 1) { + daysBack = 7; + } + if (daysBack > maxDaysBack) { + daysBack = maxDaysBack; + } + PageParameters clonedParams; + if (params == null) { + clonedParams = new PageParameters(); + } else { + clonedParams = new PageParameters(params); + } + + if (!clonedParams.containsKey("db")) { + clonedParams.put("db", daysBack); + } + + List<MenuItem> items = new ArrayList<MenuItem>(); + Set<Integer> choicesSet = new TreeSet<Integer>(app().settings().getIntegers(Keys.web.activityDurationChoices)); + if (choicesSet.isEmpty()) { + choicesSet.addAll(Arrays.asList(1, 3, 7, 14, 21, 28)); + } + List<Integer> choices = new ArrayList<Integer>(choicesSet); + Collections.sort(choices); + String lastDaysPattern = getString("gb.lastNDays"); + for (Integer db : choices) { + if (db == 1) { + items.add(new ParameterMenuItem(getString("gb.time.today"), "db", db.toString(), clonedParams)); + } else { + String txt = MessageFormat.format(lastDaysPattern, db); + items.add(new ParameterMenuItem(txt, "db", db.toString(), clonedParams)); + } + } + items.add(new MenuDivider()); + return items; + } + + protected List<RepositoryModel> getRepositories(PageParameters params) { + if (params == null) { + return getRepositoryModels(); + } + + boolean hasParameter = false; + String projectName = WicketUtils.getProjectName(params); + String userName = WicketUtils.getUsername(params); + if (StringUtils.isEmpty(projectName)) { + if (!StringUtils.isEmpty(userName)) { + projectName = ModelUtils.getPersonalPath(userName); + } + } + String repositoryName = WicketUtils.getRepositoryName(params); + String set = WicketUtils.getSet(params); + String regex = WicketUtils.getRegEx(params); + String team = WicketUtils.getTeam(params); + int daysBack = params.getInt("db", 0); + int maxDaysBack = app().settings().getInteger(Keys.web.activityDurationMaximum, 30); + + List<RepositoryModel> availableModels = getRepositoryModels(); + Set<RepositoryModel> models = new HashSet<RepositoryModel>(); + + if (!StringUtils.isEmpty(repositoryName)) { + // try named repository + hasParameter = true; + for (RepositoryModel model : availableModels) { + if (model.name.equalsIgnoreCase(repositoryName)) { + models.add(model); + break; + } + } + } + + if (!StringUtils.isEmpty(projectName)) { + // try named project + hasParameter = true; + if (projectName.equalsIgnoreCase(app().settings().getString(Keys.web.repositoryRootGroupName, "main"))) { + // root project/group + for (RepositoryModel model : availableModels) { + if (model.name.indexOf('/') == -1) { + models.add(model); + } + } + } else { + // named project/group + String group = projectName.toLowerCase() + "/"; + for (RepositoryModel model : availableModels) { + if (model.name.toLowerCase().startsWith(group)) { + models.add(model); + } + } + } + } + + if (!StringUtils.isEmpty(regex)) { + // filter the repositories by the regex + hasParameter = true; + Pattern pattern = Pattern.compile(regex); + for (RepositoryModel model : availableModels) { + if (pattern.matcher(model.name).find()) { + models.add(model); + } + } + } + + if (!StringUtils.isEmpty(set)) { + // filter the repositories by the specified sets + hasParameter = true; + List<String> sets = StringUtils.getStringsFromValue(set, ","); + for (RepositoryModel model : availableModels) { + for (String curr : sets) { + if (model.federationSets.contains(curr)) { + models.add(model); + } + } + } + } + + if (!StringUtils.isEmpty(team)) { + // filter the repositories by the specified teams + hasParameter = true; + List<String> teams = StringUtils.getStringsFromValue(team, ","); + + // need TeamModels first + List<TeamModel> teamModels = new ArrayList<TeamModel>(); + for (String name : teams) { + TeamModel teamModel = app().users().getTeamModel(name); + if (teamModel != null) { + teamModels.add(teamModel); + } + } + + // brute-force our way through finding the matching models + for (RepositoryModel repositoryModel : availableModels) { + for (TeamModel teamModel : teamModels) { + if (teamModel.hasRepositoryPermission(repositoryModel.name)) { + models.add(repositoryModel); + } + } + } + } + + if (!hasParameter) { + models.addAll(availableModels); + } + + // time-filter the list + if (daysBack > 0) { + if (maxDaysBack > 0 && daysBack > maxDaysBack) { + daysBack = maxDaysBack; + } + Calendar cal = Calendar.getInstance(); + cal.set(Calendar.HOUR_OF_DAY, 0); + cal.set(Calendar.MINUTE, 0); + cal.set(Calendar.SECOND, 0); + cal.set(Calendar.MILLISECOND, 0); + cal.add(Calendar.DATE, -1 * daysBack); + Date threshold = cal.getTime(); + Set<RepositoryModel> timeFiltered = new HashSet<RepositoryModel>(); + for (RepositoryModel model : models) { + if (model.lastChange.after(threshold)) { + timeFiltered.add(model); + } + } + models = timeFiltered; + } + + List<RepositoryModel> list = new ArrayList<RepositoryModel>(models); + Collections.sort(list); + return list; + } + + /** + * Inline login form. + */ + private class LoginForm extends Fragment { + private static final long serialVersionUID = 1L; + + public LoginForm(String id, String markupId, MarkupContainer markupProvider) { + super(id, markupId, markupProvider); + setRenderBodyOnly(true); + + SessionlessForm<Void> loginForm = new SessionlessForm<Void>("loginForm", RootPage.this.getClass(), getPageParameters()) { + + private static final long serialVersionUID = 1L; + + @Override + public void onSubmit() { + String username = RootPage.this.username.getObject(); + char[] password = RootPage.this.password.getObject().toCharArray(); + + UserModel user = app().authentication().authenticate(username, password); + if (user == null) { + error(getString("gb.invalidUsernameOrPassword")); + } else if (user.username.equals(Constants.FEDERATION_USER)) { + // disallow the federation user from logging in via the + // web ui + error(getString("gb.invalidUsernameOrPassword")); + user = null; + } else { + loginUser(user); + } + } + }; + TextField<String> unameField = new TextField<String>("username", username); + WicketUtils.setInputPlaceholder(unameField, markupProvider.getString("gb.username")); + loginForm.add(unameField); + PasswordTextField pwField = new PasswordTextField("password", password); + WicketUtils.setInputPlaceholder(pwField, markupProvider.getString("gb.password")); + loginForm.add(pwField); + add(loginForm); + } + } + + /** + * Menu for the authenticated user. + */ + class UserMenu extends Fragment { + + private static final long serialVersionUID = 1L; + + public UserMenu(String id, String markupId, MarkupContainer markupProvider) { + super(id, markupId, markupProvider); + setRenderBodyOnly(true); + } + + @Override + protected void onInitialize() { + super.onInitialize(); + + GitBlitWebSession session = GitBlitWebSession.get(); + UserModel user = session.getUser(); + boolean editCredentials = app().authentication().supportsCredentialChanges(user); + boolean standardLogin = session.authenticationType.isStandard(); + + if (app().settings().getBoolean(Keys.web.allowGravatar, true)) { + add(new GravatarImage("username", user, "navbarGravatar", 20, false)); + } else { + add(new Label("username", user.getDisplayName())); + } + + List<MenuItem> standardItems = new ArrayList<MenuItem>(); + standardItems.add(new MenuDivider()); + if (user.canAdmin() || user.canCreate()) { + standardItems.add(new PageLinkMenuItem("gb.newRepository", app().getNewRepositoryPage())); + } + standardItems.add(new PageLinkMenuItem("gb.myProfile", UserPage.class, + WicketUtils.newUsernameParameter(user.username))); + if (editCredentials) { + standardItems.add(new PageLinkMenuItem("gb.changePassword", ChangePasswordPage.class)); + } + standardItems.add(new MenuDivider()); + add(newSubmenu("standardMenu", user.getDisplayName(), standardItems)); + + if (showAdmin) { + // admin menu + List<MenuItem> adminItems = new ArrayList<MenuItem>(); + adminItems.add(new MenuDivider()); + adminItems.add(new PageLinkMenuItem("gb.users", UsersPage.class)); + adminItems.add(new PageLinkMenuItem("gb.teams", TeamsPage.class)); + + boolean showRegistrations = app().federation().canFederate() + && app().settings().getBoolean(Keys.web.showFederationRegistrations, false); + if (showRegistrations) { + adminItems.add(new PageLinkMenuItem("gb.federation", FederationPage.class)); + } + adminItems.add(new MenuDivider()); + + add(newSubmenu("adminMenu", getString("gb.administration"), adminItems)); + } else { + add(new Label("adminMenu").setVisible(false)); + } + + // plugin extension items + List<MenuItem> extensionItems = new ArrayList<MenuItem>(); + List<UserMenuExtension> extensions = app().plugins().getExtensions(UserMenuExtension.class); + for (UserMenuExtension ext : extensions) { + List<MenuItem> items = ext.getMenuItems(user); + extensionItems.addAll(items); + } + + if (extensionItems.isEmpty()) { + // no extension items + add(new Label("extensionsMenu").setVisible(false)); + } else { + // found extension items + extensionItems.add(0, new MenuDivider()); + add(newSubmenu("extensionsMenu", getString("gb.extensions"), extensionItems)); + extensionItems.add(new MenuDivider()); + } + + add(new BookmarkablePageLink<Void>("logout", + LogoutPage.class).setVisible(standardLogin)); + } + + /** + * Creates a submenu. This is not actually submenu because we're using + * an older Twitter Bootstrap which is pre-submenu. + * + * @param wicketId + * @param submenuTitle + * @param menuItems + * @return a submenu fragment + */ + private Fragment newSubmenu(String wicketId, String submenuTitle, List<MenuItem> menuItems) { + Fragment submenu = new Fragment(wicketId, "submenuFragment", this); + submenu.add(new Label("submenuTitle", submenuTitle).setRenderBodyOnly(true)); + ListDataProvider<MenuItem> menuItemsDp = new ListDataProvider<MenuItem>(menuItems); + DataView<MenuItem> submenuItems = new DataView<MenuItem>("submenuItem", menuItemsDp) { + private static final long serialVersionUID = 1L; + + @Override + public void populateItem(final Item<MenuItem> menuItem) { + final MenuItem item = menuItem.getModelObject(); + String name = item.toString(); + try { + // try to lookup translation + name = getString(name); + } catch (Exception e) { + } + if (item instanceof PageLinkMenuItem) { + // link to another Wicket page + PageLinkMenuItem pageLink = (PageLinkMenuItem) item; + menuItem.add(new LinkPanel("submenuLink", null, null, name, pageLink.getPageClass(), + pageLink.getPageParameters(), false).setRenderBodyOnly(true)); + } else if (item instanceof ExternalLinkMenuItem) { + // link to a specified href + ExternalLinkMenuItem extLink = (ExternalLinkMenuItem) item; + menuItem.add(new LinkPanel("submenuLink", null, name, extLink.getHref(), + extLink.openInNewWindow()).setRenderBodyOnly(true)); + } else if (item instanceof MenuDivider) { + // divider + menuItem.add(new Label("submenuLink").setRenderBodyOnly(true)); + WicketUtils.setCssClass(menuItem, "divider"); + } + } + }; + submenu.add(submenuItems); + submenu.setRenderBodyOnly(true); + return submenu; + } + } +} diff --git a/src/main/java/com/gitblit/wicket/pages/TeamsPage.html b/src/main/java/com/gitblit/wicket/pages/TeamsPage.html new file mode 100644 index 00000000..981fe5b1 --- /dev/null +++ b/src/main/java/com/gitblit/wicket/pages/TeamsPage.html @@ -0,0 +1,13 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+<body>
+<wicket:extend>
+<div class="container">
+ <div wicket:id="teamsPanel">[teams panel]</div>
+</div>
+</wicket:extend>
+</body>
+</html>
\ No newline at end of file diff --git a/src/main/java/com/gitblit/wicket/pages/TeamsPage.java b/src/main/java/com/gitblit/wicket/pages/TeamsPage.java new file mode 100644 index 00000000..e0e7bf47 --- /dev/null +++ b/src/main/java/com/gitblit/wicket/pages/TeamsPage.java @@ -0,0 +1,30 @@ +/*
+ * Copyright 2011 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.wicket.pages;
+
+import com.gitblit.wicket.RequiresAdminRole;
+import com.gitblit.wicket.panels.TeamsPanel;
+
+@RequiresAdminRole
+public class TeamsPage extends RootPage {
+
+ public TeamsPage() {
+ super();
+ setupPage("", "");
+
+ add(new TeamsPanel("teamsPanel", showAdmin).setVisible(showAdmin));
+ }
+}
diff --git a/src/main/java/com/gitblit/wicket/pages/TicketBasePage.java b/src/main/java/com/gitblit/wicket/pages/TicketBasePage.java deleted file mode 100644 index 60fa6381..00000000 --- a/src/main/java/com/gitblit/wicket/pages/TicketBasePage.java +++ /dev/null @@ -1,126 +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.wicket.pages;
-
-import org.apache.wicket.PageParameters;
-import org.apache.wicket.markup.html.basic.Label;
-
-import com.gitblit.models.TicketModel;
-import com.gitblit.models.TicketModel.Status;
-import com.gitblit.models.TicketModel.Type;
-import com.gitblit.wicket.WicketUtils;
-
-public abstract class TicketBasePage extends RepositoryPage {
-
- public TicketBasePage(PageParameters params) {
- super(params);
- }
-
- protected Label getStateIcon(String wicketId, TicketModel ticket) {
- return getStateIcon(wicketId, ticket.type, ticket.status);
- }
-
- protected Label getStateIcon(String wicketId, Type type, Status state) {
- Label label = new Label(wicketId);
- if (type == null) {
- type = Type.defaultType;
- }
- switch (type) {
- case Proposal:
- WicketUtils.setCssClass(label, "fa fa-code-fork");
- break;
- case Bug:
- WicketUtils.setCssClass(label, "fa fa-bug");
- break;
- case Enhancement:
- WicketUtils.setCssClass(label, "fa fa-magic");
- break;
- case Question:
- WicketUtils.setCssClass(label, "fa fa-question");
- break;
- default:
- // standard ticket
- WicketUtils.setCssClass(label, "fa fa-ticket");
- }
- WicketUtils.setHtmlTooltip(label, getTypeState(type, state));
- return label;
- }
-
- protected String getTypeState(Type type, Status state) {
- return state.toString() + " " + type.toString();
- }
-
- protected String getLozengeClass(Status status, boolean subtle) {
- if (status == null) {
- status = Status.New;
- }
- String css = "";
- switch (status) {
- case Declined:
- case Duplicate:
- case Invalid:
- case Wontfix:
- case Abandoned:
- css = "aui-lozenge-error";
- break;
- case Fixed:
- case Merged:
- case Resolved:
- css = "aui-lozenge-success";
- break;
- case New:
- css = "aui-lozenge-complete";
- break;
- case On_Hold:
- css = "aui-lozenge-current";
- break;
- default:
- css = "";
- break;
- }
-
- return "aui-lozenge" + (subtle ? " aui-lozenge-subtle": "") + (css.isEmpty() ? "" : " ") + css;
- }
-
- protected String getStatusClass(Status status) {
- String css = "";
- switch (status) {
- case Declined:
- case Duplicate:
- case Invalid:
- case Wontfix:
- case Abandoned:
- css = "resolution-error";
- break;
- case Fixed:
- case Merged:
- case Resolved:
- css = "resolution-success";
- break;
- case New:
- css = "resolution-complete";
- break;
- case On_Hold:
- css = "resolution-current";
- break;
- default:
- css = "";
- break;
- }
-
- return "resolution" + (css.isEmpty() ? "" : " ") + css;
- }
-}
diff --git a/src/main/java/com/gitblit/wicket/pages/TicketPage.java b/src/main/java/com/gitblit/wicket/pages/TicketPage.java index e7b47498..f5f63d23 100644 --- a/src/main/java/com/gitblit/wicket/pages/TicketPage.java +++ b/src/main/java/com/gitblit/wicket/pages/TicketPage.java @@ -87,6 +87,7 @@ import com.gitblit.utils.MarkdownUtils; import com.gitblit.utils.StringUtils;
import com.gitblit.utils.TimeUtils;
import com.gitblit.wicket.GitBlitWebSession;
+import com.gitblit.wicket.TicketsUI;
import com.gitblit.wicket.WicketUtils;
import com.gitblit.wicket.panels.BasePanel.JavascriptTextPrompt;
import com.gitblit.wicket.panels.CommentPanel;
@@ -103,7 +104,7 @@ import com.gitblit.wicket.panels.SimpleAjaxLink; * @author James Moger
*
*/
-public class TicketPage extends TicketBasePage {
+public class TicketPage extends RepositoryPage {
static final String NIL = "<nil>";
@@ -155,7 +156,7 @@ public class TicketPage extends TicketBasePage { String href = urlFor(TicketsPage.class, params).toString();
add(new ExternalLink("ticketNumber", href, "#" + ticket.number));
Label headerStatus = new Label("headerStatus", ticket.status.toString());
- WicketUtils.setCssClass(headerStatus, getLozengeClass(ticket.status, false));
+ WicketUtils.setCssClass(headerStatus, TicketsUI.getLozengeClass(ticket.status, false));
add(headerStatus);
add(new Label("ticketTitle", ticket.title));
if (currentPatchset == null) {
@@ -249,8 +250,12 @@ public class TicketPage extends TicketBasePage { } else {
// link to milestone query
TicketMilestone milestone = app().tickets().getMilestone(repository, ticket.milestone);
- PageParameters milestoneParameters = new PageParameters();
- milestoneParameters.put("r", repositoryName);
+ PageParameters milestoneParameters;
+ if (milestone.isOpen()) {
+ milestoneParameters = WicketUtils.newOpenTicketsParameter(repositoryName);
+ } else {
+ milestoneParameters = WicketUtils.newRepositoryParameter(repositoryName);
+ }
milestoneParameters.put(Lucene.milestone.name(), ticket.milestone);
int progress = 0;
int open = 0;
@@ -278,7 +283,8 @@ public class TicketPage extends TicketBasePage { if (StringUtils.isEmpty(ticket.body)) {
desc = getString("gb.noDescriptionGiven");
} else {
- desc = MarkdownUtils.transformGFM(app().settings(), ticket.body, ticket.repository);
+ String bugtraq = bugtraqProcessor().processText(getRepository(), repositoryName, ticket.body);
+ desc = MarkdownUtils.transformGFM(app().settings(), bugtraq, ticket.repository);
}
add(new Label("ticketDescription", desc).setEscapeModelStrings(false));
@@ -318,10 +324,10 @@ public class TicketPage extends TicketBasePage { * LARGE STATUS INDICATOR WITH ICON (DISCUSSION TAB->SIDE BAR)
*/
Fragment ticketStatus = new Fragment("ticketStatus", "ticketStatusFragment", this);
- Label ticketIcon = getStateIcon("ticketIcon", ticket);
+ Label ticketIcon = TicketsUI.getStateIcon("ticketIcon", ticket);
ticketStatus.add(ticketIcon);
ticketStatus.add(new Label("ticketStatus", ticket.status.toString()));
- WicketUtils.setCssClass(ticketStatus, getLozengeClass(ticket.status, false));
+ WicketUtils.setCssClass(ticketStatus, TicketsUI.getLozengeClass(ticket.status, false));
add(ticketStatus);
@@ -371,7 +377,7 @@ public class TicketPage extends TicketBasePage { setResponsePage(TicketsPage.class, getPageParameters());
}
};
- String css = getStatusClass(item.getModel().getObject());
+ String css = TicketsUI.getStatusClass(item.getModel().getObject());
WicketUtils.setCssClass(link, css);
item.add(link);
}
@@ -513,7 +519,7 @@ public class TicketPage extends TicketBasePage { add(new Label("ticketTopic").setVisible(false));
} else {
// process the topic using the bugtraq config to link things
- String topic = bugtraqProcessor().processPlainCommitMessage(getRepository(), repositoryName, ticket.topic);
+ String topic = bugtraqProcessor().processText(getRepository(), repositoryName, ticket.topic);
add(new Label("ticketTopic", topic).setEscapeModelStrings(false));
}
@@ -673,7 +679,7 @@ public class TicketPage extends TicketBasePage { */
Fragment frag = new Fragment("entry", "statusFragment", this);
Label status = new Label("statusChange", entry.getStatus().toString());
- String css = getLozengeClass(entry.getStatus(), false);
+ String css = TicketsUI.getLozengeClass(entry.getStatus(), false);
WicketUtils.setCssClass(status, css);
for (IBehavior b : status.getBehaviors()) {
if (b instanceof SimpleAttributeModifier) {
@@ -692,7 +698,8 @@ public class TicketPage extends TicketBasePage { /*
* COMMENT
*/
- String comment = MarkdownUtils.transformGFM(app().settings(), entry.comment.text, repositoryName);
+ String bugtraq = bugtraqProcessor().processText(getRepository(), repositoryName, entry.comment.text);
+ String comment = MarkdownUtils.transformGFM(app().settings(), bugtraq, repositoryName);
Fragment frag = new Fragment("entry", "commentFragment", this);
Label commentIcon = new Label("commentIcon");
if (entry.comment.src == CommentSource.Email) {
@@ -944,7 +951,7 @@ public class TicketPage extends TicketBasePage { case status:
// special handling for status
Status status = event.getStatus();
- String css = getLozengeClass(status, true);
+ String css = TicketsUI.getLozengeClass(status, true);
value = String.format("<span class=\"%1$s\">%2$s</span>", css, status.toString());
break;
default:
@@ -1533,14 +1540,14 @@ public class TicketPage extends TicketBasePage { switch (type) {
case Rebase:
case Rebase_Squash:
- typeCss = getLozengeClass(Status.Declined, false);
+ typeCss = TicketsUI.getLozengeClass(Status.Declined, false);
break;
case Squash:
case Amend:
- typeCss = getLozengeClass(Status.On_Hold, false);
+ typeCss = TicketsUI.getLozengeClass(Status.On_Hold, false);
break;
case Proposal:
- typeCss = getLozengeClass(Status.New, false);
+ typeCss = TicketsUI.getLozengeClass(Status.New, false);
break;
case FastForward:
default:
diff --git a/src/main/java/com/gitblit/wicket/pages/TicketsPage.html b/src/main/java/com/gitblit/wicket/pages/TicketsPage.html index 7d13852b..b0f8968e 100644 --- a/src/main/java/com/gitblit/wicket/pages/TicketsPage.html +++ b/src/main/java/com/gitblit/wicket/pages/TicketsPage.html @@ -11,7 +11,7 @@ <div class="hidden-phone pull-right">
<form class="form-search" style="margin: 0px;" wicket:id="ticketSearchForm">
<div class="input-append">
- <input type="text" class="search-query" style="width: 170px;border-radius: 14px 0 0 14px; padding-left: 14px;" id="ticketSearchBox" wicket:id="ticketSearchBox" value=""/>
+ <input type="text" class="input-medium search-query" style="border-radius: 14px 0 0 14px; padding-left: 14px;" id="ticketSearchBox" wicket:id="ticketSearchBox" value=""/>
<button class="btn" style="border-radius: 0 14px 14px 0px;margin-left:-5px;" type="submit"><i class="icon-search"></i></button>
</div>
</form>
@@ -88,42 +88,7 @@ </div>
</div>
-
- <table class="table tickets">
- <tbody>
- <tr wicket:id="ticket">
- <td class="ticket-list-icon">
- <i wicket:id="state"></i>
- </td>
- <td>
- <span wicket:id="title">[title]</span> <span wicket:id="labels" style="font-weight: normal;color:white;"><span class="label" wicket:id="label"></span></span>
- <div class="ticket-list-details">
- <span style="padding-right: 10px;" class="hidden-phone">
- <wicket:message key="gb.createdBy"></wicket:message>
- <span style="padding: 0px 2px" wicket:id="createdBy">[createdBy]</span> <span class="date" wicket:id="createDate">[create date]</span>
- </span>
- <span wicket:id="indicators" style="white-space:nowrap;"><i wicket:id="icon"></i> <span style="padding-right:10px;" wicket:id="count"></span></span>
- </div>
- <div class="hidden-phone" wicket:id="updated"></div>
- </td>
- <td class="ticket-list-state">
- <span class="badge badge-info" wicket:id="votes"></span>
- </td>
- <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-state">
- <div wicket:id="status"></div>
- </td>
- <td class="indicators">
- <div>
- <b>#<span wicket:id="id">[id]</span></b>
- </div>
- <div wicket:id="responsible"></div>
- </td>
- </tr>
- </tbody>
- </table>
+ <div wicket:id="ticketList"></div>
<div class="btn-group pull-right">
<div class="pagination pagination-right pagination-small">
@@ -139,14 +104,51 @@ </div>
<div class="tab-pane" id="milestones">
<div class="row">
- <div class="span9" wicket:id="milestoneList" style="padding-bottom: 10px;">
- <h3><span wicket:id="milestoneName"></span> <small><span wicket:id="milestoneState"></span></small></h3>
- <i style="color:#888;"class="fa fa-calendar"></i> <span wicket:id="milestoneDue"></span>
+ <span class="span12" style="padding-bottom:10px;" wicket:id="newMilestone"></span>
+ </div>
+
+ <div class="row">
+ <div class="span12"><h2><wicket:message key="gb.openMilestones"></wicket:message></h2></div>
+ <div class="span12"><hr/></div>
+ </div>
+ <div class="row">
+ <div class="span4" wicket:id="openMilestonesList" style="padding-bottom: 20px;">
+ <div wicket:id="entryPanel"></div>
</div>
</div>
+
+ <div class="row">
+ <div class="span12"><h2><wicket:message key="gb.closedMilestones"></wicket:message></h2></div>
+ <div class="span12"><hr/></div>
+ </div>
+ <div class="row">
+ <div class="span3" wicket:id="closedMilestonesList" style="padding-bottom: 15px;">
+ <div wicket:id="entryPanel"></div>
+ </div>
+ </div>
+
</div>
</div>
+<wicket:fragment wicket:id="milestoneListFragment">
+ <h3><span wicket:id="milestoneName"></span> <small><span wicket:id="milestoneState"></span></small></h3>
+ <i style="color:#888;"class="fa fa-calendar"></i> <span wicket:id="milestoneDue"></span> <span wicket:id="editMilestone"></span>
+ <div wicket:id="milestonePanel"></div>
+</wicket:fragment>
+
+<wicket:fragment wicket:id="openMilestoneFragment">
+ <div style="clear:both;padding-bottom: 10px;">
+ <div style="margin-bottom: 5px;" class="progress progress-success">
+ <div class="bar" wicket:id="progress"></div>
+ </div>
+ <div class="milestoneOverview">
+ <span wicket:id="openTickets" />,
+ <span wicket:id="closedTickets" />,
+ <span wicket:id="totalTickets" />
+ </div>
+ </div>
+</wicket:fragment>
+
<wicket:fragment wicket:id="noMilestoneFragment">
<table style="width: 100%;padding-bottom: 5px;">
<tbody>
@@ -203,13 +205,6 @@ </ul>
</wicket:fragment>
-<wicket:fragment wicket:id="updatedFragment">
- <div class="ticket-list-details">
- <wicket:message key="gb.updatedBy"></wicket:message>
- <span style="padding: 0px 2px" wicket:id="updatedBy">[updatedBy]</span> <span class="date" wicket:id="updateDate">[update date]</span>
- </div>
-</wicket:fragment>
-
</wicket:extend>
</body>
</html>
\ No newline at end of file diff --git a/src/main/java/com/gitblit/wicket/pages/TicketsPage.java b/src/main/java/com/gitblit/wicket/pages/TicketsPage.java index ca509e28..658cddec 100644 --- a/src/main/java/com/gitblit/wicket/pages/TicketsPage.java +++ b/src/main/java/com/gitblit/wicket/pages/TicketsPage.java @@ -15,11 +15,11 @@ */
package com.gitblit.wicket.pages;
-import java.io.Serializable;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
@@ -28,20 +28,16 @@ import org.apache.wicket.Component; import org.apache.wicket.PageParameters;
import org.apache.wicket.behavior.SimpleAttributeModifier;
import org.apache.wicket.markup.html.basic.Label;
-import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.markup.html.link.BookmarkablePageLink;
import org.apache.wicket.markup.html.panel.Fragment;
import org.apache.wicket.markup.repeater.Item;
import org.apache.wicket.markup.repeater.data.DataView;
import org.apache.wicket.markup.repeater.data.ListDataProvider;
-import org.apache.wicket.model.IModel;
-import org.apache.wicket.model.Model;
-import org.apache.wicket.request.target.basic.RedirectRequestTarget;
-import com.gitblit.Constants;
import com.gitblit.Constants.AccessPermission;
import com.gitblit.Keys;
import com.gitblit.models.RegistrantAccessPermission;
+import com.gitblit.models.RepositoryModel;
import com.gitblit.models.TicketModel;
import com.gitblit.models.TicketModel.Status;
import com.gitblit.models.UserModel;
@@ -54,19 +50,18 @@ import com.gitblit.tickets.TicketResponsible; import com.gitblit.utils.ArrayUtils;
import com.gitblit.utils.StringUtils;
import com.gitblit.wicket.GitBlitWebSession;
-import com.gitblit.wicket.SessionlessForm;
+import com.gitblit.wicket.TicketsUI;
+import com.gitblit.wicket.TicketsUI.TicketQuery;
+import com.gitblit.wicket.TicketsUI.TicketSort;
import com.gitblit.wicket.WicketUtils;
-import com.gitblit.wicket.panels.GravatarImage;
import com.gitblit.wicket.panels.LinkPanel;
+import com.gitblit.wicket.panels.TicketListPanel;
+import com.gitblit.wicket.panels.TicketSearchForm;
-public class TicketsPage extends TicketBasePage {
+public class TicketsPage extends RepositoryPage {
final TicketResponsible any;
- public static final String [] openStatii = new String [] { Status.New.name().toLowerCase(), Status.Open.name().toLowerCase() };
-
- public static final String [] closedStatii = new String [] { "!" + Status.New.name().toLowerCase(), "!" + Status.Open.name().toLowerCase() };
-
public TicketsPage(PageParameters params) {
super(params);
@@ -100,11 +95,8 @@ public class TicketsPage extends TicketBasePage { final String sortBy = Lucene.fromString(params.getString("sort", Lucene.created.name())).name();
final boolean desc = !"asc".equals(params.getString("direction", "desc"));
-
// add search form
- TicketSearchForm searchForm = new TicketSearchForm("ticketSearchForm", repositoryName, searchParam);
- add(searchForm);
- searchForm.setTranslatedAttributes();
+ add(new TicketSearchForm("ticketSearchForm", repositoryName, searchParam, getClass(), params));
final String activeQuery;
if (!StringUtils.isEmpty(searchParam)) {
@@ -190,12 +182,12 @@ public class TicketsPage extends TicketBasePage { milestonePanel.add(new LinkPanel("openTickets", null,
MessageFormat.format(getString("gb.nOpenTickets"), currentMilestone.getOpenTickets()),
TicketsPage.class,
- queryParameters(null, currentMilestone.name, openStatii, null, sortBy, desc, 1)));
+ queryParameters(null, currentMilestone.name, TicketsUI.openStatii, null, sortBy, desc, 1)));
milestonePanel.add(new LinkPanel("closedTickets", null,
MessageFormat.format(getString("gb.nClosedTickets"), currentMilestone.getClosedTickets()),
TicketsPage.class,
- queryParameters(null, currentMilestone.name, closedStatii, null, sortBy, desc, 1)));
+ queryParameters(null, currentMilestone.name, TicketsUI.closedStatii, null, sortBy, desc, 1)));
milestonePanel.add(new Label("totalTickets", MessageFormat.format(getString("gb.nTotalTickets"), currentMilestone.getTotalTickets())));
add(milestonePanel);
@@ -285,7 +277,7 @@ public class TicketsPage extends TicketBasePage { queryParameters(
null,
milestoneParam,
- openStatii,
+ TicketsUI.openStatii,
null,
null,
true,
@@ -395,8 +387,8 @@ public class TicketsPage extends TicketBasePage { } else {
add(new Label("selectedStatii", StringUtils.flattenStrings(Arrays.asList(statiiParam), ",")));
}
- add(new BookmarkablePageLink<Void>("openTickets", TicketsPage.class, queryParameters(queryParam, milestoneParam, openStatii, assignedToParam, sortBy, desc, 1)));
- add(new BookmarkablePageLink<Void>("closedTickets", TicketsPage.class, queryParameters(queryParam, milestoneParam, closedStatii, assignedToParam, sortBy, desc, 1)));
+ add(new BookmarkablePageLink<Void>("openTickets", TicketsPage.class, queryParameters(queryParam, milestoneParam, TicketsUI.openStatii, assignedToParam, sortBy, desc, 1)));
+ add(new BookmarkablePageLink<Void>("closedTickets", TicketsPage.class, queryParameters(queryParam, milestoneParam, TicketsUI.closedStatii, assignedToParam, sortBy, desc, 1)));
add(new BookmarkablePageLink<Void>("allTickets", TicketsPage.class, queryParameters(queryParam, milestoneParam, null, assignedToParam, sortBy, desc, 1)));
// by status
@@ -410,7 +402,7 @@ public class TicketsPage extends TicketBasePage { public void populateItem(final Item<Status> item) {
final Status status = item.getModelObject();
PageParameters p = queryParameters(queryParam, milestoneParam, new String [] { status.name().toLowerCase() }, assignedToParam, sortBy, desc, 1);
- String css = getStatusClass(status);
+ String css = TicketsUI.getStatusClass(status);
item.add(new LinkPanel("statusLink", css, status.toString(), TicketsPage.class, p).setRenderBodyOnly(true));
}
};
@@ -489,195 +481,128 @@ public class TicketsPage extends TicketBasePage { // paging links
buildPager(queryParam, milestoneParam, statiiParam, assignedToParam, sortBy, desc, page, pageSize, results.size(), totalResults);
- ListDataProvider<QueryResult> resultsDataProvider = new ListDataProvider<QueryResult>(results);
- DataView<QueryResult> ticketsView = new DataView<QueryResult>("ticket", resultsDataProvider) {
- private static final long serialVersionUID = 1L;
-
- @Override
- public void populateItem(final Item<QueryResult> item) {
- final QueryResult ticket = item.getModelObject();
- item.add(getStateIcon("state", ticket.type, ticket.status));
- item.add(new Label("id", "" + ticket.number));
- UserModel creator = app().users().getUserModel(ticket.createdBy);
- if (creator != null) {
- item.add(new LinkPanel("createdBy", null, creator.getDisplayName(),
- UserPage.class, WicketUtils.newUsernameParameter(ticket.createdBy)));
- } else {
- item.add(new Label("createdBy", ticket.createdBy));
- }
- item.add(WicketUtils.createDateLabel("createDate", ticket.createdAt, GitBlitWebSession
- .get().getTimezone(), getTimeUtils(), false));
-
- if (ticket.updatedAt == null) {
- item.add(new Label("updated").setVisible(false));
- } else {
- Fragment updated = new Fragment("updated", "updatedFragment", this);
- UserModel updater = app().users().getUserModel(ticket.updatedBy);
- if (updater != null) {
- updated.add(new LinkPanel("updatedBy", null, updater.getDisplayName(),
- UserPage.class, WicketUtils.newUsernameParameter(ticket.updatedBy)));
- } else {
- updated.add(new Label("updatedBy", ticket.updatedBy));
- }
- updated.add(WicketUtils.createDateLabel("updateDate", ticket.updatedAt, GitBlitWebSession
- .get().getTimezone(), getTimeUtils(), false));
- item.add(updated);
- }
-
- item.add(new LinkPanel("title", "list subject", StringUtils.trimString(
- ticket.title, Constants.LEN_SHORTLOG), TicketsPage.class, newTicketParameter(ticket)));
-
- ListDataProvider<String> labelsProvider = new ListDataProvider<String>(ticket.getLabels());
- DataView<String> labelsView = new DataView<String>("labels", labelsProvider) {
- private static final long serialVersionUID = 1L;
-
- @Override
- public void populateItem(final Item<String> labelItem) {
- String content = bugtraqProcessor().processPlainCommitMessage(getRepository(), repositoryName, labelItem.getModelObject());
- Label label = new Label("label", content);
- label.setEscapeModelStrings(false);
- TicketLabel tLabel = app().tickets().getLabel(getRepositoryModel(), labelItem.getModelObject());
- String background = MessageFormat.format("background-color:{0};", tLabel.color);
- label.add(new SimpleAttributeModifier("style", background));
- labelItem.add(label);
- }
- };
- item.add(labelsView);
-
- if (StringUtils.isEmpty(ticket.responsible)) {
- item.add(new Label("responsible").setVisible(false));
- } else {
- UserModel responsible = app().users().getUserModel(ticket.responsible);
- if (responsible == null) {
- responsible = new UserModel(ticket.responsible);
- }
- GravatarImage avatar = new GravatarImage("responsible", responsible.getDisplayName(),
- responsible.emailAddress, null, 16, true);
- avatar.setTooltip(getString("gb.responsible") + ": " + responsible.getDisplayName());
- item.add(avatar);
- }
-
- // votes indicator
- Label v = new Label("votes", "" + ticket.votesCount);
- WicketUtils.setHtmlTooltip(v, getString("gb.votes"));
- item.add(v.setVisible(ticket.votesCount > 0));
-
- // watching indicator
- item.add(new Label("watching").setVisible(ticket.isWatching(GitBlitWebSession.get().getUsername())));
-
- // status indicator
- String css = getLozengeClass(ticket.status, true);
- Label l = new Label("status", ticket.status.toString());
- WicketUtils.setCssClass(l, css);
- item.add(l);
-
- // add the ticket indicators/icons
- List<Indicator> indicators = new ArrayList<Indicator>();
-
- // comments
- if (ticket.commentsCount > 0) {
- int count = ticket.commentsCount;
- String pattern = "gb.nComments";
- if (count == 1) {
- pattern = "gb.oneComment";
- }
- indicators.add(new Indicator("fa fa-comment", count, pattern));
- }
-
- // participants
- if (!ArrayUtils.isEmpty(ticket.participants)) {
- int count = ticket.participants.size();
- if (count > 1) {
- String pattern = "gb.nParticipants";
- indicators.add(new Indicator("fa fa-user", count, pattern));
- }
- }
-
- // attachments
- if (!ArrayUtils.isEmpty(ticket.attachments)) {
- int count = ticket.attachments.size();
- String pattern = "gb.nAttachments";
- if (count == 1) {
- pattern = "gb.oneAttachment";
- }
- indicators.add(new Indicator("fa fa-file", count, pattern));
- }
-
- // patchset revisions
- if (ticket.patchset != null) {
- int count = ticket.patchset.commits;
- String pattern = "gb.nCommits";
- if (count == 1) {
- pattern = "gb.oneCommit";
- }
- indicators.add(new Indicator("fa fa-code", count, pattern));
- }
-
- // milestone
- if (!StringUtils.isEmpty(ticket.milestone)) {
- indicators.add(new Indicator("fa fa-bullseye", ticket.milestone));
- }
+ add(new TicketListPanel("ticketList", results, false, false));
- ListDataProvider<Indicator> indicatorsDp = new ListDataProvider<Indicator>(indicators);
- DataView<Indicator> indicatorsView = new DataView<Indicator>("indicators", indicatorsDp) {
- private static final long serialVersionUID = 1L;
+ // new milestone link
+ RepositoryModel repositoryModel = getRepositoryModel();
+ final boolean acceptingUpdates = app().tickets().isAcceptingTicketUpdates(repositoryModel)
+ && user != null && user.canAdmin(getRepositoryModel());
+ if (acceptingUpdates) {
+ add(new LinkPanel("newMilestone", null, getString("gb.newMilestone"),
+ NewMilestonePage.class, WicketUtils.newRepositoryParameter(repositoryName)));
+ } else {
+ add(new Label("newMilestone").setVisible(false));
+ }
- @Override
- public void populateItem(final Item<Indicator> item) {
- Indicator indicator = item.getModelObject();
- String tooltip = indicator.getTooltip();
+ // milestones list
+ List<TicketMilestone> openMilestones = new ArrayList<TicketMilestone>();
+ List<TicketMilestone> closedMilestones = new ArrayList<TicketMilestone>();
+ for (TicketMilestone milestone : app().tickets().getMilestones(repositoryModel)) {
+ if (milestone.isOpen()) {
+ openMilestones.add(milestone);
+ } else {
+ closedMilestones.add(milestone);
+ }
+ }
+ Collections.sort(openMilestones, new Comparator<TicketMilestone>() {
+ @Override
+ public int compare(TicketMilestone o1, TicketMilestone o2) {
+ return o2.due.compareTo(o1.due);
+ }
+ });
- Label icon = new Label("icon");
- WicketUtils.setCssClass(icon, indicator.css);
- item.add(icon);
+ Collections.sort(closedMilestones, new Comparator<TicketMilestone>() {
+ @Override
+ public int compare(TicketMilestone o1, TicketMilestone o2) {
+ return o2.due.compareTo(o1.due);
+ }
+ });
- if (indicator.count > 0) {
- Label count = new Label("count", "" + indicator.count);
- item.add(count.setVisible(!StringUtils.isEmpty(tooltip)));
- } else {
- item.add(new Label("count").setVisible(false));
- }
+ DataView<TicketMilestone> openMilestonesList = milestoneList("openMilestonesList", openMilestones, acceptingUpdates);
+ add(openMilestonesList);
- WicketUtils.setHtmlTooltip(item, tooltip);
- }
- };
- item.add(indicatorsView);
- }
- };
- add(ticketsView);
+ DataView<TicketMilestone> closedMilestonesList = milestoneList("closedMilestonesList", closedMilestones, acceptingUpdates);
+ add(closedMilestonesList);
+ }
- List<TicketMilestone> allMilestones = app().tickets().getMilestones(getRepositoryModel());
- ListDataProvider<TicketMilestone> allMilestonesDp = new ListDataProvider<TicketMilestone>(allMilestones);
- DataView<TicketMilestone> milestonesList = new DataView<TicketMilestone>("milestoneList", allMilestonesDp) {
+ protected DataView<TicketMilestone> milestoneList(String wicketId, List<TicketMilestone> milestones, final boolean acceptingUpdates) {
+ ListDataProvider<TicketMilestone> milestonesDp = new ListDataProvider<TicketMilestone>(milestones);
+ DataView<TicketMilestone> milestonesList = new DataView<TicketMilestone>(wicketId, milestonesDp) {
private static final long serialVersionUID = 1L;
@Override
public void populateItem(final Item<TicketMilestone> item) {
+ Fragment entryPanel = new Fragment("entryPanel", "milestoneListFragment", this);
+ item.add(entryPanel);
+
final TicketMilestone tm = item.getModelObject();
- PageParameters params = queryParameters(null, tm.name, null, null, null, desc, 1);
- item.add(new LinkPanel("milestoneName", null, tm.name, TicketsPage.class, params).setRenderBodyOnly(true));
+ String [] states;
+ if (tm.isOpen()) {
+ states = TicketsUI.openStatii;
+ } else {
+ states = TicketsUI.closedStatii;
+ }
+ PageParameters params = queryParameters(null, tm.name, states, null, null, true, 1);
+ entryPanel.add(new LinkPanel("milestoneName", null, tm.name, TicketsPage.class, params).setRenderBodyOnly(true));
String css;
+ String status = tm.status.name();
switch (tm.status) {
case Open:
- css = "aui-lozenge aui-lozenge-subtle";
+ if (tm.isOverdue()) {
+ css = "aui-lozenge aui-lozenge-subtle aui-lozenge-error";
+ status = "overdue";
+ } else {
+ css = "aui-lozenge aui-lozenge-subtle";
+ }
break;
default:
css = "aui-lozenge";
break;
}
- Label stateLabel = new Label("milestoneState", tm.status.name());
+ Label stateLabel = new Label("milestoneState", status);
WicketUtils.setCssClass(stateLabel, css);
- item.add(stateLabel);
+ entryPanel.add(stateLabel);
if (tm.due == null) {
- item.add(new Label("milestoneDue", getString("gb.notSpecified")));
+ entryPanel.add(new Label("milestoneDue", getString("gb.notSpecified")));
} else {
- item.add(WicketUtils.createDatestampLabel("milestoneDue", tm.due, getTimeZone(), getTimeUtils()));
+ entryPanel.add(WicketUtils.createDatestampLabel("milestoneDue", tm.due, getTimeZone(), getTimeUtils()));
+ }
+ if (acceptingUpdates) {
+ entryPanel.add(new LinkPanel("editMilestone", null, getString("gb.edit"), EditMilestonePage.class,
+ WicketUtils.newObjectParameter(repositoryName, tm.name)));
+ } else {
+ entryPanel.add(new Label("editMilestone").setVisible(false));
+ }
+
+ if (tm.isOpen()) {
+ // re-load milestone with query results
+ TicketMilestone m = app().tickets().getMilestone(getRepositoryModel(), tm.name);
+
+ Fragment milestonePanel = new Fragment("milestonePanel", "openMilestoneFragment", this);
+ Label label = new Label("progress");
+ WicketUtils.setCssStyle(label, "width:" + m.getProgress() + "%;");
+ milestonePanel.add(label);
+
+ milestonePanel.add(new LinkPanel("openTickets", null,
+ MessageFormat.format(getString("gb.nOpenTickets"), m.getOpenTickets()),
+ TicketsPage.class,
+ queryParameters(null, tm.name, TicketsUI.openStatii, null, null, true, 1)));
+
+ milestonePanel.add(new LinkPanel("closedTickets", null,
+ MessageFormat.format(getString("gb.nClosedTickets"), m.getClosedTickets()),
+ TicketsPage.class,
+ queryParameters(null, tm.name, TicketsUI.closedStatii, null, null, true, 1)));
+
+ milestonePanel.add(new Label("totalTickets", MessageFormat.format(getString("gb.nTotalTickets"), m.getTotalTickets())));
+ entryPanel.add(milestonePanel);
+ } else {
+ entryPanel.add(new Label("milestonePanel").setVisible(false));
}
}
};
- add(milestonesList);
+ return milestonesList;
}
protected PageParameters queryParameters(
@@ -780,123 +705,4 @@ public class TicketsPage extends TicketBasePage { };
add(pagesView);
}
-
- private class Indicator implements Serializable {
-
- private static final long serialVersionUID = 1L;
-
- final String css;
- final int count;
- final String tooltip;
-
- Indicator(String css, String tooltip) {
- this.css = css;
- this.tooltip = tooltip;
- this.count = 0;
- }
-
- Indicator(String css, int count, String pattern) {
- this.css = css;
- this.count = count;
- this.tooltip = StringUtils.isEmpty(pattern) ? "" : MessageFormat.format(getString(pattern), count);
- }
-
- String getTooltip() {
- return tooltip;
- }
- }
-
- private class TicketQuery implements Serializable, Comparable<TicketQuery> {
-
- private static final long serialVersionUID = 1L;
-
- final String name;
- final String query;
- String color;
-
- TicketQuery(String name, String query) {
- this.name = name;
- this.query = query;
- }
-
- TicketQuery color(String value) {
- this.color = value;
- return this;
- }
-
- @Override
- public boolean equals(Object o) {
- if (o instanceof TicketQuery) {
- return ((TicketQuery) o).query.equals(query);
- }
- return false;
- }
-
- @Override
- public int hashCode() {
- return query.hashCode();
- }
-
- @Override
- public int compareTo(TicketQuery o) {
- return query.compareTo(o.query);
- }
- }
-
- private class TicketSort implements Serializable {
-
- private static final long serialVersionUID = 1L;
-
- final String name;
- final String sortBy;
- final boolean desc;
-
- TicketSort(String name, String sortBy, boolean desc) {
- this.name = name;
- this.sortBy = sortBy;
- this.desc = desc;
- }
- }
-
- private class TicketSearchForm extends SessionlessForm<Void> implements Serializable {
- private static final long serialVersionUID = 1L;
-
- private final String repositoryName;
-
- private final IModel<String> searchBoxModel;;
-
- public TicketSearchForm(String id, String repositoryName, String text) {
- super(id, TicketsPage.this.getClass(), TicketsPage.this.getPageParameters());
-
- this.repositoryName = repositoryName;
- this.searchBoxModel = new Model<String>(text == null ? "" : text);
-
- TextField<String> searchBox = new TextField<String>("ticketSearchBox", searchBoxModel);
- add(searchBox);
- }
-
- void setTranslatedAttributes() {
- WicketUtils.setHtmlTooltip(get("ticketSearchBox"),
- MessageFormat.format(getString("gb.searchTicketsTooltip"), repositoryName));
- WicketUtils.setInputPlaceholder(get("ticketSearchBox"), getString("gb.searchTickets"));
- }
-
- @Override
- public void onSubmit() {
- String searchString = searchBoxModel.getObject();
- if (StringUtils.isEmpty(searchString)) {
- // redirect to self to avoid wicket page update bug
- String absoluteUrl = getCanonicalUrl();
- getRequestCycle().setRequestTarget(new RedirectRequestTarget(absoluteUrl));
- return;
- }
-
- // use an absolute url to workaround Wicket-Tomcat problems with
- // mounted url parameters (issue-111)
- PageParameters params = WicketUtils.newRepositoryParameter(repositoryName);
- params.add("s", searchString);
- String absoluteUrl = getCanonicalUrl(TicketsPage.class, params);
- getRequestCycle().setRequestTarget(new RedirectRequestTarget(absoluteUrl));
- }
- }
}
diff --git a/src/main/java/com/gitblit/wicket/pages/TreePage.java b/src/main/java/com/gitblit/wicket/pages/TreePage.java index d6bf1fe1..722b824f 100644 --- a/src/main/java/com/gitblit/wicket/pages/TreePage.java +++ b/src/main/java/com/gitblit/wicket/pages/TreePage.java @@ -20,6 +20,7 @@ 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.apache.wicket.markup.html.link.ExternalLink;
import org.apache.wicket.markup.html.panel.Fragment;
import org.apache.wicket.markup.repeater.Item;
import org.apache.wicket.markup.repeater.data.DataView;
@@ -30,6 +31,7 @@ import org.eclipse.jgit.revwalk.RevCommit; import com.gitblit.models.PathModel;
import com.gitblit.models.SubmoduleModel;
+import com.gitblit.servlet.RawServlet;
import com.gitblit.utils.ByteFormat;
import com.gitblit.utils.JGitUtils;
import com.gitblit.wicket.CacheControl;
@@ -162,8 +164,8 @@ public class TreePage extends RepositoryPage { links.add(new BookmarkablePageLink<Void>("view", BlobPage.class,
WicketUtils.newPathParameter(repositoryName, id,
path)));
- links.add(new BookmarkablePageLink<Void>("raw", RawPage.class, WicketUtils
- .newPathParameter(repositoryName, id, path)));
+ String rawUrl = RawServlet.asLink(getContextUrl(), repositoryName, id, path);
+ links.add(new ExternalLink("raw", rawUrl));
links.add(new BookmarkablePageLink<Void>("blame", BlamePage.class,
WicketUtils.newPathParameter(repositoryName, id,
path)));
diff --git a/src/main/java/com/gitblit/wicket/pages/UserPage.html b/src/main/java/com/gitblit/wicket/pages/UserPage.html index 7aaded7a..8dccfee7 100644 --- a/src/main/java/com/gitblit/wicket/pages/UserPage.html +++ b/src/main/java/com/gitblit/wicket/pages/UserPage.html @@ -7,27 +7,20 @@ <body>
<wicket:extend>
<div class="container">
- <div class="row" style="padding-top:10px;">
- <div class="span4">
- <div wicket:id="gravatar"></div>
- <div style="text-align: left;">
- <h2><span wicket:id="userDisplayName"></span></h2>
- <div><i class="icon-user"></i> <span wicket:id="userUsername"></span></div>
- <div><i class="icon-envelope"></i><span wicket:id="userEmail"></span></div>
- </div>
+ <div class="row" style="padding-top:15px;">
+ <div class="span3">
+ <div wicket:id="userTitlePanel"></div>
</div>
-
- <div class="span8">
- <div class="pull-right">
- <a class="btn-small" wicket:id="newRepository" style="padding-right:0px;">
- <i class="icon icon-plus-sign"></i>
- <wicket:message key="gb.newRepository"></wicket:message>
- </a>
- </div>
- <div class="tabbable">
+ </div>
+
+ <div class="row" style="padding-top:10px;">
+ <div class="span12">
+ <div class="tabbable tabs-left">
<!-- tab titles -->
<ul class="nav nav-tabs">
<li class="active"><a href="#repositories" data-toggle="tab"><wicket:message key="gb.repositories"></wicket:message></a></li>
+ <div wicket:id="preferencesLink"></div>
+ <div wicket:id="sshKeysLink"></div>
</ul>
<!-- tab content -->
@@ -41,11 +34,51 @@ </tbody>
</table>
</div>
+
+ <!-- preferences tab -->
+ <div wicket:id="preferencesTab"></div>
+
+ <!-- ssh keys tab -->
+ <div wicket:id="sshKeysTab"></div>
+
</div>
</div>
</div>
</div>
</div>
+
+<wicket:fragment wicket:id="preferencesLinkFragment">
+ <li><a href="#preferences" data-toggle="tab"><wicket:message key="gb.preferences"></wicket:message></a></li>
+</wicket:fragment>
+
+<wicket:fragment wicket:id="sshKeysLinkFragment">
+ <li><a href="#ssh" data-toggle="tab"><wicket:message key="gb.sshKeys"></wicket:message></a></li>
+</wicket:fragment>
+
+<wicket:fragment wicket:id="preferencesTabFragment">
+ <div class="tab-pane" id="preferences">
+ <h4><wicket:message key="gb.accountPreferences"></wicket:message></h4>
+ <p><wicket:message key="gb.accountPreferencesDescription"></wicket:message></p>
+ <hr />
+
+ <form wicket:id="prefsForm">
+ <div wicket:id="displayName"></div>
+ <div wicket:id="emailAddress"></div>
+ <div wicket:id="language"></div>
+ <div wicket:id="emailMeOnMyTicketChanges"></div>
+ <div wicket:id="transport"></div>
+
+ <div class="form-actions"><input class="btn btn-appmenu" type="submit" value="Save" wicket:message="value:gb.save" wicket:id="save" /></div>
+ </form>
+ </div>
+</wicket:fragment>
+
+<wicket:fragment wicket:id="sshKeysTabFragment">
+ <div class="tab-pane" id="ssh">
+ <div wicket:id="sshKeysPanel"></div>
+ </div>
+</wicket:fragment>
+
</wicket:extend>
</body>
</html>
\ No newline at end of file diff --git a/src/main/java/com/gitblit/wicket/pages/UserPage.java b/src/main/java/com/gitblit/wicket/pages/UserPage.java index a5d38d16..306eea65 100644 --- a/src/main/java/com/gitblit/wicket/pages/UserPage.java +++ b/src/main/java/com/gitblit/wicket/pages/UserPage.java @@ -15,20 +15,32 @@ */
package com.gitblit.wicket.pages;
+import java.io.Serializable;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
+import java.util.Locale;
import org.apache.wicket.PageParameters;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.markup.html.form.AjaxButton;
import org.apache.wicket.markup.html.basic.Label;
-import org.apache.wicket.markup.html.link.BookmarkablePageLink;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.markup.html.panel.Fragment;
import org.apache.wicket.markup.repeater.Item;
import org.apache.wicket.markup.repeater.data.DataView;
import org.apache.wicket.markup.repeater.data.ListDataProvider;
-import org.eclipse.jgit.lib.PersonIdent;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+import com.gitblit.Constants.Transport;
+import com.gitblit.GitBlitException;
import com.gitblit.Keys;
+import com.gitblit.models.Menu.ParameterMenuItem;
+import com.gitblit.models.NavLink;
+import com.gitblit.models.NavLink.DropDownPageMenuNavLink;
import com.gitblit.models.ProjectModel;
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.UserModel;
@@ -36,13 +48,13 @@ import com.gitblit.utils.StringUtils; import com.gitblit.wicket.GitBlitWebApp;
import com.gitblit.wicket.GitBlitWebSession;
import com.gitblit.wicket.GitblitRedirectException;
-import com.gitblit.wicket.PageRegistration;
-import com.gitblit.wicket.PageRegistration.DropDownMenuItem;
-import com.gitblit.wicket.PageRegistration.DropDownMenuRegistration;
import com.gitblit.wicket.WicketUtils;
-import com.gitblit.wicket.panels.GravatarImage;
-import com.gitblit.wicket.panels.LinkPanel;
+import com.gitblit.wicket.panels.BooleanOption;
+import com.gitblit.wicket.panels.ChoiceOption;
import com.gitblit.wicket.panels.ProjectRepositoryPanel;
+import com.gitblit.wicket.panels.SshKeysPanel;
+import com.gitblit.wicket.panels.TextOption;
+import com.gitblit.wicket.panels.UserTitlePanel;
public class UserPage extends RootPage {
@@ -83,21 +95,30 @@ public class UserPage extends RootPage { user = new UserModel(userName);
}
- add(new Label("userDisplayName", user.getDisplayName()));
- add(new Label("userUsername", user.username));
- LinkPanel email = new LinkPanel("userEmail", null, user.emailAddress, "mailto:#");
- email.setRenderBodyOnly(true);
- add(email.setVisible(app().settings().getBoolean(Keys.web.showEmailAddresses, true) && !StringUtils.isEmpty(user.emailAddress)));
- PersonIdent person = new PersonIdent(user.getDisplayName(), user.emailAddress == null ? user.getDisplayName() : user.emailAddress);
- add(new GravatarImage("gravatar", person, 210));
+ add(new UserTitlePanel("userTitlePanel", user, user.username));
UserModel sessionUser = GitBlitWebSession.get().getUser();
- if (sessionUser != null && user.canCreate() && sessionUser.equals(user)) {
- // user can create personal repositories
- add(new BookmarkablePageLink<Void>("newRepository", EditRepositoryPage.class));
+ boolean isMyProfile = sessionUser != null && sessionUser.equals(user);
+
+ if (isMyProfile) {
+ addPreferences(user);
+
+ if (app().gitblit().isServingSSH()) {
+ // show the SSH key management tab
+ addSshKeys(user);
+ } else {
+ // SSH daemon is disabled, hide keys tab
+ add(new Label("sshKeysLink").setVisible(false));
+ add(new Label("sshKeysTab").setVisible(false));
+ }
} else {
- add(new Label("newRepository").setVisible(false));
+ // visiting user
+ add(new Label("preferencesLink").setVisible(false));
+ add(new Label("preferencesTab").setVisible(false));
+
+ add(new Label("sshKeysLink").setVisible(false));
+ add(new Label("sshKeysTab").setVisible(false));
}
List<RepositoryModel> repositories = getRepositories(params);
@@ -127,10 +148,10 @@ public class UserPage extends RootPage { }
@Override
- protected void addDropDownMenus(List<PageRegistration> pages) {
+ protected void addDropDownMenus(List<NavLink> navLinks) {
PageParameters params = getPageParameters();
- DropDownMenuRegistration menu = new DropDownMenuRegistration("gb.filters",
+ DropDownPageMenuNavLink menu = new DropDownPageMenuNavLink("gb.filters",
UserPage.class);
// preserve time filter option on repository choices
menu.menuItems.addAll(getRepositoryFilterItems(params));
@@ -140,9 +161,171 @@ public class UserPage extends RootPage { if (menu.menuItems.size() > 0) {
// Reset Filter
- menu.menuItems.add(new DropDownMenuItem(getString("gb.reset"), null, null));
+ menu.menuItems.add(new ParameterMenuItem(getString("gb.reset")));
+ }
+
+ navLinks.add(menu);
+ }
+
+ private void addPreferences(UserModel user) {
+ // add preferences
+ Form<Void> prefs = new Form<Void>("prefsForm");
+
+ List<Language> languages = Arrays.asList(
+ new Language("English","en"),
+ new Language("Español", "es"),
+ new Language("Français", "fr"),
+ new Language("日本語", "ja"),
+ new Language("í•œêµë§", "ko"),
+ new Language("Nederlands", "nl"),
+ new Language("Norsk", "no"),
+ new Language("Język Polski", "pl"),
+ new Language("Português", "pt_BR"),
+ new Language("ä¸æ–‡", "zh_CN"));
+
+ Locale locale = user.getPreferences().getLocale();
+ if (locale == null) {
+ // user has not specified language preference
+ // try server default preference
+ String lc = app().settings().getString(Keys.web.forceDefaultLocale, null);
+ if (StringUtils.isEmpty(lc)) {
+ // server default language is not configured
+ // try browser preference
+ Locale sessionLocale = GitBlitWebSession.get().getLocale();
+ if (sessionLocale != null) {
+ locale = sessionLocale;
+ }
+ } else {
+
+ }
+ }
+
+ Language preferredLanguage = null;
+ if (locale != null) {
+ String localeCode = locale.getLanguage();
+ if (!StringUtils.isEmpty(locale.getCountry())) {
+ localeCode += "_" + locale.getCountry();
+ }
+
+ for (Language language : languages) {
+ if (language.code.equals(localeCode)) {
+ // language_COUNTRY match
+ preferredLanguage = language;
+ } else if (preferredLanguage != null && language.code.startsWith(locale.getLanguage())) {
+ // language match
+ preferredLanguage = language;
+ }
+ }
+ }
+
+ final IModel<String> displayName = Model.of(user.getDisplayName());
+ final IModel<String> emailAddress = Model.of(user.emailAddress == null ? "" : user.emailAddress);
+ final IModel<Language> language = Model.of(preferredLanguage);
+ final IModel<Boolean> emailMeOnMyTicketChanges = Model.of(user.getPreferences().isEmailMeOnMyTicketChanges());
+ final IModel<Transport> transport = Model.of(user.getPreferences().getTransport());
+
+ prefs.add(new TextOption("displayName",
+ getString("gb.displayName"),
+ getString("gb.displayNameDescription"),
+ displayName).setVisible(app().authentication().supportsDisplayNameChanges(user)));
+
+ prefs.add(new TextOption("emailAddress",
+ getString("gb.emailAddress"),
+ getString("gb.emailAddressDescription"),
+ emailAddress).setVisible(app().authentication().supportsEmailAddressChanges(user)));
+
+ prefs.add(new ChoiceOption<Language>("language",
+ getString("gb.languagePreference"),
+ getString("gb.languagePreferenceDescription"),
+ language,
+ languages));
+
+ prefs.add(new BooleanOption("emailMeOnMyTicketChanges",
+ getString("gb.emailMeOnMyTicketChanges"),
+ getString("gb.emailMeOnMyTicketChangesDescription"),
+ emailMeOnMyTicketChanges).setVisible(app().notifier().isSendingMail()));
+
+ List<Transport> availableTransports = new ArrayList<>();
+ if (app().gitblit().isServingSSH()) {
+ availableTransports.add(Transport.SSH);
+ }
+ if (app().gitblit().isServingHTTP()) {
+ availableTransports.add(Transport.HTTPS);
+ availableTransports.add(Transport.HTTP);
+ }
+ if (app().gitblit().isServingGIT()) {
+ availableTransports.add(Transport.GIT);
}
- pages.add(menu);
+ prefs.add(new ChoiceOption<Transport>("transport",
+ getString("gb.transportPreference"),
+ getString("gb.transportPreferenceDescription"),
+ transport,
+ availableTransports));
+
+ prefs.add(new AjaxButton("save") {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
+
+ UserModel user = GitBlitWebSession.get().getUser();
+
+ user.displayName = displayName.getObject();
+ user.emailAddress = emailAddress.getObject();
+
+ Language lang = language.getObject();
+ if (lang != null) {
+ user.getPreferences().setLocale(lang.code);
+ }
+
+ user.getPreferences().setEmailMeOnMyTicketChanges(emailMeOnMyTicketChanges.getObject());
+ user.getPreferences().setTransport(transport.getObject());
+
+ try {
+ app().gitblit().reviseUser(user.username, user);
+
+ setRedirect(true);
+ setResponsePage(UserPage.class, WicketUtils.newUsernameParameter(user.username));
+ } catch (GitBlitException e) {
+ // logger.error("Failed to update user " + user.username, e);
+ // error(getString("gb.failedToUpdateUser"), false);
+ }
+ }
+ });
+
+ // add the preferences tab
+ add(new Fragment("preferencesLink", "preferencesLinkFragment", this).setRenderBodyOnly(true));
+ Fragment fragment = new Fragment("preferencesTab", "preferencesTabFragment", this);
+ fragment.add(prefs);
+ add(fragment.setRenderBodyOnly(true));
+ }
+
+ private void addSshKeys(final UserModel user) {
+ Fragment keysTab = new Fragment("sshKeysTab", "sshKeysTabFragment", this);
+ keysTab.add(new SshKeysPanel("sshKeysPanel", user));
+
+ // add the SSH keys tab
+ add(new Fragment("sshKeysLink", "sshKeysLinkFragment", this).setRenderBodyOnly(true));
+ add(keysTab.setRenderBodyOnly(true));
+ }
+
+ private class Language implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ final String name;
+ final String code;
+
+ public Language(String name, String code) {
+ this.name = name;
+ this.code = code;
+ }
+
+ @Override
+ public String toString() {
+ return name + " (" + code +")";
+ }
}
}
diff --git a/src/main/java/com/gitblit/wicket/pages/UsersPage.html b/src/main/java/com/gitblit/wicket/pages/UsersPage.html index 6eec358d..a9a39397 100644 --- a/src/main/java/com/gitblit/wicket/pages/UsersPage.html +++ b/src/main/java/com/gitblit/wicket/pages/UsersPage.html @@ -6,8 +6,6 @@ <body>
<wicket:extend>
<div class="container">
- <div wicket:id="teamsPanel">[teams panel]</div>
-
<div wicket:id="usersPanel">[users panel]</div>
</div>
</wicket:extend>
diff --git a/src/main/java/com/gitblit/wicket/pages/UsersPage.java b/src/main/java/com/gitblit/wicket/pages/UsersPage.java index 652bdba6..eab0b18d 100644 --- a/src/main/java/com/gitblit/wicket/pages/UsersPage.java +++ b/src/main/java/com/gitblit/wicket/pages/UsersPage.java @@ -16,7 +16,6 @@ package com.gitblit.wicket.pages;
import com.gitblit.wicket.RequiresAdminRole;
-import com.gitblit.wicket.panels.TeamsPanel;
import com.gitblit.wicket.panels.UsersPanel;
@RequiresAdminRole
@@ -26,8 +25,6 @@ public class UsersPage extends RootPage { super();
setupPage("", "");
- add(new TeamsPanel("teamsPanel", showAdmin).setVisible(showAdmin));
-
add(new UsersPanel("usersPanel", showAdmin).setVisible(showAdmin));
}
}
diff --git a/src/main/java/com/gitblit/wicket/pages/create_git.md b/src/main/java/com/gitblit/wicket/pages/create_git.md new file mode 100644 index 00000000..309ac675 --- /dev/null +++ b/src/main/java/com/gitblit/wicket/pages/create_git.md @@ -0,0 +1,6 @@ + touch README.md + git init + git add README.md + git commit -m "first commit" + git remote add origin ${primaryUrl} + git push -u origin master diff --git a/src/main/java/com/gitblit/wicket/pages/existing_git.md b/src/main/java/com/gitblit/wicket/pages/existing_git.md new file mode 100644 index 00000000..0a6fad9a --- /dev/null +++ b/src/main/java/com/gitblit/wicket/pages/existing_git.md @@ -0,0 +1,2 @@ + git remote add origin ${primaryUrl} + git push -u origin master diff --git a/src/main/java/com/gitblit/wicket/pages/propose_git.md b/src/main/java/com/gitblit/wicket/pages/propose_git.md index 39ff859e..d52e1332 100644 --- a/src/main/java/com/gitblit/wicket/pages/propose_git.md +++ b/src/main/java/com/gitblit/wicket/pages/propose_git.md @@ -2,5 +2,5 @@ cd ${repo} git checkout -b ${reviewBranch} origin/${integrationBranch} ... - git push --set-upstream origin ${reviewBranch} + git push -u origin ${reviewBranch} diff --git a/src/main/java/com/gitblit/wicket/panels/AccessPolicyPanel.html b/src/main/java/com/gitblit/wicket/panels/AccessPolicyPanel.html new file mode 100644 index 00000000..07050192 --- /dev/null +++ b/src/main/java/com/gitblit/wicket/panels/AccessPolicyPanel.html @@ -0,0 +1,31 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+
+<body>
+<wicket:panel>
+
+ <h4><wicket:message key="gb.accessPolicy"></wicket:message></h4>
+ <p><wicket:message key="gb.accessPolicyDescription"></wicket:message></p>
+
+ <div wicket:id="policiesGroup">
+ <div wicket:id="policies" style="padding-top:4px;">
+ <div>
+ <label style="font-weight:bold;margin-bottom:1px;"><input type="radio" wicket:id="radio" /> <img wicket:id="image"></img> <span wicket:id="name"></span></label>
+ </div>
+ <label class="checkbox" style="color:#777;" wicket:id="description"></label>
+ </div>
+ </div>
+
+ <div wicket:id="allowForks"></div>
+
+<wicket:fragment wicket:id="allowForksFragment">
+ <hr />
+
+ <div wicket:id="allowForks"></div>
+</wicket:fragment>
+</wicket:panel>
+</body>
+</html>
\ No newline at end of file diff --git a/src/main/java/com/gitblit/wicket/panels/AccessPolicyPanel.java b/src/main/java/com/gitblit/wicket/panels/AccessPolicyPanel.java new file mode 100644 index 00000000..d8dcdced --- /dev/null +++ b/src/main/java/com/gitblit/wicket/panels/AccessPolicyPanel.java @@ -0,0 +1,199 @@ +/*
+ * 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.wicket.panels;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.wicket.ajax.form.AjaxFormChoiceComponentUpdatingBehavior;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.form.Radio;
+import org.apache.wicket.markup.html.form.RadioGroup;
+import org.apache.wicket.markup.html.list.ListItem;
+import org.apache.wicket.markup.html.list.ListView;
+import org.apache.wicket.markup.html.panel.Fragment;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+
+import com.gitblit.Constants.AccessRestrictionType;
+import com.gitblit.Constants.AuthorizationControl;
+import com.gitblit.Keys;
+import com.gitblit.models.RepositoryModel;
+import com.gitblit.wicket.WicketUtils;
+
+/**
+ * A radio group panel of the 5 available authorization/access restriction combinations.
+ *
+ * @author James Moger
+ *
+ */
+public class AccessPolicyPanel extends BasePanel {
+
+ private static final long serialVersionUID = 1L;
+
+ private final RepositoryModel repository;
+
+ private final AjaxFormChoiceComponentUpdatingBehavior callback;
+
+ private RadioGroup<AccessPolicy> policiesGroup;
+
+ private IModel<Boolean> allowForks;
+
+ public AccessPolicyPanel(String wicketId, RepositoryModel repository) {
+ this(wicketId, repository, null);
+ }
+
+ public AccessPolicyPanel(String wicketId, RepositoryModel repository, AjaxFormChoiceComponentUpdatingBehavior callback) {
+ super(wicketId);
+ this.repository = repository;
+ this.callback = callback;
+ }
+
+ @Override
+ protected void onInitialize() {
+ super.onInitialize();
+
+ AccessPolicy anonymousPolicy = new AccessPolicy(getString("gb.anonymousPolicy"),
+ getString("gb.anonymousPolicyDescription"),
+ "blank.png",
+ AuthorizationControl.AUTHENTICATED,
+ AccessRestrictionType.NONE);
+
+ AccessPolicy authenticatedPushPolicy = new AccessPolicy(getString("gb.authenticatedPushPolicy"),
+ getString("gb.authenticatedPushPolicyDescription"),
+ "lock_go_16x16.png",
+ AuthorizationControl.AUTHENTICATED,
+ AccessRestrictionType.PUSH);
+
+ AccessPolicy namedPushPolicy = new AccessPolicy(getString("gb.namedPushPolicy"),
+ getString("gb.namedPushPolicyDescription"),
+ "lock_go_16x16.png",
+ AuthorizationControl.NAMED,
+ AccessRestrictionType.PUSH);
+
+ AccessPolicy clonePolicy = new AccessPolicy(getString("gb.clonePolicy"),
+ getString("gb.clonePolicyDescription"),
+ "lock_pull_16x16.png",
+ AuthorizationControl.NAMED,
+ AccessRestrictionType.CLONE);
+
+ AccessPolicy viewPolicy = new AccessPolicy(getString("gb.viewPolicy"),
+ getString("gb.viewPolicyDescription"),
+ "shield_16x16.png",
+ AuthorizationControl.NAMED,
+ AccessRestrictionType.VIEW);
+
+ List<AccessPolicy> policies = new ArrayList<AccessPolicy>();
+ if (app().settings().getBoolean(Keys.git.allowAnonymousPushes, false)) {
+ policies.add(anonymousPolicy);
+ }
+ policies.add(authenticatedPushPolicy);
+ policies.add(namedPushPolicy);
+ policies.add(clonePolicy);
+ policies.add(viewPolicy);
+
+ AccessRestrictionType defaultRestriction = repository.accessRestriction;
+ if (defaultRestriction == null) {
+ defaultRestriction = AccessRestrictionType.fromName(app().settings().getString(Keys.git.defaultAccessRestriction,
+ AccessRestrictionType.PUSH.name()));
+ }
+
+ AuthorizationControl defaultControl = repository.authorizationControl;
+ if (defaultControl == null) {
+ defaultControl = AuthorizationControl.fromName(app().settings().getString(Keys.git.defaultAuthorizationControl,
+ AuthorizationControl.NAMED.name()));
+ }
+
+ AccessPolicy defaultPolicy = namedPushPolicy;
+ for (AccessPolicy policy : policies) {
+ if (policy.type == defaultRestriction && policy.control == defaultControl) {
+ defaultPolicy = policy;
+ }
+ }
+
+ policiesGroup = new RadioGroup<>("policiesGroup", new Model<AccessPolicy>(defaultPolicy));
+ ListView<AccessPolicy> policiesList = new ListView<AccessPolicy>("policies", policies) {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void populateItem(ListItem<AccessPolicy> item) {
+ AccessPolicy p = item.getModelObject();
+ item.add(new Radio<AccessPolicy>("radio", item.getModel()));
+ item.add(WicketUtils.newImage("image", p.image));
+ item.add(new Label("name", p.name));
+ item.add(new Label("description", p.description));
+ }
+ };
+ policiesGroup.add(policiesList);
+ if (callback != null) {
+ policiesGroup.add(callback);
+ policiesGroup.setOutputMarkupId(true);
+ }
+ add(policiesGroup);
+
+ allowForks = Model.of(app().settings().getBoolean(Keys.web.allowForking, true));
+ if (allowForks.getObject()) {
+ Fragment fragment = new Fragment("allowForks", "allowForksFragment", this);
+ fragment.add(new BooleanOption("allowForks",
+ getString("gb.allowForks"),
+ getString("gb.allowForksDescription"),
+ allowForks));
+ add(fragment);
+ } else {
+ add(new Label("allowForks").setVisible(false));
+ }
+
+ setOutputMarkupId(true);
+ }
+
+ public void updateModel(RepositoryModel repository) {
+ AccessPolicy policy = policiesGroup.getModelObject();
+ repository.authorizationControl = policy.control;
+ repository.accessRestriction = policy.type;
+ repository.allowForks = allowForks.getObject();
+ }
+
+ @Override
+ protected boolean getStatelessHint() {
+ return false;
+ }
+
+ public static class AccessPolicy implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ final String name;
+ final String description;
+ final String image;
+ final AuthorizationControl control;
+ final AccessRestrictionType type;
+
+ AccessPolicy(String name, String description, String img, AuthorizationControl control, AccessRestrictionType type) {
+ this.name = name;
+ this.description = description;
+ this.image = img;
+ this.control = control;
+ this.type = type;
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+ }
+}
diff --git a/src/main/java/com/gitblit/wicket/panels/BasePanel.java b/src/main/java/com/gitblit/wicket/panels/BasePanel.java index dda57979..e8f8f6f2 100644 --- a/src/main/java/com/gitblit/wicket/panels/BasePanel.java +++ b/src/main/java/com/gitblit/wicket/panels/BasePanel.java @@ -44,6 +44,10 @@ public abstract class BasePanel extends Panel { return GitBlitWebApp.get();
}
+ protected String getContextUrl() {
+ return getRequest().getRelativePathPrefixToContextRoot();
+ }
+
protected TimeZone getTimeZone() {
return app().settings().getBoolean(Keys.web.useClientTimezone, false) ? GitBlitWebSession.get()
.getTimezone() : app().getTimezone();
diff --git a/src/main/java/com/gitblit/wicket/panels/BooleanChoiceOption.html b/src/main/java/com/gitblit/wicket/panels/BooleanChoiceOption.html new file mode 100644 index 00000000..b1ced8da --- /dev/null +++ b/src/main/java/com/gitblit/wicket/panels/BooleanChoiceOption.html @@ -0,0 +1,19 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+
+<body>
+<wicket:panel>
+ <div style="padding-top:4px;">
+ <div>
+ <label style="font-weight:bold;margin-bottom:1px;" class="checkbox"><input type="checkbox" wicket:id="checkbox" /> <span wicket:id="name"></span></label>
+ </div>
+ <label class="checkbox" style="color:#777;"> <span wicket:id="description"></span>
+ <p style="padding-top:5px;"><select class="span3" wicket:id="choice" /></p>
+ </label>
+ </div>
+</wicket:panel>
+</body>
+</html>
\ No newline at end of file diff --git a/src/main/java/com/gitblit/wicket/panels/BooleanChoiceOption.java b/src/main/java/com/gitblit/wicket/panels/BooleanChoiceOption.java new file mode 100644 index 00000000..98a700b7 --- /dev/null +++ b/src/main/java/com/gitblit/wicket/panels/BooleanChoiceOption.java @@ -0,0 +1,78 @@ +/*
+ * 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.wicket.panels;
+
+import java.util.List;
+
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.form.CheckBox;
+import org.apache.wicket.markup.html.form.DropDownChoice;
+import org.apache.wicket.model.IModel;
+import org.parboiled.common.StringUtils;
+
+/**
+ * A re-usable conditional choice option panel.
+ *
+ * [x] title
+ * description
+ * [choices]
+ *
+ * @author James Moger
+ *
+ */
+public class BooleanChoiceOption<T> extends BasePanel {
+
+ private static final long serialVersionUID = 1L;
+
+ final CheckBox checkbox;
+ final DropDownChoice<T> choice;
+
+ public BooleanChoiceOption(String wicketId, String title, String description, IModel<Boolean> checkboxModel, IModel<T> choiceModel, List<T> choices) {
+ super(wicketId);
+ add(new Label("name", title));
+ add(new Label("description", description).setVisible(!StringUtils.isEmpty(description)));
+
+ this.checkbox = new CheckBox("checkbox", checkboxModel);
+ checkbox.setOutputMarkupId(true);
+
+ this.choice = new DropDownChoice<T>("choice", choiceModel, choices);
+ choice.setOutputMarkupId(true);
+
+ setup();
+ }
+
+ private void setup() {
+ add(checkbox);
+ add(choice.setMarkupId("choice").setEnabled(choice.getChoices().size() > 0));
+ choice.setEnabled(checkbox.getModelObject());
+
+ checkbox.add(new AjaxFormComponentUpdatingBehavior("onchange") {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void onUpdate(AjaxRequestTarget target) {
+ choice.setEnabled(checkbox.getModelObject());
+ target.addComponent(choice);
+ if (!choice.isEnabled()) {
+ choice.setModelObject(null);
+ }
+ }
+ });
+ }
+}
diff --git a/src/main/java/com/gitblit/wicket/panels/BooleanOption.html b/src/main/java/com/gitblit/wicket/panels/BooleanOption.html new file mode 100644 index 00000000..297cbd3b --- /dev/null +++ b/src/main/java/com/gitblit/wicket/panels/BooleanOption.html @@ -0,0 +1,17 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+
+<body>
+<wicket:panel>
+ <div style="padding-top:4px;">
+ <div>
+ <label style="font-weight:bold;margin-bottom:1px;" class="checkbox"><input type="checkbox" wicket:id="checkbox" /> <span wicket:id="name"></span></label>
+ </div>
+ <label class="checkbox" style="color:#777;" wicket:id="description"></label>
+ </div>
+</wicket:panel>
+</body>
+</html>
\ No newline at end of file diff --git a/src/main/java/com/gitblit/wicket/panels/BooleanOption.java b/src/main/java/com/gitblit/wicket/panels/BooleanOption.java new file mode 100644 index 00000000..4a9299ce --- /dev/null +++ b/src/main/java/com/gitblit/wicket/panels/BooleanOption.java @@ -0,0 +1,54 @@ +/*
+ * 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.wicket.panels;
+
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.form.CheckBox;
+import org.apache.wicket.model.IModel;
+import org.parboiled.common.StringUtils;
+
+/**
+ * A re-usable checkbox option panel.
+ *
+ * [x] title
+ * description
+ *
+ * @author James Moger
+ *
+ */
+public class BooleanOption extends BasePanel {
+
+ private static final long serialVersionUID = 1L;
+
+ public BooleanOption(String wicketId, String title, String description, IModel<Boolean> model) {
+ super(wicketId);
+ add(new Label("name", title));
+ add(new Label("description", description).setVisible(!StringUtils.isEmpty(description)));
+ add(new CheckBox("checkbox", model));
+ }
+
+ public BooleanOption(String wicketId, String title, String description, CheckBox checkbox) {
+ super(wicketId);
+ add(new Label("name", title));
+ add(new Label("description", description).setVisible(!StringUtils.isEmpty(description)));
+ add(checkbox.setMarkupId("checkbox"));
+ }
+
+ public BooleanOption setIsHtmlDescription(boolean val) {
+ ((Label) get("description")).setEscapeModelStrings(!val);
+ return this;
+ }
+}
diff --git a/src/main/java/com/gitblit/wicket/panels/BranchesPanel.html b/src/main/java/com/gitblit/wicket/panels/BranchesPanel.html index e95b283a..05df23ea 100644 --- a/src/main/java/com/gitblit/wicket/panels/BranchesPanel.html +++ b/src/main/java/com/gitblit/wicket/panels/BranchesPanel.html @@ -29,21 +29,21 @@ <!-- branch page links -->
<wicket:fragment wicket:id="branchPageLinks">
<span class="link">
- <a wicket:id="log"><wicket:message key="gb.log"></wicket:message></a> | <a wicket:id="tree"><wicket:message key="gb.tree"></wicket:message></a> | <a wicket:id="metrics"><wicket:message key="gb.metrics"></wicket:message></a> | <a wicket:id="syndication"><wicket:message key="gb.feed"></wicket:message></a>
+ <a wicket:id="log"><wicket:message key="gb.log"></wicket:message></a> | <a wicket:id="tree"><wicket:message key="gb.tree"></wicket:message></a> | <a wicket:id="raw"><wicket:message key="gb.raw"></wicket:message></a> | <a wicket:id="metrics"><wicket:message key="gb.metrics"></wicket:message></a> | <a wicket:id="syndication"><wicket:message key="gb.feed"></wicket:message></a>
</span>
</wicket:fragment>
<!-- branch page admin links -->
<wicket:fragment wicket:id="branchPageAdminLinks">
<span class="link">
- <a wicket:id="log"><wicket:message key="gb.log"></wicket:message></a> | <a wicket:id="tree"><wicket:message key="gb.tree"></wicket:message></a> | <a wicket:id="metrics"><wicket:message key="gb.metrics"></wicket:message></a> | <a wicket:id="syndication"><wicket:message key="gb.feed"></wicket:message></a> | <a wicket:id="deleteBranch"><wicket:message key="gb.delete"></wicket:message></a>
+ <a wicket:id="log"><wicket:message key="gb.log"></wicket:message></a> | <a wicket:id="tree"><wicket:message key="gb.tree"></wicket:message></a> | <a wicket:id="raw"><wicket:message key="gb.raw"></wicket:message></a> | <a wicket:id="metrics"><wicket:message key="gb.metrics"></wicket:message></a> | <a wicket:id="syndication"><wicket:message key="gb.feed"></wicket:message></a> | <a wicket:id="deleteBranch"><wicket:message key="gb.delete"></wicket:message></a>
</span>
</wicket:fragment>
<!-- branch panel links -->
<wicket:fragment wicket:id="branchPanelLinks">
<span class="link">
- <a wicket:id="log"><wicket:message key="gb.log"></wicket:message></a> | <a wicket:id="tree"><wicket:message key="gb.tree"></wicket:message></a>
+ <a wicket:id="log"><wicket:message key="gb.log"></wicket:message></a> | <a wicket:id="tree"><wicket:message key="gb.tree"></wicket:message></a> | <a wicket:id="raw"><wicket:message key="gb.raw"></wicket:message></a>
</span>
</wicket:fragment>
diff --git a/src/main/java/com/gitblit/wicket/panels/BranchesPanel.java b/src/main/java/com/gitblit/wicket/panels/BranchesPanel.java index 6a7873ea..4bf00f8a 100644 --- a/src/main/java/com/gitblit/wicket/panels/BranchesPanel.java +++ b/src/main/java/com/gitblit/wicket/panels/BranchesPanel.java @@ -40,6 +40,7 @@ import com.gitblit.Constants; import com.gitblit.models.RefModel;
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.UserModel;
+import com.gitblit.servlet.RawServlet;
import com.gitblit.servlet.SyndicationServlet;
import com.gitblit.utils.CommitCache;
import com.gitblit.utils.JGitUtils;
@@ -143,6 +144,8 @@ public class BranchesPanel extends BasePanel { .newObjectParameter(model.name, entry.getName())));
fragment.add(new BookmarkablePageLink<Void>("tree", TreePage.class, WicketUtils
.newObjectParameter(model.name, entry.getName())));
+ String rawUrl = RawServlet.asLink(getContextUrl(), model.name, Repository.shortenRefName(entry.getName()), null);
+ fragment.add(new ExternalLink("raw", rawUrl));
fragment.add(new BookmarkablePageLink<Void>("metrics", MetricsPage.class,
WicketUtils.newObjectParameter(model.name, entry.getName())));
fragment.add(new ExternalLink("syndication", SyndicationServlet.asLink(
@@ -158,6 +161,8 @@ public class BranchesPanel extends BasePanel { .newObjectParameter(model.name, entry.getName())));
fragment.add(new BookmarkablePageLink<Void>("tree", TreePage.class, WicketUtils
.newObjectParameter(model.name, entry.getName())));
+ String rawUrl = RawServlet.asLink(getContextUrl(), model.name, Repository.shortenRefName(entry.getName()), null);
+ fragment.add(new ExternalLink("raw", rawUrl));
item.add(fragment);
}
WicketUtils.setAlternatingBackground(item, counter);
diff --git a/src/main/java/com/gitblit/wicket/panels/ChoiceOption.html b/src/main/java/com/gitblit/wicket/panels/ChoiceOption.html new file mode 100644 index 00000000..e9e48874 --- /dev/null +++ b/src/main/java/com/gitblit/wicket/panels/ChoiceOption.html @@ -0,0 +1,19 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+
+<body>
+<wicket:panel>
+ <div style="padding-top:4px;">
+ <div style="margin-bottom:1px;">
+ <b><span wicket:id="name"></span></b>
+ </div>
+ <label class="checkbox" style="color:#777;"> <span wicket:id="description"></span>
+ <p style="padding-top:5px;"><select class="span3" wicket:id="choice" /></p>
+ </label>
+ </div>
+</wicket:panel>
+</body>
+</html>
\ No newline at end of file diff --git a/src/main/java/com/gitblit/wicket/panels/ChoiceOption.java b/src/main/java/com/gitblit/wicket/panels/ChoiceOption.java new file mode 100644 index 00000000..9c25b70a --- /dev/null +++ b/src/main/java/com/gitblit/wicket/panels/ChoiceOption.java @@ -0,0 +1,52 @@ +/*
+ * 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.wicket.panels;
+
+import java.util.List;
+
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.form.DropDownChoice;
+import org.apache.wicket.model.IModel;
+import org.parboiled.common.StringUtils;
+
+/**
+ * A re-usable choice option panel.
+ *
+ * title
+ * description
+ * [choices]
+ *
+ * @author James Moger
+ *
+ */
+public class ChoiceOption<T> extends BasePanel {
+
+ private static final long serialVersionUID = 1L;
+
+ public ChoiceOption(String wicketId, String title, String description, IModel<T> model, List<T> choices) {
+ super(wicketId);
+ add(new Label("name", title));
+ add(new Label("description", description).setVisible(!StringUtils.isEmpty(description)));
+ add(new DropDownChoice<>("choice", model, choices).setEnabled(choices.size() > 0));
+ }
+
+ public ChoiceOption(String wicketId, String title, String description, DropDownChoice<?> choice) {
+ super(wicketId);
+ add(new Label("name", title));
+ add(new Label("description", description).setVisible(!StringUtils.isEmpty(description)));
+ add(choice.setMarkupId("choice").setEnabled(choice.getChoices().size() > 0));
+ }
+}
diff --git a/src/main/java/com/gitblit/wicket/panels/DropDownMenu.java b/src/main/java/com/gitblit/wicket/panels/DropDownMenu.java index d1a632e2..4e7ae54c 100644 --- a/src/main/java/com/gitblit/wicket/panels/DropDownMenu.java +++ b/src/main/java/com/gitblit/wicket/panels/DropDownMenu.java @@ -21,38 +21,90 @@ import org.apache.wicket.markup.repeater.Item; import org.apache.wicket.markup.repeater.data.DataView;
import org.apache.wicket.markup.repeater.data.ListDataProvider;
-import com.gitblit.wicket.PageRegistration.DropDownMenuItem;
-import com.gitblit.wicket.PageRegistration.DropDownMenuRegistration;
+import com.gitblit.models.Menu.ExternalLinkMenuItem;
+import com.gitblit.models.Menu.MenuDivider;
+import com.gitblit.models.Menu.MenuItem;
+import com.gitblit.models.Menu.PageLinkMenuItem;
+import com.gitblit.models.Menu.ParameterMenuItem;
+import com.gitblit.models.NavLink.DropDownMenuNavLink;
+import com.gitblit.models.NavLink.DropDownPageMenuNavLink;
import com.gitblit.wicket.WicketUtils;
public class DropDownMenu extends Panel {
private static final long serialVersionUID = 1L;
- public DropDownMenu(String id, String label, final DropDownMenuRegistration menu) {
+ public DropDownMenu(String id, String label, final DropDownPageMenuNavLink menu) {
super(id);
add(new Label("label", label).setRenderBodyOnly(true));
- ListDataProvider<DropDownMenuItem> items = new ListDataProvider<DropDownMenuItem>(
- menu.menuItems);
- DataView<DropDownMenuItem> view = new DataView<DropDownMenuItem>("menuItems", items) {
+ ListDataProvider<MenuItem> items = new ListDataProvider<MenuItem>(menu.menuItems);
+ DataView<MenuItem> view = new DataView<MenuItem>("menuItems", items) {
private static final long serialVersionUID = 1L;
@Override
- public void populateItem(final Item<DropDownMenuItem> item) {
- DropDownMenuItem entry = item.getModelObject();
- if (entry.isDivider()) {
+ public void populateItem(final Item<MenuItem> item) {
+ MenuItem entry = item.getModelObject();
+ if (entry instanceof PageLinkMenuItem) {
+ // link to another Wicket page
+ PageLinkMenuItem pageLink = (PageLinkMenuItem) entry;
+ item.add(new LinkPanel("menuItem", null, null, pageLink.toString(), pageLink.getPageClass(),
+ pageLink.getPageParameters(), false).setRenderBodyOnly(true));
+ } else if (entry instanceof ExternalLinkMenuItem) {
+ // link to a specified href
+ ExternalLinkMenuItem extLink = (ExternalLinkMenuItem) entry;
+ item.add(new LinkPanel("menuItem", null, extLink.toString(), extLink.getHref(),
+ extLink.openInNewWindow()).setRenderBodyOnly(true));
+ } else if (entry instanceof MenuDivider) {
+ // divider
item.add(new Label("menuItem").setRenderBodyOnly(true));
WicketUtils.setCssClass(item, "divider");
} else {
+ ParameterMenuItem parameter = (ParameterMenuItem) entry;
+ // parameter link for the current page
String icon = null;
- if (entry.isSelected()) {
+ if (parameter.isSelected()) {
icon = "icon-ok";
} else {
icon = "icon-ok-white";
}
item.add(new LinkPanel("menuItem", icon, null, entry.toString(), menu.pageClass,
- entry.getPageParameters(), false).setRenderBodyOnly(true));
+ parameter.getPageParameters(), false).setRenderBodyOnly(true));
+ }
+ }
+ };
+ add(view);
+ setRenderBodyOnly(true);
+ }
+
+ public DropDownMenu(String id, String label, final DropDownMenuNavLink menu) {
+ super(id);
+
+ add(new Label("label", label).setRenderBodyOnly(true));
+ ListDataProvider<MenuItem> items = new ListDataProvider<MenuItem>(menu.menuItems);
+ DataView<MenuItem> view = new DataView<MenuItem>("menuItems", items) {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public void populateItem(final Item<MenuItem> item) {
+ MenuItem entry = item.getModelObject();
+ if (entry instanceof PageLinkMenuItem) {
+ // link to another Wicket page
+ PageLinkMenuItem pageLink = (PageLinkMenuItem) entry;
+ item.add(new LinkPanel("menuItem", null, null, pageLink.toString(), pageLink.getPageClass(),
+ pageLink.getPageParameters(), false).setRenderBodyOnly(true));
+ } else if (entry instanceof ExternalLinkMenuItem) {
+ // link to a specified href
+ ExternalLinkMenuItem extLink = (ExternalLinkMenuItem) entry;
+ item.add(new LinkPanel("menuItem", null, extLink.toString(), extLink.getHref(),
+ extLink.openInNewWindow()).setRenderBodyOnly(true));
+ } else if (entry instanceof MenuDivider) {
+ // divider
+ item.add(new Label("menuItem").setRenderBodyOnly(true));
+ WicketUtils.setCssClass(item, "divider");
+ } else {
+ throw new IllegalArgumentException(String.format("Unexpected menuitem type %s",
+ entry.getClass().getSimpleName()));
}
}
};
diff --git a/src/main/java/com/gitblit/wicket/panels/FilterableRepositoryList.java b/src/main/java/com/gitblit/wicket/panels/FilterableRepositoryList.java index 45b0bab1..4433b043 100644 --- a/src/main/java/com/gitblit/wicket/panels/FilterableRepositoryList.java +++ b/src/main/java/com/gitblit/wicket/panels/FilterableRepositoryList.java @@ -33,7 +33,6 @@ import com.gitblit.utils.StringUtils; import com.gitblit.wicket.WicketUtils;
import com.gitblit.wicket.freemarker.FreemarkerPanel;
import com.gitblit.wicket.ng.NgController;
-import com.gitblit.wicket.pages.EditRepositoryPage;
/**
* A client-side filterable rich repository list which uses Freemarker, Wicket,
@@ -98,7 +97,7 @@ public class FilterableRepositoryList extends BasePanel { }
if (allowCreate) {
- panel.add(new LinkPanel(ngList + "Button", "btn btn-mini", getString("gb.newRepository"), EditRepositoryPage.class));
+ panel.add(new LinkPanel(ngList + "Button", "btn btn-mini", getString("gb.newRepository"), app().getNewRepositoryPage()));
} else {
panel.add(new Label(ngList + "Button").setVisible(false));
}
diff --git a/src/main/java/com/gitblit/wicket/panels/NavigationPanel.java b/src/main/java/com/gitblit/wicket/panels/NavigationPanel.java index 393dd139..2bc92f4c 100644 --- a/src/main/java/com/gitblit/wicket/panels/NavigationPanel.java +++ b/src/main/java/com/gitblit/wicket/panels/NavigationPanel.java @@ -23,9 +23,11 @@ import org.apache.wicket.markup.repeater.Item; import org.apache.wicket.markup.repeater.data.DataView;
import org.apache.wicket.markup.repeater.data.ListDataProvider;
-import com.gitblit.wicket.PageRegistration;
-import com.gitblit.wicket.PageRegistration.DropDownMenuRegistration;
-import com.gitblit.wicket.PageRegistration.OtherPageLink;
+import com.gitblit.models.NavLink;
+import com.gitblit.models.NavLink.DropDownMenuNavLink;
+import com.gitblit.models.NavLink.DropDownPageMenuNavLink;
+import com.gitblit.models.NavLink.ExternalNavLink;
+import com.gitblit.models.NavLink.PageNavLink;
import com.gitblit.wicket.WicketUtils;
import com.gitblit.wicket.pages.BasePage;
@@ -34,45 +36,59 @@ public class NavigationPanel extends Panel { private static final long serialVersionUID = 1L;
public NavigationPanel(String id, final Class<? extends BasePage> pageClass,
- List<PageRegistration> registeredPages) {
+ List<NavLink> navLinks) {
super(id);
- ListDataProvider<PageRegistration> refsDp = new ListDataProvider<PageRegistration>(
- registeredPages);
- DataView<PageRegistration> refsView = new DataView<PageRegistration>("navLink", refsDp) {
+ ListDataProvider<NavLink> refsDp = new ListDataProvider<NavLink>(navLinks);
+ DataView<NavLink> linksView = new DataView<NavLink>("navLink", refsDp) {
private static final long serialVersionUID = 1L;
@Override
- public void populateItem(final Item<PageRegistration> item) {
- PageRegistration entry = item.getModelObject();
- if (entry.hiddenPhone) {
+ public void populateItem(final Item<NavLink> item) {
+ NavLink navLink = item.getModelObject();
+ String linkText = navLink.translationKey;
+ try {
+ // try to lookup translation key
+ linkText = getString(navLink.translationKey);
+ } catch (Exception e) {
+ }
+
+ if (navLink.hiddenPhone) {
WicketUtils.setCssClass(item, "hidden-phone");
}
- if (entry instanceof OtherPageLink) {
+ if (navLink instanceof ExternalNavLink) {
// other link
- OtherPageLink link = (OtherPageLink) entry;
- Component c = new LinkPanel("link", null, getString(entry.translationKey), link.url);
+ ExternalNavLink link = (ExternalNavLink) navLink;
+ Component c = new LinkPanel("link", null, linkText, link.url);
+ c.setRenderBodyOnly(true);
+ item.add(c);
+ } else if (navLink instanceof DropDownPageMenuNavLink) {
+ // drop down menu
+ DropDownPageMenuNavLink reg = (DropDownPageMenuNavLink) navLink;
+ Component c = new DropDownMenu("link", linkText, reg);
c.setRenderBodyOnly(true);
item.add(c);
- } else if (entry instanceof DropDownMenuRegistration) {
+ WicketUtils.setCssClass(item, "dropdown");
+ } else if (navLink instanceof DropDownMenuNavLink) {
// drop down menu
- DropDownMenuRegistration reg = (DropDownMenuRegistration) entry;
- Component c = new DropDownMenu("link", getString(entry.translationKey), reg);
+ DropDownMenuNavLink reg = (DropDownMenuNavLink) navLink;
+ Component c = new DropDownMenu("link", linkText, reg);
c.setRenderBodyOnly(true);
item.add(c);
WicketUtils.setCssClass(item, "dropdown");
- } else {
+ } else if (navLink instanceof PageNavLink) {
+ PageNavLink reg = (PageNavLink) navLink;
// standard page link
- Component c = new LinkPanel("link", null, getString(entry.translationKey),
- entry.pageClass, entry.params);
+ Component c = new LinkPanel("link", null, linkText,
+ reg.pageClass, reg.params);
c.setRenderBodyOnly(true);
- if (entry.pageClass.equals(pageClass)) {
+ if (reg.pageClass.equals(pageClass)) {
WicketUtils.setCssClass(item, "active");
}
item.add(c);
}
}
};
- add(refsView);
+ add(linksView);
}
}
\ 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 d5a87d3f..33345a0a 100644 --- a/src/main/java/com/gitblit/wicket/panels/ProjectRepositoryPanel.html +++ b/src/main/java/com/gitblit/wicket/panels/ProjectRepositoryPanel.html @@ -5,15 +5,6 @@ lang="en">
<wicket:panel>
- <wicket:fragment wicket:id="repositoryAdminLinks">
- <span class="link">
- <a wicket:id="tree"><wicket:message key="gb.tree"></wicket:message></a>
- | <a wicket:id="log"><wicket:message key="gb.log"></wicket:message></a>
- | <a wicket:id="editRepository"><wicket:message key="gb.edit">[edit]</wicket:message></a>
- | <a wicket:id="deleteRepository"><wicket:message key="gb.delete">[delete]</wicket:message></a>
- </span>
- </wicket:fragment>
-
<wicket:fragment wicket:id="repositoryOwnerLinks">
<span class="link">
<a wicket:id="tree"><wicket:message key="gb.tree"></wicket:message></a>
diff --git a/src/main/java/com/gitblit/wicket/panels/ProjectRepositoryPanel.java b/src/main/java/com/gitblit/wicket/panels/ProjectRepositoryPanel.java index a0f8ac48..8630d201 100644 --- a/src/main/java/com/gitblit/wicket/panels/ProjectRepositoryPanel.java +++ b/src/main/java/com/gitblit/wicket/panels/ProjectRepositoryPanel.java @@ -15,7 +15,6 @@ */
package com.gitblit.wicket.panels;
-import java.text.MessageFormat;
import java.util.Map;
import org.apache.wicket.Component;
@@ -24,7 +23,6 @@ import org.apache.wicket.PageParameters; import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.link.BookmarkablePageLink;
import org.apache.wicket.markup.html.link.ExternalLink;
-import org.apache.wicket.markup.html.link.Link;
import org.apache.wicket.markup.html.panel.Fragment;
import com.gitblit.Constants.AccessRestrictionType;
@@ -129,38 +127,10 @@ public class ProjectRepositoryPanel extends BasePanel { user = UserModel.ANONYMOUS;
}
Fragment repositoryLinks;
- boolean showOwner = entry.isOwner(user.username);
- // owner of personal repository gets admin powers
- boolean showAdmin = isAdmin || entry.isUsersPersonalRepository(user.username);
-
- if (showAdmin || showOwner) {
- repositoryLinks = new Fragment("repositoryLinks", showAdmin ? "repositoryAdminLinks"
- : "repositoryOwnerLinks", this);
+ if (user.canAdmin(entry)) {
+ repositoryLinks = new Fragment("repositoryLinks", "repositoryOwnerLinks", this);
repositoryLinks.add(new BookmarkablePageLink<Void>("editRepository", EditRepositoryPage.class,
WicketUtils.newRepositoryParameter(entry.name)));
- if (showAdmin) {
- Link<Void> deleteLink = new Link<Void>("deleteRepository") {
-
- private static final long serialVersionUID = 1L;
-
- @Override
- public void onClick() {
- if (app().repositories().deleteRepositoryModel(entry)) {
- // redirect to the owning page
- if (entry.isPersonalRepository()) {
- setResponsePage(getPage().getClass(), WicketUtils.newUsernameParameter(entry.projectPath.substring(1)));
- } else {
- setResponsePage(getPage().getClass(), WicketUtils.newProjectParameter(entry.projectPath));
- }
- } else {
- error(MessageFormat.format(getString("gb.repositoryDeleteFailed"), entry));
- }
- }
- };
- deleteLink.add(new JavascriptEventConfirmation("onclick", MessageFormat.format(
- localizer.getString("gb.deleteRepository", parent), entry)));
- repositoryLinks.add(deleteLink);
- }
} else {
repositoryLinks = new Fragment("repositoryLinks", "repositoryUserLinks", this);
}
diff --git a/src/main/java/com/gitblit/wicket/panels/RegistrantPermissionsPanel.html b/src/main/java/com/gitblit/wicket/panels/RegistrantPermissionsPanel.html index eb82245c..a48797ec 100644 --- a/src/main/java/com/gitblit/wicket/panels/RegistrantPermissionsPanel.html +++ b/src/main/java/com/gitblit/wicket/panels/RegistrantPermissionsPanel.html @@ -16,7 +16,7 @@ </form>
<div style="clear:both;" wicket:id="permissionRow">
- <div style="padding-top:10px;border-left:1px solid #ccc;border-right:1px solid #ccc;" class="row-fluid">
+ <div style="padding-top:10px;" class="row-fluid">
<div style="padding-top:5px;padding-left:5px" class="span6"><span wicket:id="registrant"></span></div><div style="padding-top:5px;padding-right:5px;text-align:right;" class="span3"><span class="label" wicket:id="pType">[permission type]</span></div> <select class="input-medium" wicket:id="permission"></select>
</div>
</div>
diff --git a/src/main/java/com/gitblit/wicket/panels/RepositoriesPanel.html b/src/main/java/com/gitblit/wicket/panels/RepositoriesPanel.html index 0cf3ef29..9ac525ad 100644 --- a/src/main/java/com/gitblit/wicket/panels/RepositoriesPanel.html +++ b/src/main/java/com/gitblit/wicket/panels/RepositoriesPanel.html @@ -42,14 +42,6 @@ </div>
</wicket:fragment>
- <wicket:fragment wicket:id="repositoryAdminLinks">
- <span class="link"><a wicket:id="editRepository"><wicket:message key="gb.edit">[edit]</wicket:message></a> | <a wicket:id="deleteRepository"><wicket:message key="gb.delete">[delete]</wicket:message></a></span>
- </wicket:fragment>
-
- <wicket:fragment wicket:id="repositoryOwnerLinks">
- <span class="link"><a wicket:id="editRepository"><wicket:message key="gb.edit">[edit]</wicket:message></a></span>
- </wicket:fragment>
-
<wicket:fragment wicket:id="flatRepositoryHeader">
<tr>
<th class="left" wicket:id="orderByRepository">
@@ -60,8 +52,7 @@ <th class="hidden-tablet hidden-phone" wicket:id="orderByOwner"><wicket:message key="gb.owner">Owner</wicket:message></th>
<th class="hidden-phone"></th>
<th wicket:id="orderByDate"><wicket:message key="gb.lastChange">Last Change</wicket:message></th>
- <th class="hidden-phone"></th>
- <th class="right"></th>
+ <th class="right hidden-phone"></th>
</tr>
</wicket:fragment>
@@ -75,8 +66,7 @@ <th class="hidden-tablet hidden-phone"><span><wicket:message key="gb.owner">Owner</wicket:message></span></th>
<th class="hidden-phone"></th>
<th><wicket:message key="gb.lastChange">Last Change</wicket:message></th>
- <th class="hidden-phone"></th>
- <th class="right"></th>
+ <th class="right hidden-phone"></th>
</tr>
</wicket:fragment>
@@ -91,15 +81,7 @@ <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><span wicket:id="repositoryLastChange">[last change]</span></td>
- <td class="hidden-phone" style="text-align: right;padding-right:15px;"><span style="font-size:0.8em;" wicket:id="repositorySize">[repository size]</span></td>
- <td class="rightAlign">
- <span class="hidden-phone">
- <span wicket:id="repositoryLinks"></span>
- <a style="text-decoration: none;" wicket:id="syndication" wicket:message="title:gb.feed">
- <img style="border:0px;vertical-align:middle;" src="feed_16x16.png"></img>
- </a>
- </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>
</wicket:panel>
diff --git a/src/main/java/com/gitblit/wicket/panels/RepositoriesPanel.java b/src/main/java/com/gitblit/wicket/panels/RepositoriesPanel.java index be5d960b..8573e1a6 100644 --- a/src/main/java/com/gitblit/wicket/panels/RepositoriesPanel.java +++ b/src/main/java/com/gitblit/wicket/panels/RepositoriesPanel.java @@ -15,7 +15,6 @@ */
package com.gitblit.wicket.panels;
-import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -32,7 +31,6 @@ import org.apache.wicket.extensions.markup.html.repeater.util.SortParam; import org.apache.wicket.extensions.markup.html.repeater.util.SortableDataProvider;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.link.BookmarkablePageLink;
-import org.apache.wicket.markup.html.link.ExternalLink;
import org.apache.wicket.markup.html.link.Link;
import org.apache.wicket.markup.html.panel.Fragment;
import org.apache.wicket.markup.repeater.Item;
@@ -47,15 +45,12 @@ import com.gitblit.Keys; import com.gitblit.models.ProjectModel;
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.UserModel;
-import com.gitblit.servlet.SyndicationServlet;
import com.gitblit.utils.ArrayUtils;
import com.gitblit.utils.ModelUtils;
import com.gitblit.utils.StringUtils;
import com.gitblit.wicket.GitBlitWebSession;
import com.gitblit.wicket.WicketUtils;
import com.gitblit.wicket.pages.BasePage;
-import com.gitblit.wicket.pages.EditRepositoryPage;
-import com.gitblit.wicket.pages.EmptyRepositoryPage;
import com.gitblit.wicket.pages.ProjectPage;
import com.gitblit.wicket.pages.RepositoriesPage;
import com.gitblit.wicket.pages.SummaryPage;
@@ -91,12 +86,12 @@ public class RepositoriesPanel extends BasePanel { setResponsePage(RepositoriesPage.class);
}
}.setVisible(app().settings().getBoolean(Keys.git.cacheRepositoryList, true)));
- managementLinks.add(new BookmarkablePageLink<Void>("newRepository", EditRepositoryPage.class));
+ managementLinks.add(new BookmarkablePageLink<Void>("newRepository", app().getNewRepositoryPage()));
add(managementLinks);
} else if (showManagement && user != null && user.canCreate()) {
// user can create personal repositories
managementLinks = new Fragment("managementPanel", "personalLinks", this);
- managementLinks.add(new BookmarkablePageLink<Void>("newRepository", EditRepositoryPage.class));
+ managementLinks.add(new BookmarkablePageLink<Void>("newRepository", app().getNewRepositoryPage()));
add(managementLinks);
} else {
// user has no management permissions
@@ -141,12 +136,11 @@ public class RepositoriesPanel extends BasePanel { Collections.sort(subModels);
groupedModels.addAll(subModels);
}
- dp = new RepositoriesProvider(groupedModels);
+ dp = new ListDataProvider<RepositoryModel>(groupedModels);
} else {
dp = new SortableRepositoriesProvider(models);
}
- final String baseUrl = WicketUtils.getGitblitURL(getRequest());
final boolean showSwatch = app().settings().getBoolean(Keys.web.repositoryListSwatches, true);
DataView<RepositoryModel> dataView = new DataView<RepositoryModel>("row", dp) {
@@ -208,15 +202,7 @@ public class RepositoriesPanel extends BasePanel { swatch.setVisible(showSwatch);
if (linksActive) {
- Class<? extends BasePage> linkPage;
- if (entry.hasCommits) {
- // repository has content
- linkPage = SummaryPage.class;
- } else {
- // new/empty repository OR proposed repository
- linkPage = EmptyRepositoryPage.class;
- }
-
+ Class<? extends BasePage> linkPage = SummaryPage.class;
PageParameters pp = WicketUtils.newRepositoryParameter(entry.name);
row.add(new LinkPanel("repositoryName", "list", repoName, linkPage, pp));
row.add(new LinkPanel("repositoryDescription", "list", entry.description,
@@ -320,48 +306,6 @@ public class RepositoriesPanel extends BasePanel { WicketUtils.setHtmlTooltip(lastChangeLabel, getString("gb.author") + ": " + entry.lastChangeAuthor);
}
- boolean showOwner = user != null && entry.isOwner(user.username);
- boolean myPersonalRepository = showOwner && entry.isUsersPersonalRepository(user.username);
- if (showAdmin || myPersonalRepository) {
- Fragment repositoryLinks = new Fragment("repositoryLinks",
- "repositoryAdminLinks", this);
- repositoryLinks.add(new BookmarkablePageLink<Void>("editRepository",
- EditRepositoryPage.class, WicketUtils
- .newRepositoryParameter(entry.name)));
- Link<Void> deleteLink = new Link<Void>("deleteRepository") {
-
- private static final long serialVersionUID = 1L;
-
- @Override
- public void onClick() {
- if (app().repositories().deleteRepositoryModel(entry)) {
- if (dp instanceof SortableRepositoriesProvider) {
- info(MessageFormat.format(getString("gb.repositoryDeleted"), entry));
- ((SortableRepositoriesProvider) dp).remove(entry);
- } else {
- setResponsePage(getPage().getClass(), getPage().getPageParameters());
- }
- } else {
- error(MessageFormat.format(getString("gb.repositoryDeleteFailed"), entry));
- }
- }
- };
- deleteLink.add(new JavascriptEventConfirmation("onclick", MessageFormat.format(
- getString("gb.deleteRepository"), entry)));
- repositoryLinks.add(deleteLink);
- row.add(repositoryLinks);
- } else if (showOwner) {
- Fragment repositoryLinks = new Fragment("repositoryLinks",
- "repositoryOwnerLinks", this);
- repositoryLinks.add(new BookmarkablePageLink<Void>("editRepository",
- EditRepositoryPage.class, WicketUtils
- .newRepositoryParameter(entry.name)));
- row.add(repositoryLinks);
- } else {
- row.add(new Label("repositoryLinks"));
- }
- row.add(new ExternalLink("syndication", SyndicationServlet.asLink(baseUrl,
- entry.name, null, 0)).setVisible(linksActive));
WicketUtils.setAlternatingBackground(item, counter);
counter++;
}
@@ -418,59 +362,6 @@ public class RepositoriesPanel extends BasePanel { };
}
- private static class RepositoriesProvider extends ListDataProvider<RepositoryModel> {
-
- private static final long serialVersionUID = 1L;
-
- public RepositoriesProvider(List<RepositoryModel> list) {
- super(list);
- }
-
- @Override
- public List<RepositoryModel> getData() {
- return super.getData();
- }
-
- public void remove(RepositoryModel model) {
- int index = getData().indexOf(model);
- RepositoryModel groupModel = null;
- if (index == (getData().size() - 1)) {
- // last element
- if (index > 0) {
- // previous element is group header, then this is last
- // repository in group. remove group too.
- if (getData().get(index - 1) instanceof GroupRepositoryModel) {
- groupModel = getData().get(index - 1);
- }
- }
- } else if (index < (getData().size() - 1)) {
- // not last element. check next element for group match.
- if (getData().get(index - 1) instanceof GroupRepositoryModel
- && getData().get(index + 1) instanceof GroupRepositoryModel) {
- // repository is sandwiched by group headers so this
- // repository is the only element in the group. remove
- // group.
- groupModel = getData().get(index - 1);
- }
- }
-
- if (groupModel == null) {
- // Find the group and decrement the count
- for (int i = index; i >= 0; i--) {
- if (getData().get(i) instanceof GroupRepositoryModel) {
- ((GroupRepositoryModel) getData().get(i)).count--;
- break;
- }
- }
- } else {
- // Remove the group header
- getData().remove(groupModel);
- }
-
- getData().remove(model);
- }
- }
-
private static class SortableRepositoriesProvider extends SortableDataProvider<RepositoryModel> {
private static final long serialVersionUID = 1L;
@@ -482,10 +373,6 @@ public class RepositoriesPanel extends BasePanel { setSort(SortBy.date.name(), false);
}
- public void remove(RepositoryModel model) {
- list.remove(model);
- }
-
@Override
public int size() {
if (list == null) {
diff --git a/src/main/java/com/gitblit/wicket/panels/RepositoryNamePanel.html b/src/main/java/com/gitblit/wicket/panels/RepositoryNamePanel.html new file mode 100644 index 00000000..6fb6e45c --- /dev/null +++ b/src/main/java/com/gitblit/wicket/panels/RepositoryNamePanel.html @@ -0,0 +1,30 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+
+<body>
+<wicket:panel>
+
+ <table class="plain">
+ <tbody class="settings">
+ <tr>
+ <th><wicket:message key="gb.project"></wicket:message></th>
+ <th><wicket:message key="gb.name"></wicket:message></th>
+ </tr>
+ <tr>
+ <td><select class="span2" wicket:id="projectPath" /></td>
+ <td class="edit"><input class="span4" type="text" wicket:id="name" id="name" /> <span class="help-inline"><wicket:message key="gb.nameDescription"></wicket:message></span></td>
+ </tr>
+ </tbody>
+ </table>
+
+ <div>
+ <b><wicket:message key="gb.description"></wicket:message></b><br/>
+ <input class="span5" type="text" wicket:id="description" />
+ </div>
+
+</wicket:panel>
+</body>
+</html>
\ No newline at end of file diff --git a/src/main/java/com/gitblit/wicket/panels/RepositoryNamePanel.java b/src/main/java/com/gitblit/wicket/panels/RepositoryNamePanel.java new file mode 100644 index 00000000..f381a45e --- /dev/null +++ b/src/main/java/com/gitblit/wicket/panels/RepositoryNamePanel.java @@ -0,0 +1,172 @@ +/*
+ * 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.wicket.panels;
+
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.apache.wicket.markup.html.form.DropDownChoice;
+import org.apache.wicket.markup.html.form.TextField;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+
+import com.gitblit.models.ProjectModel;
+import com.gitblit.models.RepositoryModel;
+import com.gitblit.models.UserModel;
+import com.gitblit.utils.StringUtils;
+import com.gitblit.wicket.GitBlitWebSession;
+
+/**
+ * A panel for naming a repository, specifying it's project, and entering a description.
+ *
+ * @author James Moger
+ *
+ */
+public class RepositoryNamePanel extends BasePanel {
+
+ private static final long serialVersionUID = 1L;
+
+ private String fullName;
+
+ private final IModel<String> projectPath;
+
+ private DropDownChoice<String> pathChoice;
+
+ private final IModel<String> repoName;
+
+ private TextField<String> nameField;
+
+ public RepositoryNamePanel(String wicketId, RepositoryModel repository) {
+ super(wicketId);
+
+ GitBlitWebSession session = GitBlitWebSession.get();
+ UserModel user = session.getUser();
+
+ // build project set for repository destination
+ String defaultPath = null;
+ String defaultName = null;
+ Set<String> pathNames = new TreeSet<String>();
+
+ // add the registered/known projects
+ for (ProjectModel project : app().projects().getProjectModels(user, false)) {
+ // TODO issue-351: user.canAdmin(project)
+ if (user.canAdmin()) {
+ if (project.isRoot) {
+ pathNames.add("/");
+ } else {
+ pathNames.add(project.name + "/");
+ }
+ }
+ }
+
+ // add the user's personal project namespace
+ if (user.canAdmin() || user.canCreate()) {
+ pathNames.add(user.getPersonalPath() + "/");
+ }
+
+ if (!StringUtils.isEmpty(repository.name)) {
+ // editing a repository name
+ // set the defaultProject to the current repository project
+ if (StringUtils.isEmpty(repository.projectPath)) {
+ defaultPath = "/";
+ defaultName = repository.name;
+ } else {
+ defaultPath = repository.projectPath + "/";
+ defaultName = repository.name.substring(defaultPath.length());
+ }
+ pathNames.add(defaultPath);
+ }
+
+ // if default project is not already set, set preference based on the user permissions
+ if (defaultPath == null) {
+ if (user.canAdmin()) {
+ defaultPath = "/";
+ } else if (user.canCreate()) {
+ defaultPath = user.getPersonalPath() + "/";
+ }
+ }
+
+ projectPath = Model.of(defaultPath);
+ pathChoice = new DropDownChoice<String>("projectPath", projectPath, new ArrayList<String>(pathNames));
+ repoName = Model.of(defaultName);
+ nameField = new TextField<String>("name", repoName);
+
+ // only enable project selection if we actually have multiple choices
+ add(pathChoice.setEnabled(pathNames.size() > 1));
+ add(nameField);
+ add(new TextField<String>("description"));
+ }
+
+ public void setEditable(boolean editable) {
+ // only enable project selection if we actually have multiple choices
+ pathChoice.setEnabled(pathChoice.getChoices().size() > 1 && editable);
+ nameField.setEnabled(editable);
+ }
+
+ public boolean updateModel(RepositoryModel repositoryModel) {
+ // confirm a project path was selected
+ if (StringUtils.isEmpty(projectPath.getObject())) {
+ error(getString("gb.pleaseSelectProject"));
+ return false;
+ }
+
+ // confirm a repository name was entered
+ if (StringUtils.isEmpty(repoName.getObject())) {
+ error(getString("gb.pleaseSetRepositoryName"));
+ return false;
+ }
+
+ String project = projectPath.getObject();
+ String name = repoName.getObject();
+
+ fullName = (project + name).trim();
+ fullName = fullName.replace('\\', '/');
+ fullName = fullName.replace("//", "/");
+ if (fullName.charAt(0) == '/') {
+ fullName = fullName.substring(1);
+ }
+ if (fullName.endsWith("/")) {
+ fullName = fullName.substring(0, fullName.length() - 1);
+ }
+
+ if (fullName.contains("../")) {
+ error(getString("gb.illegalRelativeSlash"));
+ return false;
+ }
+ if (fullName.contains("/../")) {
+ error(getString("gb.illegalRelativeSlash"));
+ return false;
+ }
+
+ // confirm valid characters in repository name
+ Character c = StringUtils.findInvalidCharacter(fullName);
+ if (c != null) {
+ error(MessageFormat.format(getString("gb.illegalCharacterRepositoryName"), c));
+ return false;
+ }
+
+ repositoryModel.name = fullName;
+
+ return true;
+ }
+
+ @Override
+ protected boolean getStatelessHint() {
+ return false;
+ }
+}
\ No newline at end of file diff --git a/src/main/java/com/gitblit/wicket/panels/SshKeysPanel.html b/src/main/java/com/gitblit/wicket/panels/SshKeysPanel.html new file mode 100644 index 00000000..d67b704a --- /dev/null +++ b/src/main/java/com/gitblit/wicket/panels/SshKeysPanel.html @@ -0,0 +1,46 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+
+<body>
+<wicket:panel>
+ <h4><wicket:message key="gb.sshKeys"></wicket:message></h4>
+ <p><wicket:message key="gb.sshKeysDescription"></wicket:message></p>
+ <hr />
+
+ <div wicket:id="keys">
+ <div style="display:inline-block;font-size:2em;padding:10px;">
+ <i class="fa fa-key"></i>
+ </div>
+ <div style="display:inline-block;">
+ <div wicket:id="comment" style="font-weight:bold;"></div>
+ <pre wicket:id="fingerprint"></pre>
+ </div>
+
+ <div style="display:inline-block;padding: 0px 20px">
+ <div wicket:id="permission" style="font-weight:bold;"></div>
+ <div wicket:id="algorithm"></div>
+ </div>
+
+ <div style="display:inline-block;vertical-align:text-bottom;">
+ <button class="btn btn-danger" wicket:id="delete"><wicket:message key="gb.delete"></wicket:message></button>
+ </div>
+
+ <hr />
+ </div>
+
+ <div class="well">
+ <form wicket:id="addKeyForm">
+ <h4><wicket:message key="gb.addSshKey"></wicket:message></h4>
+ <div wicket:id="addKeyData"></div>
+ <div wicket:id="addKeyPermission"></div>
+ <div wicket:id="addKeyComment"></div>
+
+ <div class="form-actions"><input class="btn btn-appmenu" type="submit" value="Add" wicket:message="value:gb.add" wicket:id="addKeyButton" /></div>
+ </form>
+ </div>
+</wicket:panel>
+</body>
+</html>
\ No newline at end of file diff --git a/src/main/java/com/gitblit/wicket/panels/SshKeysPanel.java b/src/main/java/com/gitblit/wicket/panels/SshKeysPanel.java new file mode 100644 index 00000000..15ebd67b --- /dev/null +++ b/src/main/java/com/gitblit/wicket/panels/SshKeysPanel.java @@ -0,0 +1,169 @@ +/*
+ * 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.wicket.panels;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.markup.html.AjaxLink;
+import org.apache.wicket.ajax.markup.html.form.AjaxButton;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.markup.repeater.Item;
+import org.apache.wicket.markup.repeater.data.DataView;
+import org.apache.wicket.markup.repeater.data.ListDataProvider;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+
+import com.gitblit.Constants.AccessPermission;
+import com.gitblit.models.UserModel;
+import com.gitblit.transport.ssh.SshKey;
+import com.gitblit.utils.StringUtils;
+import com.gitblit.wicket.GitBlitWebSession;
+
+
+/**
+ * A panel that enumerates and manages SSH public keys using AJAX.
+ *
+ * @author James Moger
+ *
+ */
+public class SshKeysPanel extends BasePanel {
+
+ private static final long serialVersionUID = 1L;
+
+ private final UserModel user;
+
+ public SshKeysPanel(String wicketId, UserModel user) {
+ super(wicketId);
+
+ this.user = user;
+ }
+
+ @Override
+ protected void onInitialize() {
+ super.onInitialize();
+
+ setOutputMarkupId(true);
+
+ final List<SshKey> keys = new ArrayList<SshKey>(app().keys().getKeys(user.username));
+ final ListDataProvider<SshKey> dp = new ListDataProvider<SshKey>(keys);
+ final DataView<SshKey> keysView = new DataView<SshKey>("keys", dp) {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public void populateItem(final Item<SshKey> item) {
+ final SshKey key = item.getModelObject();
+ item.add(new Label("comment", key.getComment()));
+ item.add(new Label("fingerprint", key.getFingerprint()));
+ item.add(new Label("permission", key.getPermission().toString()));
+ item.add(new Label("algorithm", key.getAlgorithm()));
+
+ AjaxLink<Void> delete = new AjaxLink<Void>("delete") {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public void onClick(AjaxRequestTarget target) {
+ if (app().keys().removeKey(user.username, key)) {
+ // reset the keys list
+ keys.clear();
+ keys.addAll(app().keys().getKeys(user.username));
+
+ // update the panel
+ target.addComponent(SshKeysPanel.this);
+ }
+ }
+ };
+ item.add(delete);
+ }
+ };
+ add(keysView);
+
+ Form<Void> addKeyForm = new Form<Void>("addKeyForm");
+
+ final IModel<String> keyData = Model.of("");
+ addKeyForm.add(new TextAreaOption("addKeyData",
+ getString("gb.key"),
+ null,
+ "span5",
+ keyData));
+
+ final IModel<AccessPermission> keyPermission = Model.of(AccessPermission.PUSH);
+ addKeyForm.add(new ChoiceOption<AccessPermission>("addKeyPermission",
+ getString("gb.permission"),
+ getString("gb.sshKeyPermissionDescription"),
+ keyPermission,
+ Arrays.asList(AccessPermission.SSHPERMISSIONS)));
+
+ final IModel<String> keyComment = Model.of("");
+ addKeyForm.add(new TextOption("addKeyComment",
+ getString("gb.comment"),
+ getString("gb.sshKeyCommentDescription"),
+ "span5",
+ keyComment));
+
+ addKeyForm.add(new AjaxButton("addKeyButton") {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
+
+ UserModel user = GitBlitWebSession.get().getUser();
+ String data = keyData.getObject();
+ if (StringUtils.isEmpty(data)) {
+ // do not submit empty key
+ return;
+ }
+
+ SshKey key = new SshKey(data);
+ try {
+ key.getPublicKey();
+ } catch (Exception e) {
+ // failed to parse the key
+ return;
+ }
+
+ AccessPermission permission = keyPermission.getObject();
+ key.setPermission(permission);
+
+ String comment = keyComment.getObject();
+ if (!StringUtils.isEmpty(comment)) {
+ key.setComment(comment);
+ }
+
+ if (app().keys().addKey(user.username, key)) {
+ // reset add key fields
+ keyData.setObject("");
+ keyPermission.setObject(AccessPermission.PUSH);
+ keyComment.setObject("");
+
+ // reset the keys list
+ keys.clear();
+ keys.addAll(app().keys().getKeys(user.username));
+
+ // update the panel
+ target.addComponent(SshKeysPanel.this);
+ }
+ }
+ });
+
+ add(addKeyForm);
+ }
+}
diff --git a/src/main/java/com/gitblit/wicket/panels/TagsPanel.java b/src/main/java/com/gitblit/wicket/panels/TagsPanel.java index 9f3987be..f1f82739 100644 --- a/src/main/java/com/gitblit/wicket/panels/TagsPanel.java +++ b/src/main/java/com/gitblit/wicket/panels/TagsPanel.java @@ -17,9 +17,11 @@ package com.gitblit.wicket.panels; import java.util.List;
+import org.apache.wicket.RequestCycle;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.link.BookmarkablePageLink;
+import org.apache.wicket.markup.html.link.ExternalLink;
import org.apache.wicket.markup.html.panel.Fragment;
import org.apache.wicket.markup.repeater.Item;
import org.apache.wicket.markup.repeater.data.DataView;
@@ -29,13 +31,13 @@ import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.Repository;
import com.gitblit.models.RefModel;
+import com.gitblit.servlet.RawServlet;
import com.gitblit.utils.JGitUtils;
import com.gitblit.utils.StringUtils;
import com.gitblit.wicket.WicketUtils;
import com.gitblit.wicket.pages.BlobPage;
import com.gitblit.wicket.pages.CommitPage;
import com.gitblit.wicket.pages.LogPage;
-import com.gitblit.wicket.pages.RawPage;
import com.gitblit.wicket.pages.TagPage;
import com.gitblit.wicket.pages.TagsPage;
import com.gitblit.wicket.pages.TreePage;
@@ -113,9 +115,10 @@ public class TagsPanel extends BasePanel { .newObjectParameter(repositoryName, entry.getReferencedObjectId()
.getName())));
- fragment.add(new BookmarkablePageLink<Void>("raw", RawPage.class, WicketUtils
- .newObjectParameter(repositoryName, entry.getReferencedObjectId()
- .getName())));
+ String contextUrl = RequestCycle.get().getRequest().getRelativePathPrefixToContextRoot();
+ String rawUrl = RawServlet.asLink(contextUrl, repositoryName, entry.displayName,
+ entry.getReferencedObjectId().getName());
+ fragment.add(new ExternalLink("raw", rawUrl));
item.add(fragment);
} else {
// TODO Tree Tag Object
diff --git a/src/main/java/com/gitblit/wicket/panels/TextAreaOption.html b/src/main/java/com/gitblit/wicket/panels/TextAreaOption.html new file mode 100644 index 00000000..bb7dc7c6 --- /dev/null +++ b/src/main/java/com/gitblit/wicket/panels/TextAreaOption.html @@ -0,0 +1,20 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+
+<body>
+<wicket:panel>
+ <div style="padding-top:4px;">
+ <div style="margin-bottom:1px;">
+ <b><span wicket:id="name"></span></b>
+ </div>
+ <label class="checkbox" style="color:#777;"> <span wicket:id="description"></span>
+ <p style="padding-top:5px;"><textarea rows="12" class="span5" wicket:id="text"></textarea></p>
+ </label>
+
+ </div>
+</wicket:panel>
+</body>
+</html>
\ No newline at end of file diff --git a/src/main/java/com/gitblit/wicket/panels/TextAreaOption.java b/src/main/java/com/gitblit/wicket/panels/TextAreaOption.java new file mode 100644 index 00000000..d2c74a06 --- /dev/null +++ b/src/main/java/com/gitblit/wicket/panels/TextAreaOption.java @@ -0,0 +1,54 @@ +/*
+ * 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.wicket.panels;
+
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.form.TextArea;
+import org.apache.wicket.model.IModel;
+
+import com.gitblit.utils.StringUtils;
+import com.gitblit.wicket.WicketUtils;
+
+/**
+ * A re-usable textarea option panel.
+ *
+ * title
+ * description
+ * [text
+ * area]
+ *
+ * @author James Moger
+ *
+ */
+public class TextAreaOption extends BasePanel {
+
+ private static final long serialVersionUID = 1L;
+
+ public TextAreaOption(String wicketId, String title, String description, IModel<String> model) {
+ this(wicketId, title, description, null, model);
+ }
+
+ public TextAreaOption(String wicketId, String title, String description, String css, IModel<String> model) {
+ super(wicketId);
+ add(new Label("name", title));
+ add(new Label("description", description).setVisible(!StringUtils.isEmpty(description)));
+ TextArea<String> tf = new TextArea<String>("text", model);
+ if (!StringUtils.isEmpty(css)) {
+ WicketUtils.setCssClass(tf, css);
+ }
+ add(tf);
+ }
+}
diff --git a/src/main/java/com/gitblit/wicket/panels/TextOption.html b/src/main/java/com/gitblit/wicket/panels/TextOption.html new file mode 100644 index 00000000..d14da2b1 --- /dev/null +++ b/src/main/java/com/gitblit/wicket/panels/TextOption.html @@ -0,0 +1,20 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+
+<body>
+<wicket:panel>
+ <div style="padding-top:4px;">
+ <div style="margin-bottom:1px;">
+ <b><span wicket:id="name"></span></b>
+ </div>
+ <label class="checkbox" style="color:#777;"> <span wicket:id="description"></span>
+ <p style="padding-top:5px;"><input class="span3" type="text" wicket:id="text" /></p>
+ </label>
+
+ </div>
+</wicket:panel>
+</body>
+</html>
\ No newline at end of file diff --git a/src/main/java/com/gitblit/wicket/panels/TextOption.java b/src/main/java/com/gitblit/wicket/panels/TextOption.java new file mode 100644 index 00000000..22370f3c --- /dev/null +++ b/src/main/java/com/gitblit/wicket/panels/TextOption.java @@ -0,0 +1,53 @@ +/*
+ * 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.wicket.panels;
+
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.form.TextField;
+import org.apache.wicket.model.IModel;
+
+import com.gitblit.utils.StringUtils;
+import com.gitblit.wicket.WicketUtils;
+
+/**
+ * A re-usable textfield option panel.
+ *
+ * title
+ * description
+ * [textfield]
+ *
+ * @author James Moger
+ *
+ */
+public class TextOption extends BasePanel {
+
+ private static final long serialVersionUID = 1L;
+
+ public TextOption(String wicketId, String title, String description, IModel<String> model) {
+ this(wicketId, title, description, null, model);
+ }
+
+ public TextOption(String wicketId, String title, String description, String css, IModel<String> model) {
+ super(wicketId);
+ add(new Label("name", title));
+ add(new Label("description", description).setVisible(!StringUtils.isEmpty(description)));
+ TextField<String> tf = new TextField<String>("text", model);
+ if (!StringUtils.isEmpty(css)) {
+ WicketUtils.setCssClass(tf, css);
+ }
+ add(tf);
+ }
+}
diff --git a/src/main/java/com/gitblit/wicket/panels/TicketListPanel.html b/src/main/java/com/gitblit/wicket/panels/TicketListPanel.html new file mode 100644 index 00000000..30f50367 --- /dev/null +++ b/src/main/java/com/gitblit/wicket/panels/TicketListPanel.html @@ -0,0 +1,55 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+
+<body>
+<wicket:panel>
+<table class="table tickets">
+ <tbody>
+ <tr wicket:id="row">
+ <td class="ticket-list-icon">
+ <i wicket:id="state"></i>
+ </td>
+ <td>
+ <span wicket:id="title">[title]</span> <span wicket:id="labels" style="font-weight: normal;color:white;"><span class="label" wicket:id="label"></span></span>
+ <div class="ticket-list-details">
+ <span style="padding-right: 10px;" class="hidden-phone">
+ <wicket:message key="gb.createdBy"></wicket:message>
+ <span style="padding: 0px 2px" wicket:id="createdBy">[createdBy]</span> <span class="date" wicket:id="createDate">[create date]</span>
+ </span>
+ <span wicket:id="indicators" style="white-space:nowrap;"><i wicket:id="icon"></i> <span style="padding-right:10px;" wicket:id="count"></span></span>
+ </div>
+ <div class="hidden-phone" wicket:id="updated"></div>
+ <div class="ticket-list-details"><span class="activitySwatch" wicket:id="ticketsLink">[tickets link]</span></div>
+ </td>
+ <td class="ticket-list-state">
+ <span class="badge badge-info" wicket:id="votes"></span>
+ </td>
+ <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-state">
+ <div wicket:id="status"></div>
+ </td>
+ <td class="indicators">
+ <div>
+ <b>#<span wicket:id="id">[id]</span></b>
+ </div>
+ <div wicket:id="responsible"></div>
+ </td>
+ </tr>
+ </tbody>
+</table>
+
+<wicket:fragment wicket:id="updatedFragment">
+ <div class="ticket-list-details">
+ <wicket:message key="gb.updatedBy"></wicket:message>
+ <span style="padding: 0px 2px" wicket:id="updatedBy">[updatedBy]</span> <span class="date" wicket:id="updateDate">[update date]</span>
+ </div>
+</wicket:fragment>
+
+</wicket:panel>
+</body>
+</html>
\ No newline at end of file diff --git a/src/main/java/com/gitblit/wicket/panels/TicketListPanel.java b/src/main/java/com/gitblit/wicket/panels/TicketListPanel.java new file mode 100644 index 00000000..1ca483ce --- /dev/null +++ b/src/main/java/com/gitblit/wicket/panels/TicketListPanel.java @@ -0,0 +1,242 @@ +/* + * 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.wicket.panels; + +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.List; + +import org.apache.wicket.PageParameters; +import org.apache.wicket.behavior.SimpleAttributeModifier; +import org.apache.wicket.markup.html.basic.Label; +import org.apache.wicket.markup.html.panel.Fragment; +import org.apache.wicket.markup.repeater.Item; +import org.apache.wicket.markup.repeater.data.DataView; +import org.apache.wicket.markup.repeater.data.ListDataProvider; +import org.eclipse.jgit.lib.Repository; + +import com.gitblit.Constants; +import com.gitblit.models.RepositoryModel; +import com.gitblit.models.UserModel; +import com.gitblit.tickets.QueryResult; +import com.gitblit.tickets.TicketLabel; +import com.gitblit.utils.ArrayUtils; +import com.gitblit.utils.BugtraqProcessor; +import com.gitblit.utils.StringUtils; +import com.gitblit.wicket.GitBlitWebSession; +import com.gitblit.wicket.TicketsUI; +import com.gitblit.wicket.TicketsUI.Indicator; +import com.gitblit.wicket.WicketUtils; +import com.gitblit.wicket.pages.TicketsPage; +import com.gitblit.wicket.pages.UserPage; + +/** + * + * The ticket list panel lists tickets in a table. + * + * @author James Moger + * + */ +public class TicketListPanel extends BasePanel { + + private static final long serialVersionUID = 1L; + + public TicketListPanel(String wicketId, List<QueryResult> list, final boolean showSwatch, final boolean showRepository) { + super(wicketId); + + final ListDataProvider<QueryResult> dp = new ListDataProvider<QueryResult>(list); + DataView<QueryResult> dataView = new DataView<QueryResult>("row", dp) { + private static final long serialVersionUID = 1L; + + @Override + protected void populateItem(Item<QueryResult> item) { + final QueryResult ticket = item.getModelObject(); + final RepositoryModel repository = app().repositories().getRepositoryModel(ticket.repository); + + if (showSwatch) { + // set repository color + String color = StringUtils.getColor(StringUtils.stripDotGit(repository.name)); + WicketUtils.setCssStyle(item, MessageFormat.format("border-left: 2px solid {0};", color)); + } + + PageParameters tp = WicketUtils.newObjectParameter(ticket.repository, "" + ticket.number); + + if (showRepository) { + String name = StringUtils.stripDotGit(ticket.repository); + PageParameters rp = WicketUtils.newOpenTicketsParameter(ticket.repository); + LinkPanel link = new LinkPanel("ticketsLink", null, name, TicketsPage.class, rp); + WicketUtils.setCssBackground(link, name); + item.add(link); + } else { + item.add(new Label("ticketsLink").setVisible(false)); + } + + item.add(TicketsUI.getStateIcon("state", ticket.type, ticket.status)); + item.add(new Label("id", "" + ticket.number)); + UserModel creator = app().users().getUserModel(ticket.createdBy); + if (creator != null) { + item.add(new LinkPanel("createdBy", null, creator.getDisplayName(), + UserPage.class, WicketUtils.newUsernameParameter(ticket.createdBy))); + } else { + item.add(new Label("createdBy", ticket.createdBy)); + } + item.add(WicketUtils.createDateLabel("createDate", ticket.createdAt, GitBlitWebSession + .get().getTimezone(), getTimeUtils(), false)); + + if (ticket.updatedAt == null) { + item.add(new Label("updated").setVisible(false)); + } else { + Fragment updated = new Fragment("updated", "updatedFragment", this); + UserModel updater = app().users().getUserModel(ticket.updatedBy); + if (updater != null) { + updated.add(new LinkPanel("updatedBy", null, updater.getDisplayName(), + UserPage.class, WicketUtils.newUsernameParameter(ticket.updatedBy))); + } else { + updated.add(new Label("updatedBy", ticket.updatedBy)); + } + updated.add(WicketUtils.createDateLabel("updateDate", ticket.updatedAt, GitBlitWebSession + .get().getTimezone(), getTimeUtils(), false)); + item.add(updated); + } + + item.add(new LinkPanel("title", "list subject", StringUtils.trimString( + ticket.title, Constants.LEN_SHORTLOG), TicketsPage.class, tp)); + + ListDataProvider<String> labelsProvider = new ListDataProvider<String>(ticket.getLabels()); + DataView<String> labelsView = new DataView<String>("labels", labelsProvider) { + private static final long serialVersionUID = 1L; + + @Override + public void populateItem(final Item<String> labelItem) { + BugtraqProcessor btp = new BugtraqProcessor(app().settings()); + Repository db = app().repositories().getRepository(repository.name); + String content = btp.processText(db, repository.name, labelItem.getModelObject()); + db.close(); + Label label = new Label("label", content); + label.setEscapeModelStrings(false); + TicketLabel tLabel = app().tickets().getLabel(repository, labelItem.getModelObject()); + String background = MessageFormat.format("background-color:{0};", tLabel.color); + label.add(new SimpleAttributeModifier("style", background)); + labelItem.add(label); + } + }; + item.add(labelsView); + + if (StringUtils.isEmpty(ticket.responsible)) { + item.add(new Label("responsible").setVisible(false)); + } else { + UserModel responsible = app().users().getUserModel(ticket.responsible); + if (responsible == null) { + responsible = new UserModel(ticket.responsible); + } + GravatarImage avatar = new GravatarImage("responsible", responsible.getDisplayName(), + responsible.emailAddress, null, 16, true); + avatar.setTooltip(getString("gb.responsible") + ": " + responsible.getDisplayName()); + item.add(avatar); + } + + // votes indicator + Label v = new Label("votes", "" + ticket.votesCount); + WicketUtils.setHtmlTooltip(v, getString("gb.votes")); + item.add(v.setVisible(ticket.votesCount > 0)); + + // watching indicator + item.add(new Label("watching").setVisible(ticket.isWatching(GitBlitWebSession.get().getUsername()))); + + // status indicator + String css = TicketsUI.getLozengeClass(ticket.status, true); + Label l = new Label("status", ticket.status.toString()); + WicketUtils.setCssClass(l, css); + item.add(l); + + // add the ticket indicators/icons + List<Indicator> indicators = new ArrayList<Indicator>(); + + // comments + if (ticket.commentsCount > 0) { + int count = ticket.commentsCount; + String pattern = getString("gb.nComments"); + if (count == 1) { + pattern = getString("gb.oneComment"); + } + indicators.add(new Indicator("fa fa-comment", count, pattern)); + } + + // participants + if (!ArrayUtils.isEmpty(ticket.participants)) { + int count = ticket.participants.size(); + if (count > 1) { + String pattern = getString("gb.nParticipants"); + indicators.add(new Indicator("fa fa-user", count, pattern)); + } + } + + // attachments + if (!ArrayUtils.isEmpty(ticket.attachments)) { + int count = ticket.attachments.size(); + String pattern = getString("gb.nAttachments"); + if (count == 1) { + pattern = getString("gb.oneAttachment"); + } + indicators.add(new Indicator("fa fa-file", count, pattern)); + } + + // patchset revisions + if (ticket.patchset != null) { + int count = ticket.patchset.commits; + String pattern = getString("gb.nCommits"); + if (count == 1) { + pattern = getString("gb.oneCommit"); + } + indicators.add(new Indicator("fa fa-code", count, pattern)); + } + + // milestone + if (!StringUtils.isEmpty(ticket.milestone)) { + indicators.add(new Indicator("fa fa-bullseye", ticket.milestone)); + } + + ListDataProvider<Indicator> indicatorsDp = new ListDataProvider<Indicator>(indicators); + DataView<Indicator> indicatorsView = new DataView<Indicator>("indicators", indicatorsDp) { + private static final long serialVersionUID = 1L; + + @Override + public void populateItem(final Item<Indicator> item) { + Indicator indicator = item.getModelObject(); + String tooltip = indicator.getTooltip(); + + Label icon = new Label("icon"); + WicketUtils.setCssClass(icon, indicator.css); + item.add(icon); + + if (indicator.count > 0) { + Label count = new Label("count", "" + indicator.count); + item.add(count.setVisible(!StringUtils.isEmpty(tooltip))); + } else { + item.add(new Label("count").setVisible(false)); + } + + WicketUtils.setHtmlTooltip(item, tooltip); + } + }; + item.add(indicatorsView); + } + }; + + add(dataView); + } +} + diff --git a/src/main/java/com/gitblit/wicket/panels/TicketSearchForm.java b/src/main/java/com/gitblit/wicket/panels/TicketSearchForm.java new file mode 100644 index 00000000..21bf1bab --- /dev/null +++ b/src/main/java/com/gitblit/wicket/panels/TicketSearchForm.java @@ -0,0 +1,78 @@ +/* + * 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.wicket.panels; + +import java.io.Serializable; +import java.text.MessageFormat; + +import org.apache.wicket.PageParameters; +import org.apache.wicket.markup.html.form.TextField; +import org.apache.wicket.model.IModel; +import org.apache.wicket.model.Model; +import org.apache.wicket.request.target.basic.RedirectRequestTarget; + +import com.gitblit.utils.StringUtils; +import com.gitblit.wicket.SessionlessForm; +import com.gitblit.wicket.WicketUtils; +import com.gitblit.wicket.pages.BasePage; + +public class TicketSearchForm extends SessionlessForm<Void> implements Serializable { + + private static final long serialVersionUID = 1L; + + private final String repositoryName; + + private final IModel<String> searchBoxModel; + + public TicketSearchForm(String id, String repositoryName, String text, + Class<? extends BasePage> pageClass, PageParameters params) { + + super(id, pageClass, params); + + this.repositoryName = repositoryName; + this.searchBoxModel = new Model<String>(text == null ? "" : text); + + TextField<String> searchBox = new TextField<String>("ticketSearchBox", searchBoxModel); + add(searchBox); + } + + @Override + protected + void onInitialize() { + super.onInitialize(); + WicketUtils.setHtmlTooltip(get("ticketSearchBox"), + MessageFormat.format(getString("gb.searchTicketsTooltip"), "")); + WicketUtils.setInputPlaceholder(get("ticketSearchBox"), getString("gb.searchTickets")); + } + + @Override + public void onSubmit() { + String searchString = searchBoxModel.getObject(); + if (StringUtils.isEmpty(searchString)) { + // redirect to self to avoid wicket page update bug + String absoluteUrl = getAbsoluteUrl(); + getRequestCycle().setRequestTarget(new RedirectRequestTarget(absoluteUrl)); + return; + } + + // use an absolute url to workaround Wicket-Tomcat problems with + // mounted url parameters (issue-111) + PageParameters params = WicketUtils.newRepositoryParameter(repositoryName); + params.add("s", searchString); + String absoluteUrl = getAbsoluteUrl(pageClass, params); + getRequestCycle().setRequestTarget(new RedirectRequestTarget(absoluteUrl)); + } +} diff --git a/src/main/java/com/gitblit/wicket/panels/UserTitlePanel.html b/src/main/java/com/gitblit/wicket/panels/UserTitlePanel.html new file mode 100644 index 00000000..432c880c --- /dev/null +++ b/src/main/java/com/gitblit/wicket/panels/UserTitlePanel.html @@ -0,0 +1,16 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+
+<body>
+<wicket:panel>
+<div style="display:inline-block;vertical-align:top;padding: 0px 2px 2px;"><img wicket:id="userGravatar"></img></div>
+ <div style="display:inline-block;">
+ <div style="font-size:1.5em;" wicket:id="userDisplayName"></div>
+ <div style="color:#888;font-size:1.2em;padding-top:4px;"><span wicket:id="userTitle"></span></div>
+</div>
+</wicket:panel>
+</body>
+</html>
\ No newline at end of file diff --git a/src/main/java/com/gitblit/wicket/panels/UserTitlePanel.java b/src/main/java/com/gitblit/wicket/panels/UserTitlePanel.java new file mode 100644 index 00000000..2bf5ee7b --- /dev/null +++ b/src/main/java/com/gitblit/wicket/panels/UserTitlePanel.java @@ -0,0 +1,32 @@ +/*
+ * 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.wicket.panels;
+
+import org.apache.wicket.markup.html.basic.Label;
+
+import com.gitblit.models.UserModel;
+
+public class UserTitlePanel extends BasePanel {
+
+ private static final long serialVersionUID = 1L;
+
+ public UserTitlePanel(String wicketId, UserModel user, String title) {
+ super(wicketId);
+ add(new GravatarImage("userGravatar", user, "gravatar", 36, false));
+ add(new Label("userDisplayName", user.getDisplayName()));
+ add(new Label("userTitle", title));
+ }
+}
diff --git a/src/main/java/pt.py b/src/main/java/pt.py index 3b35c167..f45baded 100644 --- a/src/main/java/pt.py +++ b/src/main/java/pt.py @@ -54,9 +54,9 @@ def fetch(args): # fetch all current ticket patchsets print("Fetching ticket patchsets from the '{}' repository".format(args.remote)) if args.quiet: - __call(['git', 'fetch', args.remote, '--quiet']) + __call(['git', 'fetch', '-p', args.remote, '--quiet']) else: - __call(['git', 'fetch', args.remote]) + __call(['git', 'fetch', '-p', args.remote]) else: # fetch specific patchset __resolve_patchset(args) @@ -206,6 +206,12 @@ def start(args): branches.append(branch.strip()) branch = 'topic/' + args.topic + try: + int(args.topic) + branch = 'ticket/' + args.topic + except ValueError: + pass + illegals = set(branches) & {'topic', branch} # ensure there are no local branch names that will interfere with branch creation @@ -246,6 +252,13 @@ def propose(args): push_ref = topic except ValueError: pass + if curr_branch.startswith('ticket/'): + topic = curr_branch[7:].strip() + try: + int(topic) + push_ref = topic + except ValueError: + pass if push_ref is None: push_ref = 'new' else: @@ -288,8 +301,8 @@ def propose(args): if fields[0] == 'remote' and fields[1].strip().startswith('--> #'): # set the upstream branch configuration args.id = int(fields[1].strip()[len('--> #'):]) - __call(['git', 'fetch', args.remote]) - __call(['git', 'branch', '--set-upstream-to={}/ticket/{:d}'.format(args.remote, args.id)]) + __call(['git', 'fetch', '-p', args.remote]) + __call(['git', 'branch', '-u', '{}/ticket/{:d}'.format(args.remote, args.id)]) break return diff --git a/src/site/openshift.mkd b/src/site/openshift.mkd deleted file mode 100644 index 63b1b007..00000000 --- a/src/site/openshift.mkd +++ /dev/null @@ -1,56 +0,0 @@ -## Gitblit on RedHat's OpenShift Cloud Service
-
-The Gitblit Express distribution can be copied to the root of your RedHat OpenShift
-application repository. Gitblit Express is an exploded WAR file with all appropriate
-dependencies bundled.
-
-You should delete the `pom.xml` file and the `src` folder from your application repository
-as Gitblit Express is not a source distribution to be built with Maven on OpenShift.
-
-Gitblit automatically adjusts itself to running on OpenShift. Repositories, users,
-federation proposals, setting overrides, and Groovy push scripts are stored in *OPENSHIFT_DATA_DIR*.
-
-It is recommended to enable all RPC settings in the `web.xml` file to allow remote
-administration and, more importantly, configuration of your Gitblit Express
-installation using the Gitblit Manager.
-
-It is also recommended to set *web.forwardSlashCharacter* to ! because OpenShift
-runs on JBoss/Tomcat behind a proxy, neither of which are friendly to embedded
-forward-slashes.
-
-Please do not change the following settings unless you know exactly what you are
-doing:
-
-- *git.repositoriesFolder*
-- *groovy.scriptsFolder*
-- *federation.proposalsFolder*
-- *realm.userService* (for standard users.conf)
-
-Additionally, it is recommended to force your Gitblit installation to cleanup up
-older versions on your OpenShift filesystem to maximize available space for your
-repositories.
-
-Append the following command to your ./openshift/action_hooks/build file:
-
- rm -fr $OPENSHIFT_APP_DIR/jbossas-7.0/standalone/tmp/vfs/*
-
-Lastly, you may want to play with the heap and permgen settings of your Gitblit
-instance because the default heap for the JVM is 95 MB, which may be a little
-tight.
-
-To do that you will have to login to your account via ssh:
-
- ssh hashcode@app-domain.rhcloud.com
-
-and then you will have to manipulate the -Xmx and -XX:MaxPermSize values.
-
- vi $OPENSHIFT_APP_DIR/jbossas-7.0/bin/standalone.conf
- ctl_app restart
-
-OpenShift currently allows 300MB of memory per application which includes ssh access, JVM, etc.
-The Gitblit demo hosted on OpenShift Express operates with -Xmx160m and -XX:MaxPermSize=90m.
-
-For more detailed instructions on how to setup and deploy an OpenShift application
-please see this excellent turorial:
-
-https://github.com/opensas/play-demo/wiki/Step-12.5---deploy-to-openshift
diff --git a/src/site/plugins_extensions.mkd b/src/site/plugins_extensions.mkd index 60f8b47d..0e066438 100644 --- a/src/site/plugins_extensions.mkd +++ b/src/site/plugins_extensions.mkd @@ -52,6 +52,37 @@ public class ExamplePlugin extends GitblitPlugin { public void onUninstall() { } } + +/** + * You can also create Webapp plugins that register pages. + */ +public class ExampleWicketPlugin extends GitblitWicketPlugin { + @Override + public void start() { + } + + @Override + public void stop() { + } + + @Override + public void onInstall() { + } + + @Override + public void onUpgrade(Version oldVersion) { + } + + @Override + public void onUninstall() { + } + + @Override + protected void init(GitblitWicketApp app) { + app.mount("/logo", LogoPage.class); + app.mount("/hello", HelloWorldPage.class); + } +} ``` ### SSH Dispatch Command @@ -185,3 +216,172 @@ public class MyTicketHook extends TicketHook { } ``` +### Request Filter + +*SINCE 1.6.0* + +You can provide your own custom request filter by subclassing the *HttpRequestFilter* class. + +```java +import com.gitblit.extensions.HttpRequestFilter; +import ro.fortsoft.pf4j.Extension; + +@Extension +public class MyRequestFilter extends HttpRequestFilter { + + @Override + public void doFilter(ServletRequest request, ServletResponse response, + FilterChain chain) throws IOException, ServletException { + } +} +``` + +### User Menu Items + +*SINCE 1.6.0* + +You can provide your own user menu items by subclassing the *UserMenuExtension* class. + +```java +import java.util.Arrays; +import java.util.List; +import ro.fortsoft.pf4j.Extension; +import com.gitblit.extensions.UserMenuExtension; +import com.gitblit.models.Menu.ExternalLinkMenuItem; +import com.gitblit.models.Menu.MenuItem; +import com.gitblit.models.UserModel; + +@Extension +public class MyUserMenuContributor extends UserMenuExtension { + + @Override + public List<MenuItem> getMenuItems(UserModel user) { + MenuItem item = new ExternalLinkMenuItem("Github", String.format("https://github.com/%s", user.username)); + return Arrays.asList(item); + } +} +``` + +### Navigation Links + +*SINCE 1.6.0* + +You can provide your own top-level navigation links by subclassing the *NavLinkExtension* class. + +```java +import java.util.Arrays; +import java.util.List; +import ro.fortsoft.pf4j.Extension; +import com.gitblit.extensions.NavLinkExtension; +import com.gitblit.models.UserModel; + +@Extension +public class MyNavLink extends NavLinkExtension { + + @Override + public List<NavLink> getNavLinks(UserModel user) { + NavLink link = new ExternalLinkMenuItem("Github", String.format("https://github.com/%s", user.username)); + return Arrays.asList(link); + } +} +``` + +### Server Lifecycle Listener + +*SINCE 1.6.0* + +You can provide a lifecycle listener to be notified when Gitblit has completely started and just before Gitblit is gracefully terminated. + +```java +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import ro.fortsoft.pf4j.Extension; +import com.gitblit.extensions.LifeCycleListener; + +@Extension +public class MyLifeCycleListener extends LifeCycleListener { + + final Logger log = LoggerFactory.getLogger(getClass()); + + @Override + public void onStartup() { + log.info("Gitblit is Ready!!"); + } + + @Override + public void onShutdown() { + log.info("Gitblit is Going Down!!"); + } +} +``` + +### Repository Lifecycle Listener + +*SINCE 1.6.0* + +You can provide a lifecycle listener to be notified when Gitblit has created or deleted a repository. + +```java +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import ro.fortsoft.pf4j.Extension; +import com.gitblit.extensions.RepositoryLifeCycleListener; +import com.gitblit.models.RepositoryModel; + +@Extension +public class MyRepoLifeCycleListener extends RepositoryLifeCycleListener { + + final Logger log = LoggerFactory.getLogger(getClass()); + + @Override + public void onCreation(RepositoryModel repo) { + log.info("Gitblit created {}", repo); + } + + @Override + public void onDeletion(RepositoryModel repo) { + log.info("Gitblit deleted {}", repo); + } +} +``` + +### User/Team Lifecycle Listener + +*SINCE 1.6.0* + +You can provide a lifecycle listener to be notified when Gitblit has created or deleted a user or a team. + +```java +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import ro.fortsoft.pf4j.Extension; +import com.gitblit.extensions.UserTeamLifeCycleListener; +import com.gitblit.models.TeamModel; +import com.gitblit.models.UserModel; + +@Extension +public class MyUserTeamLifeCycleListener extends UserTeamLifeCycleListener { + + final Logger log = LoggerFactory.getLogger(getClass()); + + @Override + public void onCreation(UserModel user) { + log.info("Gitblit created user {}", user); + } + + @Override + public void onDeletion(UserModel user) { + log.info("Gitblit deleted user {}", user); + } + + @Override + public void onCreation(TeamModel team) { + log.info("Gitblit created team {}", team); + } + + @Override + public void onDeletion(TeamModel team) { + log.info("Gitblit deleted team {}", team); + } +} +``` diff --git a/src/site/plugins_overview.mkd b/src/site/plugins_overview.mkd index 43f3a12a..f7872fdb 100644 --- a/src/site/plugins_overview.mkd +++ b/src/site/plugins_overview.mkd @@ -45,6 +45,8 @@ Administrators may manage plugins through the `plugin` SSH dispatch command: Through this command interface plugins can be started, stopped, disabled, enabled, installed, uninstalled, listed, etc. Each command is supports the `--help` argument which will guide you in understanding the options and usage of the command. +You may watch an Asciinema screencast of how to use the SSH transport and the plugin manager [here](https://asciinema.org/a/9342). + ### Default Plugin Registry Gitblit provides a simple default registry of plugins. The registry is a JSON file and it lists plugin metadata and download locations. @@ -62,13 +64,3 @@ If you develop your own plugins that you want hosted by or linked in the default The `plugins.json` file is parameterized with the `${self}` placeholder. This parameter is substituted on download with with the source URL of the registry file. This allows you to clone and serve your own copy of this git repository or just serve your own `plugins.json` on your own network. Gitblit also supports loading multiple plugin registries. Just place another **properly formatted** `.json` file in `${baseFolder}/plugins` and Gitblit will load that as an additional registry. - -### Mac OSX Fonts - -Gitblit's core SSH commands and those in the *powertools* plugin rely on ANSI border characters to provide a pretty presentation of data. Unfortunately, the fonts provided by Apple - while very nice - don't work well with ANSI border characters. The following public domain fixed-width, fixed-point, bitmapped fonts work very nicely. I find the 6x12 font with a line spacing of ~0.8 to be quite acceptable. - -[6x12.dfont](6x12.dfont) -[6x13.dfont](6x13.dfont) -[7x13.dfont](7x13.dfont) -[7x14.dfont](7x14.dfont) - diff --git a/src/site/roadmap.mkd b/src/site/roadmap.mkd index 03d69dfb..ea643218 100644 --- a/src/site/roadmap.mkd +++ b/src/site/roadmap.mkd @@ -3,7 +3,6 @@ This is not exactly a formal roadmap but it is a priority list of what might be implemented in future releases.
This list is volatile and may not reflect what will be in the next release.
-* **In-Progress**: Integrate an SSH daemon (issue-369)
-* Diff should highlight inserted/removed fragment compared to original line
-* Respect Gerrit branch permissions, if found (issue 36)
-
+* Add support for Project owners/administrators (ticket-75)
+* Add Project create/update pages
+* Integrate improvements for git-flow (ticket-55)
diff --git a/src/site/rpc.mkd b/src/site/rpc.mkd index b86fd9ad..302084fb 100644 --- a/src/site/rpc.mkd +++ b/src/site/rpc.mkd @@ -32,6 +32,7 @@ The Gitblit API includes methods for retrieving and interpreting RSS feeds. The <tr><th>url parameter</th><th>default</th><th>description</th></tr>
<tr><td colspan='3'><b>standard query</b></td></tr>
<tr><td><em>repository</em></td><td><em>required</em></td><td>repository name is part of the url (see examples below)</td></tr>
+<tr><td>ot=</td><td><em>optional</em><br/>default: COMMIT</td><td>object type to return in results. COMMIT or TAG</td></tr>
<tr><td>h=</td><td><em>optional</em><br/>default: HEAD</td><td>starting branch, ref, or commit id</td></tr>
<tr><td>l=</td><td><em>optional</em><br/>default: web.syndicationEntries</td><td>maximum return count</td></tr>
<tr><td>pg=</td><td><em>optional</em><br/>default: 0</td><td>page number for paging<br/>(offset into history = pagenumber*maximum return count)</td></tr>
@@ -57,9 +58,10 @@ The Gitblit API includes methods for retrieving and interpreting RSS feeds. The <tr><td>Gitblit v0.8.0</td><td>2</td></tr>
<tr><td>Gitblit v0.9.0 - v1.0.0</td><td>3</td></tr>
<tr><td>Gitblit v1.1.0</td><td>4</td></tr>
-<tr><td>Gitblit v1.2.0+</td><td>5</td></tr>
-<tr><td>Gitblit v1.3.1+</td><td>6</td></tr>
-<tr><td>Gitblit v1.4.0+</td><td>7</td></tr>
+<tr><td>Gitblit v1.2.0</td><td>5</td></tr>
+<tr><td>Gitblit v1.3.1</td><td>6</td></tr>
+<tr><td>Gitblit v1.4.0</td><td>7</td></tr>
+<tr><td>Gitblit v1.6.0</td><td>8</td></tr>
</tbody>
</table>
@@ -81,6 +83,7 @@ Use *SET_REPOSITORY_TEAM_PERMISSIONS* instead. <tr><td>LIST_BRANCHES</td><td>-</td><td>-</td><td>1</td><td>-</td><td>Map<String, List<String>></td></tr>
<tr><td>LIST_SETTINGS</td><td>-</td><td><em>-</em></td><td>1</td><td>-</td><td>ServerSettings (basic keys)</td></tr>
<tr><td>GET_USER</td><td>user name</td><td>-</td><td>6</td><td>-</td><td>UserModel</td></tr>
+<tr><td>FORK_REPOSITORY</td><td>repository name</td><td><em>-</em></td><td>8</td><td>-</td><td>-</td></tr>
<tr><td colspan='6'><em>web.enableRpcManagement=true</em></td></tr>
<tr><td>CREATE_REPOSITORY</td><td>repository name</td><td><em>admin</em></td><td>1</td><td>RepositoryModel</td><td>-</td></tr>
<tr><td>EDIT_REPOSITORY</td><td>repository name</td><td><em>admin</em></td><td>1</td><td>RepositoryModel</td><td>-</td></tr>
diff --git a/src/site/setup_express.mkd b/src/site/setup_express.mkd deleted file mode 100644 index cf3ef659..00000000 --- a/src/site/setup_express.mkd +++ /dev/null @@ -1,64 +0,0 @@ -## Gitblit on RedHat's OpenShift Cloud Service
-
-The Gitblit Express distribution can be copied to the root of your RedHat OpenShift
-application repository. Gitblit Express is an exploded WAR file with all appropriate
-dependencies bundled.
-
-You should delete the `pom.xml` file and the `src` folder from your application repository
-as Gitblit Express is not a source distribution to be built with Maven on OpenShift.
-
-Gitblit automatically adjusts itself to running on OpenShift. Repositories, users,
-federation proposals, setting overrides, and Groovy push scripts are stored in *OPENSHIFT_DATA_DIR*.
-
-### Recommended Settings
-
-You should disable the git daemon by setting *git.daemonPort=0*.
-
-It is recommended to enable all RPC settings in the `web.xml` file to allow remote
-administration and, more importantly, configuration of your Gitblit Express
-installation using the Gitblit Manager.
-
-It is also recommended to set *web.forwardSlashCharacter* to ! because OpenShift
-runs on JBoss/Tomcat behind a proxy, neither of which are friendly to embedded
-forward-slashes.
-
-Please do not change the following settings unless you know exactly what you are
-doing:
-
-- *git.repositoriesFolder*
-- *groovy.scriptsFolder*
-- *federation.proposalsFolder*
-- *realm.userService* (for standard users.conf)
-
-### Native Git Failures
-
-Unfortunately, sometime in early 2013 RedHat changed their SSL certificate such that cloning from/pushing to Gitblit Express over https fails due to an SNI (server name indication) TLS alert. There is no known workaround for native git and https.
-
-However, if your git client is JGit-based, like Eclipse/EGit, then you can workaround this problem and happily push/clone using https and OpenShift.
-
-Luckily, Java 6-based clients ignore SNI alerts but when using Java 7-based clients, SNI checking is enabled by default. You can disable SNI alerts by specifying the JVM system parameter `-Djsse.enableSNIExtension=false` when your Java-based client launches.
-
-For Eclipse, you can append `-Djsse.enableSNIExtension=false` to your *eclipse.ini* file.
-
-### Heap and PermGen
-
-You may want to play with the heap and permgen settings of your Gitblit
-instance because the default heap for the JVM is 95 MB, which may be a little
-tight.
-
-To do that you will have to login to your account via ssh:
-
- ssh hashcode@app-domain.rhcloud.com
-
-and then you will have to manipulate the -Xmx and -XX:MaxPermSize values.
-
- vi $OPENSHIFT_APP_DIR/jbossas-7.0/bin/standalone.conf
- ctl_app restart
-
-OpenShift currently allows 300MB of memory per application which includes ssh access, JVM, etc.
-The Gitblit demo hosted on OpenShift Express operates with -Xmx160m and -XX:MaxPermSize=90m.
-
-For more detailed instructions on how to setup and deploy an OpenShift application
-please see this excellent turorial:
-
-[Deploying to OpenShift](https://github.com/opensas/play-demo/wiki/Step-12.5---deploy-to-openshift)
diff --git a/src/site/setup_transport_ssh.mkd b/src/site/setup_transport_ssh.mkd index 7b53624b..c18dac46 100644 --- a/src/site/setup_transport_ssh.mkd +++ b/src/site/setup_transport_ssh.mkd @@ -5,6 +5,8 @@ The SSH transport is a very exciting improvement to Gitblit. Aside from offering a simple password-less, public key workflow the SSH transport also allows exposes a new approach to interacting with Gitblit: SSH commands. The Gerrit and Android projects have to be thanked for providing great base SSH code that Gitblit has integrated.
+You may watch an Asciinema screencast of using the SSH transport and it's command infrastructure [here](https://asciinema.org/a/9342).
+
### Cloning & Pushing
By default, Gitblit serves the SSH transport on port 29418, which is the same as Gerrit. Why was 29418 chosen? It's likely because it resembles the IANA port assigned to the git protocol (9418).
@@ -21,14 +23,22 @@ First you'll need to create an SSH key pair, if you don't already have one or if ssh-keygen
+**NOTE:** It is important to note that *ssh-keygen* generates a public/private keypair (e.g. id_rsa and id_rsa.pub). You want to upload the *public* key, which is denoted by the *.pub* file extension.
+
+#### Uploading your public key from the command-line
+
Then you can upload your *public* key right from the command-line.
cat ~/.ssh/id_rsa.pub | ssh -l <username> -p 29418 <hostname> keys add
cat c:\<userfolder>\.ssh\id_rsa.pub | ssh -l <username> -p 29418 <hostname> keys add
-**NOTE:** It is important to note that *ssh-keygen* generates a public/private keypair (e.g. id_rsa and id_rsa.pub). You want to upload the *public* key, which is denoted by the *.pub* file extension.
+#### Uploading your public key through the browser
+
+1. Navigate to your *profile* page from the dropdown user menu.
+2. Click the *SSH Keys* tab and paste your public key into the *Add SSH Key* form.
+3. Click the *Save* button
-Once you've done both of those steps you should be able to execute the following command without a password prompt.
+Once you ave uploaded your public key you should be able to execute the following command without a password prompt.
ssh -l <username> -p 29418 <hostname>
@@ -90,4 +100,12 @@ You may control the access permission for each SSH key. This is more of a safet | R | SSH key may be used to clone/fetch |
| RW | SSH key may be used to clone/fetch and push |
+### Mac OSX Fonts
+
+Many of Gitblit's SSH commands rely on ANSI border characters to provide a pretty presentation of data. Unfortunately, the fonts provided by Apple - while very nice - don't work well with ANSI border characters. The following public domain fixed-width, fixed-point, bitmapped fonts work very nicely. I find the 6x12 font with a line spacing of ~0.8 to be quite acceptable.
+
+[6x12.dfont](6x12.dfont)
+[6x13.dfont](6x13.dfont)
+[7x13.dfont](7x13.dfont)
+[7x14.dfont](7x14.dfont)
diff --git a/src/site/siteindex.mkd b/src/site/siteindex.mkd index 88bb3f3b..58e18af1 100644 --- a/src/site/siteindex.mkd +++ b/src/site/siteindex.mkd @@ -1,14 +1,23 @@ <div class="well" style="margin-left:5px;float:right;width:275px;padding: 10px 10px;">
+<script>
+ (function(d, s, id) {
+ var js, fjs = d.getElementsByTagName(s)[0];
+ if (d.getElementById(id)) return;
+ js = d.createElement(s); js.id = id;
+ js.async = true;
+ js.src = "//go.jelastic.com/widgets.js";
+ fjs.parentNode.insertBefore(js, fjs);
+ }(document, 'script', 'jelastic-jssdk'));
+</script>
<div style="text-align:center">
<b>Current Release ${project.releaseVersion} (${project.releaseDate})</b><br/><a href="releasenotes.html">release notes</a>
<div style="padding:5px;"><a style="width:175px;text-decoration:none;" class="btn btn-success" href="%GCURL%gitblit-${project.releaseVersion}.zip">Download Gitblit GO (Windows)</a></div>
<div style="padding:5px;"><a style="width:175px;text-decoration:none;" class="btn btn-success" href="%GCURL%gitblit-${project.releaseVersion}.tar.gz">Download Gitblit GO (Linux/OSX)</a></div>
<div style="padding:5px;"><a style="width:175px;text-decoration:none;" class="btn btn-danger" href="%GCURL%gitblit-${project.releaseVersion}.war">Download Gitblit WAR</a></div>
-<div style="padding:5px;"><a style="width:175px;text-decoration:none;" class="btn btn-info" href="%GCURL%express-${project.releaseVersion}.zip">Download Gitblit Express</a></div>
<div style="padding:5px;"><a style="width:175px;text-decoration:none;" class="btn btn-primary" href="%GCURL%manager-${project.releaseVersion}.zip">Download Gitblit Manager</a></div>
<a href='https://bintray.com/gitblit/releases/gitblit/view?source=watch' alt='Get automatic notifications about new "stable" versions'><img src='https://www.bintray.com/docs/images/bintray_badge_color.png'></a>
</div>
-
+<div data-manifest="http://1c57d83a4c5f3a21ec25c050d4c5e37b.app.jelastic.com/xssu/cross/download/RDYYHABkAFJbUVlMMVU7RUtDARgATExFCEBuGS4jdQJKRUsEDwIBQmNTTEBI" data-width="280" data-theme="flat-blue" data-text="Get it hosted now!" data-tx-empty="Type your email and click the button" data-tx-invalid-email="Invalid email, please check the spelling" data-tx-error="An error has occurred, please try again later" data-tx-success="Check your email" class="je-app" ></div>
<div style="padding-top:5px;">
<table class="table condensed-table">
<tbody>
@@ -41,20 +50,25 @@ This is what you should download if you want to go from zero to Git in less than ### WAR: For Your Servlet Container
*Gitblit WAR* is what you should download if you already have a servlet container available that you wish to use. Jetty 6/7/8 and Tomcat 6/7 are known to work. Generally, any Servlet 2.5 or Servlet 3.0 container should work.
-### Express: For the Cloud
-*Gitblit Express* is a prepared distribution for [RedHat's OpenShift][rhcloud] cloud service.
-
### You decide how to use Gitblit
Gitblit can be used as a dumb repository viewer with no administrative controls or user accounts.
Gitblit can be used as a complete Git stack for cloning, pushing, and repository access control.
Gitblit can be used without any other Git tooling (including actual Git) or it can cooperate with your established tools.
+### All Transports
+
+The SSH, HTTP, & GIT protocols are supported and ready-to-go out of the box.
+
+### Issue tracking with branch-based pull requests
+
+Gitblit blends elements of GitHub, BitBucket, and Gerrit to provide a streamlined collaboration workflow based on branches within the primary repository.
+
### Easy Remote Management
Administrators can create and manage all repositories, user accounts, and teams from the *Web UI*.
Administrators can create and manage all repositories, user accounts, and teams from the *JSON RPC interface* using the [Gitblit Manager](http://code.google.com/p/gitblit/downloads/detail?name=%MANAGER%) or your own custom tooling.
-Administrators can create and manage all repositories, user accounts, and teams from the *command-line* using the [Powertools plugin](https://github.com/gitblit/gitblit-powertools-plugin).
+Administrators can create and manage all repositories, user accounts, and teams from the *command-line* using SSH & the [Powertools plugin](https://github.com/gitblit/gitblit-powertools-plugin).
### Integration with Your Infrastructure
@@ -81,4 +95,3 @@ Gitblit requires a Java 7 Runtime Environment (JRE) or a Java 7 Development Kit [jgit]: http://eclipse.org/jgit "Eclipse JGit Site"
[git]: http://git-scm.com "Official Git Site"
-[rhcloud]: https://openshift.redhat.com/app "RedHat OpenShift"
diff --git a/src/site/tickets_overview.mkd b/src/site/tickets_overview.mkd index 485ca06d..14e4ab99 100644 --- a/src/site/tickets_overview.mkd +++ b/src/site/tickets_overview.mkd @@ -114,36 +114,23 @@ The answer is, it's really not that different from a GitHub pull request. When Additionally, because the patchset is not linked to a user's personal fork it is possible to allow others to collaborate on development. -## Status - -The Tickets feature is highly functional but there are several areas which need further refinements. - -#### What is working +#### Features +- My Tickets page - Ticket creation and editing -- Ticket creation on patchset push +- Ticket creation on patchset push (proposal) +- Branch-based pull-requests - Comments with Markdown syntax support - Rich email notifications - Fast-forward patchset updates and patchset rewrites - Voting - Watching - Mentions -- Partial milestone support +- Milestones - Querying - Searching - Is Mergeable test on view ticket page load +- Server-side merge - Close-on-push of detected merge - Multiple backend choices -- Server-side merge (testing) - -#### TODO - -- Implement a My Tickets page (ticket-15) -- Ticket Lifecycle Hooks (ticket-16) -- CRUD pages for Milestones (ticket-17) -- Ticket service migration tool (ticket-19) -- Collapsible ticket description (e.g. AUI expander) -- Edit a comment -- Delete a comment -- REST API for tooling -- Client-side Markdown previews (AngularMarkdown?) + diff --git a/src/site/tickets_replication.mkd b/src/site/tickets_replication.mkd index 472c7270..60f12cf6 100644 --- a/src/site/tickets_replication.mkd +++ b/src/site/tickets_replication.mkd @@ -133,3 +133,27 @@ You can trigger a live reindex of tickets for any backend using Gitblit's RPC in curl --insecure --user admin:admin "https://localhost:8443/rpc?req=reindex_tickets" curl --insecure --user admin:admin "https://localhost:8443/rpc?req=reindex_tickets&name=gitblit.git" +#### Migrating Tickets between Ticket Services + +##### Gitblit GO + +Gitblit GO ships with a script that executes the *com.gitblit.MigrateTickets* tool included in the Gitblit jar file. This tool will migrate *all* tickets in *all* repositories **AND** must be run when Gitblit is offline. + + migrate-tickets <outputservice> <baseFolder> + +For example, this would migrate tickets from the current ticket service configured in `c:\gitblit-data\gitblit.properties` to a Redis ticket service. The Redis service is configured in the same config file so you must be sure to properly setup all appropriate Redis settings. + + migrate-tickets com.gitblit.tickets.RedisTicketService c:\gitblit-data + +##### Gitblit WAR + +Gitblit WAR does not ship with anything other than the WAR, but you can still migrate tickets offline with a little extra effort. + +*Windows* + + java -cp "C:/path/to/WEB-INF/lib/*" com.gitblit.MigrateTickets <outputservice> --baseFolder <baseFolder> + +*Linux/Unix/Mac OSX* + + java -cp /path/to/WEB-INF/lib/* com.gitblit.MigrateTickets <outputservice> --baseFolder <baseFolder> + diff --git a/src/site/tickets_setup.mkd b/src/site/tickets_setup.mkd index aeecbeb1..f70d75cd 100644 --- a/src/site/tickets_setup.mkd +++ b/src/site/tickets_setup.mkd @@ -109,7 +109,7 @@ Gitblit has a simple review scoring mechanism designed to indicate overall impre #### Milestones -Milestones are a way to group tickets together. Currently milestones are specified at the repository level and are stored in the repository git config file. Gitblit's internal architecture has all the methods necessary to maintain milestones, but this functionality is not yet exposed through the web ui. For now you will have to control milestones manually with a text editor. +Milestones are a way to group tickets together. Milestones are specified for each repository and are stored in the repository git config file. Repository owners may create milestones through the web ui in the *Tickets* page on the *Milestones* tab. [milestone "v1.5.0"] status = Open diff --git a/src/site/tickets_using.mkd b/src/site/tickets_using.mkd index 7d9e569a..3cda7314 100644 --- a/src/site/tickets_using.mkd +++ b/src/site/tickets_using.mkd @@ -28,7 +28,7 @@ Any authenticated user who can clone your repository. ...add a single commit... git push origin HEAD:refs/for/new # read ticket id from server output - git branch --set-upstream-to=origin/ticket/{id} + git branch -u origin/ticket/{id} ### Creating the first Patchset for an Existing Ticket @@ -42,7 +42,7 @@ Any authenticated user who can clone your repository. cd repo git checkout -b ticket/{id} ...add one or more commits... - git push --set-upstream origin ticket/{id} + git push -u origin ticket/{id} ### Safely adding commits to a Patchset for an Existing Ticket @@ -148,7 +148,7 @@ Add some commits (fast-forward) to ticket *12*. Set the milestone to *1.4.1*. ### Merging Patchsets -The Gitblit web ui offers a merge button which *should work* but is not fully tested. Gitblit does verify that you can cleanly merge a patchset to the integration branch. +The Gitblit web ui offers a merge button which will work for clean merges of a patchset to the integration branch. There are complicated merge scenarios for which it may be best to merge using your Git client. There are several ways to do this, here is a safe merge strategy which pulls into a new branch and then fast-forwards your integration branch, assuming you were happy with the pull (merge). diff --git a/src/site/upgrade_express.mkd b/src/site/upgrade_express.mkd deleted file mode 100644 index 9e1eed9d..00000000 --- a/src/site/upgrade_express.mkd +++ /dev/null @@ -1,23 +0,0 @@ -## Upgrading Gitblit Express
-
-1. Make a backup copy of */deployments/ROOT.war/WEB-INF/web.xml*
-2. Delete your */deployments/ROOT.war* and then copy the new */deployments/ROOT.war* from the archive.
-3. Diff your backup copy of web.xml with the pristine one you copied as part of */deployments/ROOT.war* and apply any necessary changes.
-
-These steps are necessary to ensure that you end up using the specified libraries and resources for the new version. Otherwise you could end up with a hybrid filesystem that would make it difficult to troubleshoot. It is important to note that the web.xml file contains both your default settings AND application configuration. Not updating the web.xml is just a likely to create problems as making a hybrid filesystem.
-
-### 1.3.1 web.xml
-
-- Changed *WicketFilter* to *GitblitWicketFilter* to handle optional, smart cache-control updates (issue-274)
-
-### 1.3.0 web.xml
-
-- Added LogoServlet
-- Added SparkleShareInviteServlet
-- Added EnforceAuthenticationFilter
-- Moved GitServlet
-
-### 1.4.0 web.xml
-
-- Changed baseFolder from a context-parameter to a JNDI env-entry
-- Added pt servlet
\ No newline at end of file diff --git a/src/test/config/test-users.conf b/src/test/config/test-users.conf index 1d01f846..4361410c 100644 --- a/src/test/config/test-users.conf +++ b/src/test/config/test-users.conf @@ -2,13 +2,9 @@ password = admin cookie = dd94709528bb1c83d08f3088d4043f4742891f4f accountType = LOCAL + emailMeOnMyTicketChanges = true role = "#admin" role = "#notfederated" -[user "sampleuser"] - password = sampleuser - cookie = 6e07ed42149fc166206319faffdfba2e2ec82e43 - accountType = LOCAL - role = "#none" [team "admins"] role = "#none" accountType = LOCAL diff --git a/src/test/java/com/gitblit/tests/AuthenticationManagerTest.java b/src/test/java/com/gitblit/tests/AuthenticationManagerTest.java index 84a2b749..f1d2711e 100644 --- a/src/test/java/com/gitblit/tests/AuthenticationManagerTest.java +++ b/src/test/java/com/gitblit/tests/AuthenticationManagerTest.java @@ -43,7 +43,7 @@ public class AuthenticationManagerTest extends GitblitUnitTest { IAuthenticationManager newAuthenticationManager() { RuntimeManager runtime = new RuntimeManager(getSettings(), GitBlitSuite.BASEFOLDER).start(); - users = new UserManager(runtime).start(); + users = new UserManager(runtime, null).start(); AuthenticationManager auth = new AuthenticationManager(runtime, users).start(); return auth; } diff --git a/src/test/java/com/gitblit/tests/BranchTicketServiceTest.java b/src/test/java/com/gitblit/tests/BranchTicketServiceTest.java index 6119b8db..cc404abf 100644 --- a/src/test/java/com/gitblit/tests/BranchTicketServiceTest.java +++ b/src/test/java/com/gitblit/tests/BranchTicketServiceTest.java @@ -54,8 +54,8 @@ public class BranchTicketServiceTest extends TicketServiceTest { IRuntimeManager runtimeManager = new RuntimeManager(settings).start(); IPluginManager pluginManager = new PluginManager(runtimeManager).start(); INotificationManager notificationManager = new NotificationManager(settings).start(); - IUserManager userManager = new UserManager(runtimeManager).start(); - IRepositoryManager repositoryManager = new RepositoryManager(runtimeManager, userManager).start(); + IUserManager userManager = new UserManager(runtimeManager, pluginManager).start(); + IRepositoryManager repositoryManager = new RepositoryManager(runtimeManager, pluginManager, userManager).start(); BranchTicketService service = new BranchTicketService( runtimeManager, diff --git a/src/test/java/com/gitblit/tests/FileTicketServiceTest.java b/src/test/java/com/gitblit/tests/FileTicketServiceTest.java index 20cde26b..6ede042a 100644 --- a/src/test/java/com/gitblit/tests/FileTicketServiceTest.java +++ b/src/test/java/com/gitblit/tests/FileTicketServiceTest.java @@ -53,8 +53,8 @@ public class FileTicketServiceTest extends TicketServiceTest { IRuntimeManager runtimeManager = new RuntimeManager(settings).start(); IPluginManager pluginManager = new PluginManager(runtimeManager).start(); INotificationManager notificationManager = new NotificationManager(settings).start(); - IUserManager userManager = new UserManager(runtimeManager).start(); - IRepositoryManager repositoryManager = new RepositoryManager(runtimeManager, userManager).start(); + IUserManager userManager = new UserManager(runtimeManager, pluginManager).start(); + IRepositoryManager repositoryManager = new RepositoryManager(runtimeManager, pluginManager, userManager).start(); FileTicketService service = new FileTicketService( runtimeManager, diff --git a/src/test/java/com/gitblit/tests/HtpasswdAuthenticationTest.java b/src/test/java/com/gitblit/tests/HtpasswdAuthenticationTest.java index 4e1c3ac1..f4e24d4e 100644 --- a/src/test/java/com/gitblit/tests/HtpasswdAuthenticationTest.java +++ b/src/test/java/com/gitblit/tests/HtpasswdAuthenticationTest.java @@ -75,15 +75,15 @@ public class HtpasswdAuthenticationTest extends GitblitUnitTest { private HtpasswdAuthProvider newHtpasswdAuthentication(IStoredSettings settings) { RuntimeManager runtime = new RuntimeManager(settings, GitBlitSuite.BASEFOLDER).start(); - UserManager users = new UserManager(runtime).start(); + UserManager users = new UserManager(runtime, null).start(); HtpasswdAuthProvider htpasswd = new HtpasswdAuthProvider(); htpasswd.setup(runtime, users); return htpasswd; } - + private AuthenticationManager newAuthenticationManager(IStoredSettings settings) { RuntimeManager runtime = new RuntimeManager(settings, GitBlitSuite.BASEFOLDER).start(); - UserManager users = new UserManager(runtime).start(); + UserManager users = new UserManager(runtime, null).start(); HtpasswdAuthProvider htpasswd = new HtpasswdAuthProvider(); htpasswd.setup(runtime, users); AuthenticationManager auth = new AuthenticationManager(runtime, users); @@ -191,7 +191,7 @@ public class HtpasswdAuthenticationTest extends GitblitUnitTest { assertEquals("leading", user.username); } - + @Test public void testAuthenticationManager() { diff --git a/src/test/java/com/gitblit/tests/LdapAuthenticationTest.java b/src/test/java/com/gitblit/tests/LdapAuthenticationTest.java index 21063d58..646f7e9f 100644 --- a/src/test/java/com/gitblit/tests/LdapAuthenticationTest.java +++ b/src/test/java/com/gitblit/tests/LdapAuthenticationTest.java @@ -68,7 +68,7 @@ public class LdapAuthenticationTest extends GitblitUnitTest { private static InMemoryDirectoryServer ds; private IUserManager userManager; - + private AuthenticationManager auth; private MemorySettings settings; @@ -97,12 +97,12 @@ public class LdapAuthenticationTest extends GitblitUnitTest { private LdapAuthProvider newLdapAuthentication(IStoredSettings settings) { RuntimeManager runtime = new RuntimeManager(settings, GitBlitSuite.BASEFOLDER).start(); - userManager = new UserManager(runtime).start(); + userManager = new UserManager(runtime, null).start(); LdapAuthProvider ldap = new LdapAuthProvider(); ldap.setup(runtime, userManager); return ldap; } - + private AuthenticationManager newAuthenticationManager(IStoredSettings settings) { RuntimeManager runtime = new RuntimeManager(settings, GitBlitSuite.BASEFOLDER).start(); AuthenticationManager auth = new AuthenticationManager(runtime, userManager); @@ -258,7 +258,7 @@ public class LdapAuthenticationTest extends GitblitUnitTest { assertNull(userThreeModel.getTeam("git_admins")); assertTrue(userThreeModel.canAdmin); } - + @Test public void testBindWithUser() { settings.put(Keys.realm.ldap.bindpattern, "CN=${username},OU=US,OU=Users,OU=UserControl,OU=MyOrganization,DC=MyDomain"); @@ -267,7 +267,7 @@ public class LdapAuthenticationTest extends GitblitUnitTest { UserModel userOneModel = auth.authenticate("UserOne", "userOnePassword".toCharArray()); assertNotNull(userOneModel); - + UserModel userOneModelFailedAuth = auth.authenticate("UserOne", "userTwoPassword".toCharArray()); assertNull(userOneModelFailedAuth); } diff --git a/src/test/java/com/gitblit/tests/LuceneExecutorTest.java b/src/test/java/com/gitblit/tests/LuceneExecutorTest.java index 319c09c1..5c319e65 100644 --- a/src/test/java/com/gitblit/tests/LuceneExecutorTest.java +++ b/src/test/java/com/gitblit/tests/LuceneExecutorTest.java @@ -49,8 +49,8 @@ public class LuceneExecutorTest extends GitblitUnitTest { MemorySettings settings = new MemorySettings();
settings.put(Keys.git.repositoriesFolder, GitBlitSuite.REPOSITORIES);
RuntimeManager runtime = new RuntimeManager(settings, GitBlitSuite.BASEFOLDER).start();
- UserManager users = new UserManager(runtime).start();
- RepositoryManager repos = new RepositoryManager(runtime, users);
+ UserManager users = new UserManager(runtime, null).start();
+ RepositoryManager repos = new RepositoryManager(runtime, null, users);
return new LuceneService(settings, repos);
}
diff --git a/src/test/java/com/gitblit/tests/RedisTicketServiceTest.java b/src/test/java/com/gitblit/tests/RedisTicketServiceTest.java index 94391a15..b782b449 100644 --- a/src/test/java/com/gitblit/tests/RedisTicketServiceTest.java +++ b/src/test/java/com/gitblit/tests/RedisTicketServiceTest.java @@ -61,8 +61,8 @@ public class RedisTicketServiceTest extends TicketServiceTest { IRuntimeManager runtimeManager = new RuntimeManager(settings).start(); IPluginManager pluginManager = new PluginManager(runtimeManager).start(); INotificationManager notificationManager = new NotificationManager(settings).start(); - IUserManager userManager = new UserManager(runtimeManager).start(); - IRepositoryManager repositoryManager = new RepositoryManager(runtimeManager, userManager).start(); + IUserManager userManager = new UserManager(runtimeManager, pluginManager).start(); + IRepositoryManager repositoryManager = new RepositoryManager(runtimeManager, pluginManager, userManager).start(); RedisTicketService service = new RedisTicketService( runtimeManager, diff --git a/src/test/java/com/gitblit/tests/RedmineAuthenticationTest.java b/src/test/java/com/gitblit/tests/RedmineAuthenticationTest.java index 6ede8313..3b6b7bba 100644 --- a/src/test/java/com/gitblit/tests/RedmineAuthenticationTest.java +++ b/src/test/java/com/gitblit/tests/RedmineAuthenticationTest.java @@ -26,7 +26,7 @@ public class RedmineAuthenticationTest extends GitblitUnitTest { RedmineAuthProvider newRedmineAuthentication(IStoredSettings settings) { RuntimeManager runtime = new RuntimeManager(settings, GitBlitSuite.BASEFOLDER).start(); - UserManager users = new UserManager(runtime).start(); + UserManager users = new UserManager(runtime, null).start(); RedmineAuthProvider redmine = new RedmineAuthProvider(); redmine.setup(runtime, users); return redmine; @@ -35,10 +35,10 @@ public class RedmineAuthenticationTest extends GitblitUnitTest { RedmineAuthProvider newRedmineAuthentication() { return newRedmineAuthentication(getSettings()); } - + AuthenticationManager newAuthenticationManager() { RuntimeManager runtime = new RuntimeManager(getSettings(), GitBlitSuite.BASEFOLDER).start(); - UserManager users = new UserManager(runtime).start(); + UserManager users = new UserManager(runtime, null).start(); RedmineAuthProvider redmine = new RedmineAuthProvider(); redmine.setup(runtime, users); redmine.setTestingCurrentUserAsJson(JSON); diff --git a/src/test/java/com/gitblit/tests/RpcTests.java b/src/test/java/com/gitblit/tests/RpcTests.java index ced1a130..51b4671b 100644 --- a/src/test/java/com/gitblit/tests/RpcTests.java +++ b/src/test/java/com/gitblit/tests/RpcTests.java @@ -397,4 +397,67 @@ public class RpcTests extends GitblitUnitTest { assertNotNull(branches);
assertTrue(branches.size() > 0);
}
+
+ @Test
+ public void testFork() throws Exception {
+ // test forking by an administrator
+ // admins are all-powerful and can fork the unforakable :)
+ testFork(account, password, true, true);
+ testFork(account, password, false, true);
+
+ // test forking by a permitted normal user
+ UserModel forkUser = new UserModel("forkuser");
+ forkUser.password = forkUser.username;
+ forkUser.canFork = true;
+ RpcUtils.deleteUser(forkUser, url, account, password.toCharArray());
+ RpcUtils.createUser(forkUser, url, account, password.toCharArray());
+ testFork(forkUser.username, forkUser.password, true, true);
+ testFork(forkUser.username, forkUser.password, false, false);
+ RpcUtils.deleteUser(forkUser, url, account, password.toCharArray());
+
+ // test forking by a non-permitted normal user
+ UserModel noForkUser = new UserModel("noforkuser");
+ noForkUser.password = noForkUser.username;
+ noForkUser.canFork = false;
+ RpcUtils.deleteUser(noForkUser, url, account, password.toCharArray());
+ RpcUtils.createUser(noForkUser, url, account, password.toCharArray());
+ testFork(forkUser.username, forkUser.password, true, false);
+ testFork(forkUser.username, forkUser.password, false, false);
+ RpcUtils.deleteUser(noForkUser, url, account, password.toCharArray());
+ }
+
+ private void testFork(String forkAcct, String forkAcctPassword, boolean allowForks, boolean expectSuccess) throws Exception {
+ // test does not exist
+ RepositoryModel dne = new RepositoryModel();
+ dne.name = "doesNotExist.git";
+ assertFalse(String.format("Successfully forked %s!", dne.name),
+ RpcUtils.forkRepository(dne, url, forkAcct, forkAcctPassword.toCharArray()));
+
+ // delete any previous fork
+ RepositoryModel fork = findRepository(String.format("~%s/helloworld.git", forkAcct));
+ if (fork != null) {
+ RpcUtils.deleteRepository(fork, url, account, password.toCharArray());
+ }
+
+ // update the origin to allow forks or not
+ RepositoryModel origin = findRepository("helloworld.git");
+ origin.allowForks = allowForks;
+ RpcUtils.updateRepository(origin.name, origin, url, account, password.toCharArray());
+
+ // fork the repository
+ if (expectSuccess) {
+ assertTrue(String.format("Failed to fork %s!", origin.name),
+ RpcUtils.forkRepository(origin, url, forkAcct, forkAcctPassword.toCharArray()));
+ } else {
+ assertFalse(String.format("Successfully forked %s!", origin.name),
+ RpcUtils.forkRepository(origin, url, forkAcct, forkAcctPassword.toCharArray()));
+ }
+
+ // attempt another fork
+ assertFalse(String.format("Successfully forked %s!", origin.name),
+ RpcUtils.forkRepository(origin, url, forkAcct, forkAcctPassword.toCharArray()));
+
+ // delete the fork repository
+ RpcUtils.deleteRepository(fork, url, account, password.toCharArray());
+ }
}
diff --git a/src/test/java/com/gitblit/tests/SyndicationUtilsTest.java b/src/test/java/com/gitblit/tests/SyndicationUtilsTest.java index d206bbdb..b4bb044f 100644 --- a/src/test/java/com/gitblit/tests/SyndicationUtilsTest.java +++ b/src/test/java/com/gitblit/tests/SyndicationUtilsTest.java @@ -21,7 +21,10 @@ import java.util.Date; import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
import org.junit.Test;
import com.gitblit.Constants.SearchType;
@@ -30,6 +33,20 @@ import com.gitblit.utils.SyndicationUtils; public class SyndicationUtilsTest extends GitblitUnitTest {
+ private static final AtomicBoolean started = new AtomicBoolean(false);
+
+ @BeforeClass
+ public static void startGitblit() throws Exception {
+ started.set(GitBlitSuite.startGitblit());
+ }
+
+ @AfterClass
+ public static void stopGitblit() throws Exception {
+ if (started.get()) {
+ GitBlitSuite.stopGitblit();
+ }
+ }
+
@Test
public void testSyndication() throws Exception {
List<FeedEntryModel> entries = new ArrayList<FeedEntryModel>();
@@ -60,7 +77,7 @@ public class SyndicationUtilsTest extends GitblitUnitTest { }
@Test
- public void testFeedRead() throws Exception {
+ public void testFeedReadCommits() throws Exception {
Set<String> links = new HashSet<String>();
for (int i = 0; i < 2; i++) {
List<FeedEntryModel> feed = SyndicationUtils.readFeed(GitBlitSuite.url, "ticgit.git",
@@ -77,6 +94,23 @@ public class SyndicationUtilsTest extends GitblitUnitTest { }
@Test
+ public void testFeedReadTags() throws Exception {
+ Set<String> links = new HashSet<String>();
+ for (int i = 0; i < 2; i++) {
+ List<FeedEntryModel> feed = SyndicationUtils.readTags(GitBlitSuite.url, "test/gitective.git",
+ 5, i, GitBlitSuite.account, GitBlitSuite.password.toCharArray());
+ assertTrue(feed != null);
+ assertTrue(feed.size() > 0);
+ assertEquals(5, feed.size());
+ for (FeedEntryModel entry : feed) {
+ links.add(entry.link);
+ }
+ }
+ // confirm we have 10 unique tags
+ assertEquals("Feed pagination failed", 10, links.size());
+ }
+
+ @Test
public void testSearchFeedRead() throws Exception {
List<FeedEntryModel> feed = SyndicationUtils
.readSearchFeed(GitBlitSuite.url, "ticgit.git", null, "test", null, 5, 0,
diff --git a/src/test/java/com/gitblit/tests/TicketServiceTest.java b/src/test/java/com/gitblit/tests/TicketServiceTest.java index d91ce533..1676e341 100644 --- a/src/test/java/com/gitblit/tests/TicketServiceTest.java +++ b/src/test/java/com/gitblit/tests/TicketServiceTest.java @@ -95,7 +95,7 @@ public abstract class TicketServiceTest extends GitblitUnitTest { // query non-existent ticket
TicketModel nonExistent = service.getTicket(getRepository(), 0);
assertNull(nonExistent);
-
+
// create and insert a ticket
Change c1 = newChange("testCreation() " + Long.toHexString(System.currentTimeMillis()));
TicketModel ticket = service.createTicket(getRepository(), c1);
@@ -205,6 +205,9 @@ public abstract class TicketServiceTest extends GitblitUnitTest { assertEquals(1, results.size());
assertTrue(results.get(0).title.startsWith("testUpdates"));
+ // check the ids
+ assertEquals("[1, 2]", service.getIds(getRepository()).toString());
+
// delete all tickets
for (TicketModel aTicket : allTickets) {
assertTrue(service.deleteTicket(getRepository(), aTicket.number, "D"));
diff --git a/src/test/java/com/gitblit/tests/mock/MockRuntimeManager.java b/src/test/java/com/gitblit/tests/mock/MockRuntimeManager.java index 6e56a873..54be539f 100644 --- a/src/test/java/com/gitblit/tests/mock/MockRuntimeManager.java +++ b/src/test/java/com/gitblit/tests/mock/MockRuntimeManager.java @@ -82,6 +82,21 @@ public class MockRuntimeManager implements IRuntimeManager { } @Override + public boolean isServingHTTP() { + return true; + } + + @Override + public boolean isServingGIT() { + return true; + } + + @Override + public boolean isServingSSH() { + return true; + } + + @Override public boolean isDebugMode() { return true; } |