From ce048e750f7ae986dddfc8ab9b57750114d2b7b9 Mon Sep 17 00:00:00 2001 From: James Moger Date: Fri, 25 Apr 2014 23:52:30 -0400 Subject: Create and update milestone pages with rename support --- src/main/java/com/gitblit/wicket/GitBlitWebApp.java | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/main/java/com/gitblit/wicket/GitBlitWebApp.java') diff --git a/src/main/java/com/gitblit/wicket/GitBlitWebApp.java b/src/main/java/com/gitblit/wicket/GitBlitWebApp.java index c4fdeda5..d4c1bc40 100644 --- a/src/main/java/com/gitblit/wicket/GitBlitWebApp.java +++ b/src/main/java/com/gitblit/wicket/GitBlitWebApp.java @@ -51,6 +51,7 @@ 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.EditTicketPage; import com.gitblit.wicket.pages.ExportTicketPage; import com.gitblit.wicket.pages.FederationRegistrationPage; @@ -63,6 +64,7 @@ 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.NewMilestonePage; import com.gitblit.wicket.pages.NewTicketPage; import com.gitblit.wicket.pages.OverviewPage; import com.gitblit.wicket.pages.PatchPage; @@ -187,6 +189,8 @@ 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"); // setup the markup document urls mount("/docs", DocsPage.class, "r"); -- cgit v1.2.3 From 3380373c3123f875f50e48b0290c84dfae0519e4 Mon Sep 17 00:00:00 2001 From: BUISSON Christian Date: Tue, 15 Apr 2014 15:21:33 +0200 Subject: First Step in My Tickets --- .../com/gitblit/tickets/FileTicketService.java | 116 +++++++++++-------- .../java/com/gitblit/tickets/ITicketService.java | 11 +- .../java/com/gitblit/wicket/GitBlitWebApp.java | 2 + .../com/gitblit/wicket/GitBlitWebApp.properties | 3 +- .../com/gitblit/wicket/GitBlitWebApp_fr.properties | 1 + .../com/gitblit/wicket/pages/MyTicketsPage.html | 50 ++++++++ .../com/gitblit/wicket/pages/MyTicketsPage.java | 128 +++++++++++++++++++++ .../java/com/gitblit/wicket/pages/RootPage.java | 1 + 8 files changed, 262 insertions(+), 50 deletions(-) create mode 100644 src/main/java/com/gitblit/wicket/pages/MyTicketsPage.html create mode 100644 src/main/java/com/gitblit/wicket/pages/MyTicketsPage.java (limited to 'src/main/java/com/gitblit/wicket/GitBlitWebApp.java') diff --git a/src/main/java/com/gitblit/tickets/FileTicketService.java b/src/main/java/com/gitblit/tickets/FileTicketService.java index 4386020f..ea5d065f 100644 --- a/src/main/java/com/gitblit/tickets/FileTicketService.java +++ b/src/main/java/com/gitblit/tickets/FileTicketService.java @@ -207,62 +207,82 @@ public class FileTicketService extends ITicketService { @Override public List getTickets(RepositoryModel repository, TicketFilter filter) { List list = new ArrayList(); - - Repository db = repositoryManager.getRepository(repository.name); - try { - // Collect the set of all json files - File dir = new File(db.getDirectory(), TICKETS_PATH); - List journals = findAll(dir, JOURNAL); - - // Deserialize each ticket and optionally filter out unwanted tickets - for (File journal : journals) { - String json = null; - try { - json = new String(FileUtils.readContent(journal), Constants.ENCODING); - } catch (Exception e) { - log.error(null, e); - } - if (StringUtils.isEmpty(json)) { - // journal was touched but no changes were written - continue; - } - try { - // 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); - List changes = TicketSerializer.deserializeJournal(json); - if (ArrayUtils.isEmpty(changes)) { - log.warn("Empty journal for {}:{}", repository, journal); + List databases = new ArrayList(); + List models = new ArrayList(); + + if(repository == null) + { + List allRepo = repositoryManager.getRepositoryList(); + for(int i = 0; i < allRepo.size(); i++) + { + databases.add(repositoryManager.getRepository(allRepo.get(i))); + models.add(repositoryManager.getRepositoryModel(allRepo.get(i))); + } + } + else + { + databases.add(repositoryManager.getRepository(repository.name)); + models.add(repository); + } + + for(int i = 0; i < databases.size(); i++) + { + Repository db = databases.get(i); + try { + // Collect the set of all json files + File dir = new File(db.getDirectory(), TICKETS_PATH); + List journals = findAll(dir, JOURNAL); + + // Deserialize each ticket and optionally filter out unwanted tickets + for (File journal : journals) { + String json = null; + try { + json = new String(FileUtils.readContent(journal), Constants.ENCODING); + } catch (Exception e) { + log.error(null, e); + } + if (StringUtils.isEmpty(json)) { + // journal was touched but no changes were written continue; } - TicketModel ticket = TicketModel.buildTicket(changes); - ticket.project = repository.projectPath; - ticket.repository = repository.name; - ticket.number = ticketId; - - // add the ticket, conditionally, to the list - if (filter == null) { - list.add(ticket); - } else { - if (filter.accept(ticket)) { + try { + // 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); + List changes = TicketSerializer.deserializeJournal(json); + if (ArrayUtils.isEmpty(changes)) { + log.warn("Empty journal for {}:{}", models.get(i), journal); + continue; + } + TicketModel ticket = TicketModel.buildTicket(changes); + ticket.project = models.get(i).projectPath; + ticket.repository = models.get(i).name; + ticket.number = ticketId; + + // add the ticket, conditionally, to the list + if (filter == null) { list.add(ticket); + } else { + if (filter.accept(ticket)) { + list.add(ticket); + } } + } catch (Exception e) { + log.error("failed to deserialize {}/{}\n{}", + new Object [] { models.get(i), journal, e.getMessage()}); + log.error(null, e); } - } catch (Exception e) { - log.error("failed to deserialize {}/{}\n{}", - new Object [] { repository, journal, e.getMessage()}); - log.error(null, e); } + } finally { + db.close(); } - - // sort the tickets by creation - Collections.sort(list); - return list; - } finally { - db.close(); } + + // sort the tickets by creation + Collections.sort(list); + return list; } private List findAll(File dir, String filename) { diff --git a/src/main/java/com/gitblit/tickets/ITicketService.java b/src/main/java/com/gitblit/tickets/ITicketService.java index 3261ca96..e7b4c71d 100644 --- a/src/main/java/com/gitblit/tickets/ITicketService.java +++ b/src/main/java/com/gitblit/tickets/ITicketService.java @@ -779,12 +779,21 @@ public abstract class ITicketService { * @since 1.4.0 */ public abstract boolean hasTicket(RepositoryModel repository, long ticketId); + + /** + * Returns all tickets. This is not a Lucene search! + * + * @return all tickets + */ + public List getTickets() { + return getTickets(null, null); + } /** * Returns all tickets. This is not a Lucene search! * * @param repository - * @return all tickets + * @return all tickets of a given repository * @since 1.4.0 */ public List getTickets(RepositoryModel repository) { diff --git a/src/main/java/com/gitblit/wicket/GitBlitWebApp.java b/src/main/java/com/gitblit/wicket/GitBlitWebApp.java index d4c1bc40..9f002d2b 100644 --- a/src/main/java/com/gitblit/wicket/GitBlitWebApp.java +++ b/src/main/java/com/gitblit/wicket/GitBlitWebApp.java @@ -81,6 +81,7 @@ import com.gitblit.wicket.pages.TicketsPage; import com.gitblit.wicket.pages.TreePage; import com.gitblit.wicket.pages.UserPage; import com.gitblit.wicket.pages.UsersPage; +import com.gitblit.wicket.pages.MyTicketsPage; public class GitBlitWebApp extends WebApplication { @@ -191,6 +192,7 @@ public class GitBlitWebApp extends WebApplication { 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"); diff --git a/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties b/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties index 1394890f..030c5ebe 100644 --- a/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties +++ b/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties @@ -677,4 +677,5 @@ gb.editMilestone = edit milestone gb.notifyChangedOpenTickets = send notification for changed open tickets gb.overdue = overdue gb.openMilestones = open milestones -gb.closedMilestones = closed milestones \ No newline at end of file +gb.closedMilestones = closed milestones +gb.mytickets = my tickets \ 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..75a3ab7a 100644 --- a/src/main/java/com/gitblit/wicket/GitBlitWebApp_fr.properties +++ b/src/main/java/com/gitblit/wicket/GitBlitWebApp_fr.properties @@ -670,3 +670,4 @@ gb.repositoryDoesNotAcceptPatchsets = Ce d 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 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..bbb2b96b --- /dev/null +++ b/src/main/java/com/gitblit/wicket/pages/MyTicketsPage.html @@ -0,0 +1,50 @@ + + + + + + + Responsible Tickets + + + + + + +
+ + + Author Tickets + + + + + + +
+ + + Voted Tickets + + + + + + +
+ + + Watched Tickets + + + + + + +
+
+ + \ 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..509b00ca --- /dev/null +++ b/src/main/java/com/gitblit/wicket/pages/MyTicketsPage.java @@ -0,0 +1,128 @@ +package com.gitblit.wicket.pages; + +import java.util.ArrayList; +import java.util.List; + +import com.gitblit.models.UserModel; +import com.gitblit.models.TicketModel; +import com.gitblit.tickets.ITicketService; +import com.gitblit.wicket.GitBlitWebApp; +import com.gitblit.wicket.GitBlitWebSession; +import com.gitblit.wicket.panels.LinkPanel; + +import org.apache.wicket.markup.html.basic.Label; +import org.apache.wicket.markup.html.list.ListItem; +import org.apache.wicket.markup.html.list.ListView; +import org.apache.wicket.PageParameters; + +public class MyTicketsPage extends RootPage { + + public MyTicketsPage(PageParameters params) + { + this(); + } + + public MyTicketsPage() + { + super(); + setupPage("", ""); + + UserModel currentUser = GitBlitWebSession.get().getUser(); + if (currentUser == null) { + currentUser = UserModel.ANONYMOUS; + } + String username = currentUser.getName(); + + ITicketService tickets = GitBlitWebApp.get().tickets(); + List returnedTickets = tickets.getTickets(null); + + List responsibleTickets = new ArrayList(); + List authorTickets = new ArrayList(); + List votedTickets = new ArrayList(); + List watchedTickets = new ArrayList(); + for(int i = 0; i < returnedTickets.size(); i++) + { + TicketModel ticket = returnedTickets.get(i); + if(ticket.isOpen()) + { + if(ticket.isResponsible(username)) + { + responsibleTickets.add(ticket); + } + if(ticket.isAuthor(username)) + { + authorTickets.add(ticket); + } + if(ticket.isVoter(username)) + { + votedTickets.add(ticket); + } + if(ticket.isWatching(username)) + { + watchedTickets.add(ticket); + } + } + } + + ListView responsibleView = new ListView("responsibleTickets", responsibleTickets) + { + private static final long serialVersionUID = 1L; + + @Override + public void populateItem(final ListItem item) + { + final TicketModel ticket = item.getModelObject(); + String ticketUrl = app().tickets().getTicketUrl(ticket); + item.add(new LinkPanel("ticketName", "", ticket.title, ticketUrl)); + item.add(new Label("ticketDescription", ticket.body)); + } + }; + + ListView authorView = new ListView("authorTickets", authorTickets) + { + private static final long serialVersionUID = 1L; + + @Override + public void populateItem(final ListItem item) + { + final TicketModel ticket = item.getModelObject(); + String ticketUrl = app().tickets().getTicketUrl(ticket); + item.add(new LinkPanel("ticketName", "", ticket.title, ticketUrl)); + item.add(new Label("ticketDescription", ticket.body)); + } + }; + + ListView votedView = new ListView("votedTickets", votedTickets) + { + private static final long serialVersionUID = 1L; + + @Override + public void populateItem(final ListItem item) + { + final TicketModel ticket = item.getModelObject(); + String ticketUrl = app().tickets().getTicketUrl(ticket); + item.add(new LinkPanel("ticketName", "", ticket.title, ticketUrl)); + item.add(new Label("ticketDescription", ticket.body)); + } + }; + + ListView watchedView = new ListView("watchedTickets", watchedTickets) + { + private static final long serialVersionUID = 1L; + + @Override + public void populateItem(final ListItem item) + { + final TicketModel ticket = item.getModelObject(); + String ticketUrl = app().tickets().getTicketUrl(ticket); + item.add(new LinkPanel("ticketName", "", ticket.title, ticketUrl)); + item.add(new Label("ticketDescription", ticket.body)); + } + }; + + add(responsibleView); + add(authorView); + add(votedView); + add(watchedView); + } +} diff --git a/src/main/java/com/gitblit/wicket/pages/RootPage.java b/src/main/java/com/gitblit/wicket/pages/RootPage.java index 5ccc3a4c..ec413b19 100644 --- a/src/main/java/com/gitblit/wicket/pages/RootPage.java +++ b/src/main/java/com/gitblit/wicket/pages/RootPage.java @@ -173,6 +173,7 @@ public abstract class RootPage extends BasePage { pages.add(new PageRegistration("gb.repositories", RepositoriesPage.class, getRootPageParameters())); pages.add(new PageRegistration("gb.activity", ActivityPage.class, getRootPageParameters())); + pages.add(new PageRegistration("gb.mytickets", MyTicketsPage.class, getRootPageParameters())); if (app().settings().getBoolean(Keys.web.allowLuceneIndexing, true)) { pages.add(new PageRegistration("gb.search", LuceneSearchPage.class)); } -- cgit v1.2.3 From ff17f7bac432b4ba8310ba3ea335748a34859d50 Mon Sep 17 00:00:00 2001 From: James Moger Date: Mon, 5 May 2014 09:53:24 -0400 Subject: Replace RawPage with RawServlet --- src/main/java/WEB-INF/web.xml | 30 +- src/main/java/com/gitblit/Constants.java | 2 +- .../java/com/gitblit/servlet/BranchFilter.java | 126 ------ .../java/com/gitblit/servlet/BranchServlet.java | 480 --------------------- src/main/java/com/gitblit/servlet/PagesFilter.java | 2 +- .../java/com/gitblit/servlet/PagesServlet.java | 2 +- src/main/java/com/gitblit/servlet/RawFilter.java | 126 ++++++ src/main/java/com/gitblit/servlet/RawServlet.java | 472 ++++++++++++++++++++ .../java/com/gitblit/wicket/GitBlitWebApp.java | 4 +- .../java/com/gitblit/wicket/MarkupProcessor.java | 11 +- .../java/com/gitblit/wicket/pages/BasePage.java | 4 + .../java/com/gitblit/wicket/pages/BlobPage.java | 12 +- .../com/gitblit/wicket/pages/CommitDiffPage.java | 5 +- .../java/com/gitblit/wicket/pages/CommitPage.java | 5 +- .../java/com/gitblit/wicket/pages/ComparePage.java | 5 +- .../java/com/gitblit/wicket/pages/DocPage.java | 6 +- .../java/com/gitblit/wicket/pages/DocsPage.java | 9 +- .../java/com/gitblit/wicket/pages/TreePage.java | 6 +- .../java/com/gitblit/wicket/panels/TagsPanel.java | 11 +- 19 files changed, 664 insertions(+), 654 deletions(-) delete mode 100644 src/main/java/com/gitblit/servlet/BranchFilter.java delete mode 100644 src/main/java/com/gitblit/servlet/BranchServlet.java create mode 100644 src/main/java/com/gitblit/servlet/RawFilter.java create mode 100644 src/main/java/com/gitblit/servlet/RawServlet.java (limited to 'src/main/java/com/gitblit/wicket/GitBlitWebApp.java') diff --git a/src/main/java/WEB-INF/web.xml b/src/main/java/WEB-INF/web.xml index d60992dd..cb483af4 100644 --- a/src/main/java/WEB-INF/web.xml +++ b/src/main/java/WEB-INF/web.xml @@ -134,18 +134,18 @@ - - BranchServlet - com.gitblit.servlet.BranchServlet + RawServlet + com.gitblit.servlet.RawServlet - BranchServlet - /branch/* + RawServlet + /raw/* @@ -280,16 +280,16 @@ - BranchFilter - com.gitblit.servlet.BranchFilter + RawFilter + com.gitblit.servlet.RawFilter - BranchFilter - /branch/* + RawFilter + /raw/* @@ -340,12 +340,12 @@ * FederationServlet * RpcFilter * RpcServlet - * BranchFilter - * BranchServlet + * RawFilter + * RawServlet * PagesFilter * PagesServlet * com.gitblit.Constants.PAGES_PATH --> - r/,git/,pt,feed/,zip/,federation/,rpc/,branch/,pages/,robots.txt,logo.png,graph/,sparkleshare/ + r/,git/,pt,feed/,zip/,federation/,rpc/,raw/,pages/,robots.txt,logo.png,graph/,sparkleshare/ diff --git a/src/main/java/com/gitblit/Constants.java b/src/main/java/com/gitblit/Constants.java index 96f13c89..4b9755f3 100644 --- a/src/main/java/com/gitblit/Constants.java +++ b/src/main/java/com/gitblit/Constants.java @@ -68,7 +68,7 @@ public class Constants { public static final String SPARKLESHARE_INVITE_PATH = "/sparkleshare/"; - public static final String BRANCH = "/branch/"; + public static final String RAW_PATH = "/raw/"; public static final String BRANCH_GRAPH_PATH = "/graph/"; diff --git a/src/main/java/com/gitblit/servlet/BranchFilter.java b/src/main/java/com/gitblit/servlet/BranchFilter.java deleted file mode 100644 index 58b8f433..00000000 --- a/src/main/java/com/gitblit/servlet/BranchFilter.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * 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 BranchFilter is an AccessRestrictionFilter which ensures http branch - * requests for a view-restricted repository are authenticated and authorized. - * - * @author James Moger - * - */ -public class BranchFilter 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/BranchServlet.java b/src/main/java/com/gitblit/servlet/BranchServlet.java deleted file mode 100644 index 33808961..00000000 --- a/src/main/java/com/gitblit/servlet/BranchServlet.java +++ /dev/null @@ -1,480 +0,0 @@ -/* - * 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 BranchServlet extends DaggerServlet { - - private static final long serialVersionUID = 1L; - - private transient Logger logger = LoggerFactory.getLogger(BranchServlet.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); - } - String encodedPath = path.replace(' ', '-'); - try { - encodedPath = URLEncoder.encode(encodedPath, "UTF-8"); - } catch (UnsupportedEncodingException e) { - } - return baseURL + Constants.BRANCH + 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); - } - return branch; - } - - 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 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 names = new TreeMap(); - for (PathModel entry : pathEntries) { - names.put(entry.name.toLowerCase(), entry.name); - } - - List extensions = new ArrayList(); - 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); - String content = MarkdownUtils.transformMarkdown(str); - - try { - response.setStatus(HttpServletResponse.SC_NOT_FOUND); - - byte [] bytes = content.getBytes(Constants.ENCODING); - ByteArrayInputStream is = new ByteArrayInputStream(bytes); - sendContent(response, new Date(), is); - return; - } catch (Throwable t) { - logger.error("Failed to write page to client", t); - } - } else { - // - // directory list - // - response.setContentType("text/html"); - response.getWriter().append(""); - response.getWriter().append(""); - response.getWriter().append(""); - response.getWriter().append(""); - response.getWriter().append(""); - String pattern = ""; - 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), byteFormat.format(entry.size))); - } - response.getWriter().append(""); - response.getWriter().append("
pathmodesize
{1}{2}{3}
"); - } - } 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/PagesFilter.java b/src/main/java/com/gitblit/servlet/PagesFilter.java index 0535ea06..e07d9b3b 100644 --- a/src/main/java/com/gitblit/servlet/PagesFilter.java +++ b/src/main/java/com/gitblit/servlet/PagesFilter.java @@ -23,7 +23,7 @@ package com.gitblit.servlet; * @author James Moger * */ -public class PagesFilter extends BranchFilter { +public class PagesFilter extends RawFilter { } diff --git a/src/main/java/com/gitblit/servlet/PagesServlet.java b/src/main/java/com/gitblit/servlet/PagesServlet.java index 7919e442..f578f86f 100644 --- a/src/main/java/com/gitblit/servlet/PagesServlet.java +++ b/src/main/java/com/gitblit/servlet/PagesServlet.java @@ -26,7 +26,7 @@ import com.gitblit.Constants; * @author James Moger * */ -public class PagesServlet extends BranchServlet { +public class PagesServlet extends RawServlet { private static final long serialVersionUID = 1L; 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..cde7b2e1 --- /dev/null +++ b/src/main/java/com/gitblit/servlet/RawServlet.java @@ -0,0 +1,472 @@ +/* + * 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); + } + String encodedPath = 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); + } + return branch; + } + + 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 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 names = new TreeMap(); + for (PathModel entry : pathEntries) { + names.put(entry.name.toLowerCase(), entry.name); + } + + List extensions = new ArrayList(); + 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(""); + response.getWriter().append(""); + response.getWriter().append(""); + response.getWriter().append(""); + response.getWriter().append(""); + String pattern = ""; + 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(""); + response.getWriter().append("
pathmodesize
{1}{2}{3}
"); + } + } 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/wicket/GitBlitWebApp.java b/src/main/java/com/gitblit/wicket/GitBlitWebApp.java index 9f002d2b..dc79af26 100644 --- a/src/main/java/com/gitblit/wicket/GitBlitWebApp.java +++ b/src/main/java/com/gitblit/wicket/GitBlitWebApp.java @@ -64,13 +64,13 @@ 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.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; @@ -81,7 +81,6 @@ import com.gitblit.wicket.pages.TicketsPage; import com.gitblit.wicket.pages.TreePage; import com.gitblit.wicket.pages.UserPage; import com.gitblit.wicket.pages.UsersPage; -import com.gitblit.wicket.pages.MyTicketsPage; public class GitBlitWebApp extends WebApplication { @@ -173,7 +172,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"); 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/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("historyLink", HistoryPage.class).setEnabled(false)); - add(new BookmarkablePageLink("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("historyLink", HistoryPage.class, WicketUtils.newPathParameter(repositoryName, objectId, blobPath))); - add(new BookmarkablePageLink("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("view", BlobPage.class, WicketUtils .newPathParameter(repositoryName, entry.commitId, entry.path)) .setEnabled(!entry.changeType.equals(ChangeType.DELETE))); - item.add(new BookmarkablePageLink("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("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("view", BlobPage.class, WicketUtils .newPathParameter(repositoryName, entry.commitId, entry.path)) .setEnabled(!entry.changeType.equals(ChangeType.DELETE))); - item.add(new BookmarkablePageLink("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("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("view", BlobPage.class, WicketUtils .newPathParameter(repositoryName, endId, entry.path)) .setEnabled(!entry.changeType.equals(ChangeType.DELETE))); - item.add(new BookmarkablePageLink("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("blame", BlamePage.class, WicketUtils .newPathParameter(repositoryName, endId, entry.path)) 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("historyLink", HistoryPage.class, WicketUtils.newPathParameter(repositoryName, objectId, documentPath))); - fragment.add(new BookmarkablePageLink("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("historyLink", HistoryPage.class, WicketUtils.newPathParameter(repositoryName, commitId, doc.documentPath))); - item.add(new BookmarkablePageLink("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("view", DocPage.class, WicketUtils .newPathParameter(repositoryName, id, entry.path))); - item.add(new BookmarkablePageLink("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("blame", BlamePage.class, WicketUtils .newPathParameter(repositoryName, id, entry.path))); item.add(new BookmarkablePageLink("history", HistoryPage.class, WicketUtils 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("view", BlobPage.class, WicketUtils.newPathParameter(repositoryName, id, path))); - links.add(new BookmarkablePageLink("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("blame", BlamePage.class, WicketUtils.newPathParameter(repositoryName, id, path))); 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("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 -- cgit v1.2.3 From 859deba551b5e6850fb6331084493a402cecce45 Mon Sep 17 00:00:00 2001 From: James Moger Date: Tue, 22 Apr 2014 16:08:52 -0400 Subject: Integrate admin menu into user menu and add user menu extension --- .../com/gitblit/extensions/UserMenuExtension.java | 40 + .../java/com/gitblit/wicket/GitBlitWebApp.java | 2 + .../com/gitblit/wicket/GitBlitWebApp.properties | 5 +- .../java/com/gitblit/wicket/PageRegistration.java | 12 +- .../java/com/gitblit/wicket/pages/RootPage.html | 14 +- .../java/com/gitblit/wicket/pages/RootPage.java | 1343 +++++++++++--------- .../java/com/gitblit/wicket/pages/TeamsPage.html | 13 + .../java/com/gitblit/wicket/pages/TeamsPage.java | 30 + .../java/com/gitblit/wicket/pages/UsersPage.html | 2 - .../java/com/gitblit/wicket/pages/UsersPage.java | 3 - .../com/gitblit/wicket/panels/NavigationPanel.java | 13 +- src/site/plugins_extensions.mkd | 10 +- 12 files changed, 836 insertions(+), 651 deletions(-) create mode 100644 src/main/java/com/gitblit/extensions/UserMenuExtension.java create mode 100644 src/main/java/com/gitblit/wicket/pages/TeamsPage.html create mode 100644 src/main/java/com/gitblit/wicket/pages/TeamsPage.java (limited to 'src/main/java/com/gitblit/wicket/GitBlitWebApp.java') 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 getMenuItems(UserModel user); +} diff --git a/src/main/java/com/gitblit/wicket/GitBlitWebApp.java b/src/main/java/com/gitblit/wicket/GitBlitWebApp.java index dc79af26..3ca7d48f 100644 --- a/src/main/java/com/gitblit/wicket/GitBlitWebApp.java +++ b/src/main/java/com/gitblit/wicket/GitBlitWebApp.java @@ -77,6 +77,7 @@ 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; @@ -181,6 +182,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 diff --git a/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties b/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties index 2c83dd6c..d0c2d48c 100644 --- a/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties +++ b/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties @@ -680,4 +680,7 @@ gb.notifyChangedOpenTickets = send notification for changed open tickets gb.overdue = overdue gb.openMilestones = open milestones gb.closedMilestones = closed milestones -gb.adminMenuItem = admin +gb.administration = administration +gb.plugins = plugins +gb.extensions = extensions + diff --git a/src/main/java/com/gitblit/wicket/PageRegistration.java b/src/main/java/com/gitblit/wicket/PageRegistration.java index ff4a55b4..9fd8f870 100644 --- a/src/main/java/com/gitblit/wicket/PageRegistration.java +++ b/src/main/java/com/gitblit/wicket/PageRegistration.java @@ -67,13 +67,13 @@ public class PageRegistration implements Serializable { public final String url; - public OtherPageLink(String translationKey, String url) { - super(translationKey, null); + public OtherPageLink(String keyOrText, String url) { + super(keyOrText, null); this.url = url; } - public OtherPageLink(String translationKey, String url, boolean hiddenPhone) { - super(translationKey, null, null, hiddenPhone); + public OtherPageLink(String keyOrText, String url, boolean hiddenPhone) { + super(keyOrText, null, null, hiddenPhone); this.url = url; } } @@ -90,8 +90,8 @@ public class PageRegistration implements Serializable { public final List menuItems; - public DropDownMenuRegistration(String translationKey, Class pageClass) { - super(translationKey, pageClass); + public DropDownMenuRegistration(String keyOrText, Class pageClass) { + super(keyOrText, pageClass); menuItems = new ArrayList(); } } 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 @@ + + +
  • +
  • +
    diff --git a/src/main/java/com/gitblit/wicket/pages/RootPage.java b/src/main/java/com/gitblit/wicket/pages/RootPage.java index b9055c13..3003c70e 100644 --- a/src/main/java/com/gitblit/wicket/pages/RootPage.java +++ b/src/main/java/com/gitblit/wicket/pages/RootPage.java @@ -1,624 +1,719 @@ -/* - * 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.extensions.AdminMenuExtension; -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.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.DropDownMenuRegistration; -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 username = new Model(""); - IModel password = new Model(""); - List repositoryModels = new ArrayList(); - - 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("\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 pages = new ArrayList(); - if (!authenticateView || (authenticateView && isLoggedIn)) { - pages.add(new PageRegistration(isLoggedIn ? "gb.myDashboard" : "gb.dashboard", MyDashboardPage.class, - getRootPageParameters())); - if (isLoggedIn && app().tickets().isReady()) { - pages.add(new PageRegistration("gb.myTickets", MyTicketsPage.class)); - } - pages.add(new PageRegistration("gb.repositories", RepositoriesPage.class, - getRootPageParameters())); - pages.add(new PageRegistration("gb.activity", ActivityPage.class, getRootPageParameters())); - if (allowLucene) { - pages.add(new PageRegistration("gb.search", LuceneSearchPage.class)); - } - - UserModel user = GitBlitWebSession.get().getUser(); - - if (showAdmin) { - // admin dropdown menu - DropDownMenuRegistration adminMenu = new DropDownMenuRegistration("gb.adminMenuItem", MyDashboardPage.class); - - adminMenu.menuItems.add(new PageLinkMenuItem(getString("gb.users"), UsersPage.class)); - - boolean showRegistrations = app().federation().canFederate() - && app().settings().getBoolean(Keys.web.showFederationRegistrations, false); - if (showRegistrations) { - adminMenu.menuItems.add(new PageLinkMenuItem(getString("gb.federation"), FederationPage.class)); - } - - // allow plugins to contribute admin menu items - List extensions = app().plugins().getExtensions(AdminMenuExtension.class); - for (AdminMenuExtension ext : extensions) { - adminMenu.menuItems.add(new MenuDivider()); - adminMenu.menuItems.addAll(ext.getMenuItems(user)); - } - - pages.add(adminMenu); - } - - if (!authenticateView || (authenticateView && 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 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 getRepositoryModels() { - if (repositoryModels.isEmpty()) { - final UserModel user = GitBlitWebSession.get().getUser(); - List repositories = app().repositories().getRepositoryModels(user); - repositoryModels.addAll(repositories); - Collections.sort(repositoryModels); - } - return repositoryModels; - } - - protected void addDropDownMenus(List pages) { - - } - - protected List getRepositoryFilterItems(PageParameters params) { - final UserModel user = GitBlitWebSession.get().getUser(); - Set filters = new LinkedHashSet(); - List repositories = getRepositoryModels(); - - // accessible repositories by federation set - Map setMap = new HashMap(); - 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 sets = new ArrayList(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 teams = new ArrayList(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 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(filters); - } - - protected List 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 items = new ArrayList(); - Set choicesSet = new TreeSet(app().settings().getIntegers(Keys.web.activityDurationChoices)); - if (choicesSet.isEmpty()) { - choicesSet.addAll(Arrays.asList(1, 3, 7, 14, 21, 28)); - } - List choices = new ArrayList(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 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 availableModels = getRepositoryModels(); - Set models = new HashSet(); - - 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 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 teams = StringUtils.getStringsFromValue(team, ","); - - // need TeamModels first - List teamModels = new ArrayList(); - 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 timeFiltered = new HashSet(); - for (RepositoryModel model : models) { - if (model.lastChange.after(threshold)) { - timeFiltered.add(model); - } - } - models = timeFiltered; - } - - List list = new ArrayList(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 loginForm = new SessionlessForm("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 unameField = new TextField("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("newRepository", - EditRepositoryPage.class).setVisible(user.canAdmin() || user.canCreate())); - - add(new BookmarkablePageLink("myProfile", - UserPage.class, WicketUtils.newUsernameParameter(user.username))); - - add(new BookmarkablePageLink("changePassword", - ChangePasswordPage.class).setVisible(editCredentials)); - - add(new BookmarkablePageLink("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.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.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.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 username = new Model(""); + IModel password = new Model(""); + List repositoryModels = new ArrayList(); + + 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("\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 pages = new ArrayList(); + if (!authenticateView || (authenticateView && isLoggedIn)) { + pages.add(new PageRegistration(isLoggedIn ? "gb.myDashboard" : "gb.dashboard", MyDashboardPage.class, + getRootPageParameters())); + if (isLoggedIn && app().tickets().isReady()) { + pages.add(new PageRegistration("gb.myTickets", MyTicketsPage.class)); + } + pages.add(new PageRegistration("gb.repositories", RepositoriesPage.class, + getRootPageParameters())); + pages.add(new PageRegistration("gb.activity", ActivityPage.class, getRootPageParameters())); + if (allowLucene) { + pages.add(new PageRegistration("gb.search", LuceneSearchPage.class)); + } + + UserModel user = GitBlitWebSession.get().getUser(); + + if (showAdmin) { + // admin dropdown menu + DropDownMenuRegistration adminMenu = new DropDownMenuRegistration("gb.adminMenuItem", MyDashboardPage.class); + + adminMenu.menuItems.add(new PageLinkMenuItem(getString("gb.users"), UsersPage.class)); + + boolean showRegistrations = app().federation().canFederate() + && app().settings().getBoolean(Keys.web.showFederationRegistrations, false); + if (showRegistrations) { + adminMenu.menuItems.add(new PageLinkMenuItem(getString("gb.federation"), FederationPage.class)); + } + + // allow plugins to contribute admin menu items + List extensions = app().plugins().getExtensions(AdminMenuExtension.class); + for (AdminMenuExtension ext : extensions) { + adminMenu.menuItems.add(new MenuDivider()); + adminMenu.menuItems.addAll(ext.getMenuItems(user)); + } + + pages.add(adminMenu); + } + + if (!authenticateView || (authenticateView && 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 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 getRepositoryModels() { + if (repositoryModels.isEmpty()) { + final UserModel user = GitBlitWebSession.get().getUser(); + List repositories = app().repositories().getRepositoryModels(user); + repositoryModels.addAll(repositories); + Collections.sort(repositoryModels); + } + return repositoryModels; + } + + protected void addDropDownMenus(List pages) { + + } + + protected List getRepositoryFilterItems(PageParameters params) { + final UserModel user = GitBlitWebSession.get().getUser(); + Set filters = new LinkedHashSet(); + List repositories = getRepositoryModels(); + + // accessible repositories by federation set + Map setMap = new HashMap(); + 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 sets = new ArrayList(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 teams = new ArrayList(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 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(filters); + } + + protected List 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 items = new ArrayList(); + Set choicesSet = new TreeSet(app().settings().getIntegers(Keys.web.activityDurationChoices)); + if (choicesSet.isEmpty()) { + choicesSet.addAll(Arrays.asList(1, 3, 7, 14, 21, 28)); + } + List choices = new ArrayList(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 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 availableModels = getRepositoryModels(); + Set models = new HashSet(); + + 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 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 teams = StringUtils.getStringsFromValue(team, ","); + + // need TeamModels first + List teamModels = new ArrayList(); + 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 timeFiltered = new HashSet(); + for (RepositoryModel model : models) { + if (model.lastChange.after(threshold)) { + timeFiltered.add(model); + } + } + models = timeFiltered; + } + + List list = new ArrayList(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 loginForm = new SessionlessForm("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 unameField = new TextField("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 standardItems = new ArrayList(); + standardItems.add(new MenuDivider()); + if (user.canAdmin() || user.canCreate()) { + standardItems.add(new PageLinkMenuItem("gb.newRepository", EditRepositoryPage.class)); + } + standardItems.add(new PageLinkMenuItem("gb.myProfile", UserPage.class, + WicketUtils.newUsernameParameter(user.username))); + if (editCredentials) { + standardItems.add(new PageLinkMenuItem("gb.changePassword", ChangePasswordPage.class)); + } + standardItems.add(new MenuDivider()); + add(newSubmenu("standardMenu", user.getDisplayName(), standardItems)); + + if (showAdmin) { + // admin menu + List adminItems = new ArrayList(); + 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 extensionItems = new ArrayList(); + List extensions = app().plugins().getExtensions(UserMenuExtension.class); + for (UserMenuExtension ext : extensions) { + List 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("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 menuItems) { + Fragment submenu = new Fragment(wicketId, "submenuFragment", this); + submenu.add(new Label("submenuTitle", submenuTitle).setRenderBodyOnly(true)); + ListDataProvider menuItemsDp = new ListDataProvider(menuItems); + DataView submenuItems = new DataView("submenuItem", menuItemsDp) { + private static final long serialVersionUID = 1L; + + @Override + public void populateItem(final Item 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 @@ + + + + +
    +
    [teams panel]
    +
    +
    + + \ 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/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 @@
    -
    [teams panel]
    -
    [users panel]
    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/panels/NavigationPanel.java b/src/main/java/com/gitblit/wicket/panels/NavigationPanel.java index 393dd139..7db29fa2 100644 --- a/src/main/java/com/gitblit/wicket/panels/NavigationPanel.java +++ b/src/main/java/com/gitblit/wicket/panels/NavigationPanel.java @@ -45,25 +45,32 @@ public class NavigationPanel extends Panel { @Override public void populateItem(final Item item) { PageRegistration entry = item.getModelObject(); + String linkText = entry.translationKey; + try { + // try to lookup translation key + linkText = getString(entry.translationKey); + } catch (Exception e) { + } + if (entry.hiddenPhone) { WicketUtils.setCssClass(item, "hidden-phone"); } if (entry instanceof OtherPageLink) { // other link OtherPageLink link = (OtherPageLink) entry; - Component c = new LinkPanel("link", null, getString(entry.translationKey), link.url); + Component c = new LinkPanel("link", null, linkText, link.url); c.setRenderBodyOnly(true); item.add(c); } else if (entry instanceof DropDownMenuRegistration) { // drop down menu DropDownMenuRegistration reg = (DropDownMenuRegistration) entry; - Component c = new DropDownMenu("link", getString(entry.translationKey), reg); + Component c = new DropDownMenu("link", linkText, reg); c.setRenderBodyOnly(true); item.add(c); WicketUtils.setCssClass(item, "dropdown"); } else { // standard page link - Component c = new LinkPanel("link", null, getString(entry.translationKey), + Component c = new LinkPanel("link", null, linkText, entry.pageClass, entry.params); c.setRenderBodyOnly(true); if (entry.pageClass.equals(pageClass)) { diff --git a/src/site/plugins_extensions.mkd b/src/site/plugins_extensions.mkd index 684373e0..18a7e325 100644 --- a/src/site/plugins_extensions.mkd +++ b/src/site/plugins_extensions.mkd @@ -205,23 +205,23 @@ public class MyRequestFilter extends HttpRequestFilter { } ``` -### Admin Menu Items +### User Menu Items *SINCE 1.6.0* -You can provide your own admin menu items by subclassing the *AdminMenuExtension* class. +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.AdminMenuExtension; +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 MyAdminMenuContributor extends AdminMenuExtension { +public class MyUserMenuContributor extends UserMenuExtension { @Override public List getMenuItems(UserModel user) { @@ -229,5 +229,3 @@ public class MyAdminMenuContributor extends AdminMenuExtension { } } ``` - - -- cgit v1.2.3 From 7a401a3ff909bf82fb4068d6dba430497f74084a Mon Sep 17 00:00:00 2001 From: James Moger Date: Tue, 22 Apr 2014 22:53:06 -0400 Subject: Allow plugins to extend the top navbar and repository navbar This change also ties the plugin manager into the Wicket framework and allows plugins to contribute and mount new pages which are linked by the top navbar and repository navbar extensions. --- .../com/gitblit/extensions/AdminMenuExtension.java | 40 ------ .../gitblit/extensions/GitblitWicketPlugin.java | 49 ++++++++ .../com/gitblit/extensions/NavLinkExtension.java | 40 ++++++ .../extensions/RepositoryNavLinkExtension.java | 42 +++++++ src/main/java/com/gitblit/models/NavLink.java | 140 +++++++++++++++++++++ .../java/com/gitblit/wicket/GitBlitWebApp.java | 106 ++++++++++++++-- .../java/com/gitblit/wicket/GitblitWicketApp.java | 72 +++++++++++ .../java/com/gitblit/wicket/PageRegistration.java | 99 --------------- .../com/gitblit/wicket/PluginClassResolver.java | 122 ++++++++++++++++++ .../gitblit/wicket/UrlExternalFormComparator.java | 39 ++++++ .../com/gitblit/wicket/pages/ActivityPage.java | 10 +- .../com/gitblit/wicket/pages/DashboardPage.java | 10 +- .../java/com/gitblit/wicket/pages/ProjectPage.java | 14 +-- .../com/gitblit/wicket/pages/ProjectsPage.java | 10 +- .../com/gitblit/wicket/pages/RepositoriesPage.java | 10 +- .../com/gitblit/wicket/pages/RepositoryPage.java | 61 +++++---- .../java/com/gitblit/wicket/pages/RootPage.java | 53 ++++---- .../java/com/gitblit/wicket/pages/UserPage.java | 10 +- .../com/gitblit/wicket/panels/DropDownMenu.java | 45 ++++++- .../com/gitblit/wicket/panels/NavigationPanel.java | 49 +++++--- src/site/plugins_extensions.mkd | 58 ++++++++- 21 files changed, 817 insertions(+), 262 deletions(-) delete mode 100644 src/main/java/com/gitblit/extensions/AdminMenuExtension.java create mode 100644 src/main/java/com/gitblit/extensions/GitblitWicketPlugin.java create mode 100644 src/main/java/com/gitblit/extensions/NavLinkExtension.java create mode 100644 src/main/java/com/gitblit/extensions/RepositoryNavLinkExtension.java create mode 100644 src/main/java/com/gitblit/models/NavLink.java create mode 100644 src/main/java/com/gitblit/wicket/GitblitWicketApp.java delete mode 100644 src/main/java/com/gitblit/wicket/PageRegistration.java create mode 100644 src/main/java/com/gitblit/wicket/PluginClassResolver.java create mode 100644 src/main/java/com/gitblit/wicket/UrlExternalFormComparator.java (limited to 'src/main/java/com/gitblit/wicket/GitBlitWebApp.java') diff --git a/src/main/java/com/gitblit/extensions/AdminMenuExtension.java b/src/main/java/com/gitblit/extensions/AdminMenuExtension.java deleted file mode 100644 index 8fe4288f..00000000 --- a/src/main/java/com/gitblit/extensions/AdminMenuExtension.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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 administration menu items. - * - * @author James Moger - * @since 1.6.0 - * - */ -public abstract class AdminMenuExtension implements ExtensionPoint { - - /** - * @param user - * @since 1.6.0 - * @return a list of menu items - */ - public abstract List getMenuItems(UserModel user); -} 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/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 getNavLinks(UserModel user); +} 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 getNavLinks(UserModel user, RepositoryModel repository); +} 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 pageClass; + public final PageParameters params; + + public PageNavLink(String translationKey, Class pageClass) { + this(translationKey, pageClass, null); + } + + public PageNavLink(String translationKey, Class pageClass, + PageParameters params) { + this(translationKey, pageClass, params, false); + } + + public PageNavLink(String translationKey, Class 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 menuItems; + + public DropDownPageMenuNavLink(String keyOrText, Class pageClass) { + this(keyOrText, pageClass, false); + } + + public DropDownPageMenuNavLink(String keyOrText, Class pageClass, boolean hiddenPhone) { + super(keyOrText, pageClass, null, hiddenPhone); + menuItems = new ArrayList(); + } + } + + /** + * Represents a DropDownMenu. + * + * @author James Moger + * + */ + public static class DropDownMenuNavLink extends NavLink implements Serializable { + + private static final long serialVersionUID = 1L; + + public final List menuItems; + + public DropDownMenuNavLink(String keyOrText) { + this(keyOrText, false); + } + + public DropDownMenuNavLink(String keyOrText, boolean hiddenPhone) { + super(keyOrText, hiddenPhone); + menuItems = new ArrayList(); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/gitblit/wicket/GitBlitWebApp.java b/src/main/java/com/gitblit/wicket/GitBlitWebApp.java index 3ca7d48f..d3aa62fd 100644 --- a/src/main/java/com/gitblit/wicket/GitBlitWebApp.java +++ b/src/main/java/com/gitblit/wicket/GitBlitWebApp.java @@ -28,8 +28,12 @@ import org.apache.wicket.Session; 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; @@ -83,7 +87,7 @@ 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 homePageClass = MyDashboardPage.class; @@ -210,11 +214,29 @@ 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 + PluginClassResolver classResolver = new PluginClassResolver(pluginManager); + getApplicationSettings().setClassResolver(classResolver); + getMarkupSettings().setDefaultMarkupEncoding("UTF-8"); - super.init(); } - private void mount(String location, Class 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 clazz, String... parameters) { if (parameters == null) { parameters = new String[] {}; } @@ -230,15 +252,26 @@ public class GitBlitWebApp extends WebApplication { } } + /* (non-Javadoc) + * @see com.gitblit.wicket.Webapp#getHomePage() + */ @Override public Class getHomePage() { return homePageClass; } + /* (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); } @@ -254,15 +287,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(); } @@ -271,58 +307,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/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 clazz, String... parameters); + + public abstract Class 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/PageRegistration.java b/src/main/java/com/gitblit/wicket/PageRegistration.java deleted file mode 100644 index 9fd8f870..00000000 --- a/src/main/java/com/gitblit/wicket/PageRegistration.java +++ /dev/null @@ -1,99 +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.models.Menu.MenuItem; - -/** - * 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 pageClass; - public final PageParameters params; - public final boolean hiddenPhone; - - public PageRegistration(String translationKey, Class pageClass) { - this(translationKey, pageClass, null); - } - - public PageRegistration(String translationKey, Class pageClass, - PageParameters params) { - this(translationKey, pageClass, params, false); - } - - public PageRegistration(String translationKey, Class 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 keyOrText, String url) { - super(keyOrText, null); - this.url = url; - } - - public OtherPageLink(String keyOrText, String url, boolean hiddenPhone) { - super(keyOrText, 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 menuItems; - - public DropDownMenuRegistration(String keyOrText, Class pageClass) { - super(keyOrText, pageClass); - menuItems = new ArrayList(); - } - } - -} \ 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..ba53b04b --- /dev/null +++ b/src/main/java/com/gitblit/wicket/PluginClassResolver.java @@ -0,0 +1,122 @@ +/* + * 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.Application; +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 IPluginManager pluginManager; + + public PluginClassResolver(IPluginManager pluginManager) { + 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); + } + } + } + + throw new ClassNotFoundException(className); + } + + @Override + public Iterator getResources(final String name) { + Set urls = new TreeSet(new UrlExternalFormComparator()); + + for (PluginWrapper plugin : pluginManager.getPlugins()) { + if (PluginState.STARTED != plugin.getPluginState()) { + // ignore this plugin + continue; + } + + Iterator it = getResources(name, plugin); + while (it.hasNext()) { + URL url = it.next(); + urls.add(url); + } + } + + return urls.iterator(); + } + + protected Iterator getResources(String name, PluginWrapper plugin) { + HashSet loadedFiles = new HashSet(); + try { + // Try the classloader for the wicket jar/bundle + Enumeration resources = plugin.getPluginClassLoader().getResources(name); + loadResources(resources, loadedFiles); + + // Try the classloader for the user's application jar/bundle + resources = Application.get().getClass().getClassLoader().getResources(name); + loadResources(resources, loadedFiles); + + // Try the context class loader + resources = Thread.currentThread().getContextClassLoader().getResources(name); + loadResources(resources, loadedFiles); + } catch (IOException e) { + throw new WicketRuntimeException(e); + } + + return loadedFiles.iterator(); + } + + private void loadResources(Enumeration resources, Set 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/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 +{ + @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/pages/ActivityPage.java b/src/main/java/com/gitblit/wicket/pages/ActivityPage.java index 0870ff96..c505a666 100644 --- a/src/main/java/com/gitblit/wicket/pages/ActivityPage.java +++ b/src/main/java/com/gitblit/wicket/pages/ActivityPage.java @@ -32,14 +32,14 @@ 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.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 pages) { - DropDownMenuRegistration filters = new DropDownMenuRegistration("gb.filters", + protected void addDropDownMenus(List navLinks) { + DropDownPageMenuNavLink filters = new DropDownPageMenuNavLink("gb.filters", ActivityPage.class); PageParameters currentParameters = getPageParameters(); @@ -155,7 +155,7 @@ public class ActivityPage extends RootPage { // Reset Filter filters.menuItems.add(new ParameterMenuItem(getString("gb.reset"))); } - pages.add(filters); + navLinks.add(filters); } /** diff --git a/src/main/java/com/gitblit/wicket/pages/DashboardPage.java b/src/main/java/com/gitblit/wicket/pages/DashboardPage.java index 16b0b734..9c10e01b 100644 --- a/src/main/java/com/gitblit/wicket/pages/DashboardPage.java +++ b/src/main/java/com/gitblit/wicket/pages/DashboardPage.java @@ -37,7 +37,9 @@ 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; @@ -46,8 +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.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 pages) { + protected void addDropDownMenus(List 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 @@ -155,7 +155,7 @@ public abstract class DashboardPage extends RootPage { menu.menuItems.add(new ParameterMenuItem(getString("gb.reset"))); } - pages.add(menu); + navLinks.add(menu); } diff --git a/src/main/java/com/gitblit/wicket/pages/ProjectPage.java b/src/main/java/com/gitblit/wicket/pages/ProjectPage.java index 6c8aa4f4..d358b775 100644 --- a/src/main/java/com/gitblit/wicket/pages/ProjectPage.java +++ b/src/main/java/com/gitblit/wicket/pages/ProjectPage.java @@ -29,6 +29,8 @@ 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; @@ -40,8 +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.DropDownMenuRegistration; import com.gitblit.wicket.WicketUtils; import com.gitblit.wicket.panels.FilterableRepositoryList; @@ -161,10 +161,10 @@ public class ProjectPage extends DashboardPage { } @Override - protected void addDropDownMenus(List pages) { + protected void addDropDownMenus(List 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)); @@ -177,12 +177,12 @@ public class ProjectPage extends DashboardPage { 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 diff --git a/src/main/java/com/gitblit/wicket/pages/ProjectsPage.java b/src/main/java/com/gitblit/wicket/pages/ProjectsPage.java index c404ae61..f04fa78a 100644 --- a/src/main/java/com/gitblit/wicket/pages/ProjectsPage.java +++ b/src/main/java/com/gitblit/wicket/pages/ProjectsPage.java @@ -25,10 +25,10 @@ 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.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 pages) { + protected void addDropDownMenus(List 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)); @@ -131,6 +131,6 @@ public class ProjectsPage extends RootPage { 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 41fe057c..a0b15a83 100644 --- a/src/main/java/com/gitblit/wicket/pages/RepositoriesPage.java +++ b/src/main/java/com/gitblit/wicket/pages/RepositoriesPage.java @@ -30,14 +30,14 @@ import org.eclipse.jgit.lib.Constants; 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.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.DropDownMenuRegistration; import com.gitblit.wicket.WicketUtils; import com.gitblit.wicket.panels.RepositoriesPanel; @@ -92,10 +92,10 @@ public class RepositoriesPage extends RootPage { } @Override - protected void addDropDownMenus(List pages) { + protected void addDropDownMenus(List 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)); @@ -108,7 +108,7 @@ public class RepositoriesPage extends RootPage { 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.java b/src/main/java/com/gitblit/wicket/pages/RepositoryPage.java index 5ea99fd8..165feedf 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; @@ -66,8 +69,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.TicketsUI; import com.gitblit.wicket.WicketUtils; @@ -91,7 +92,6 @@ public abstract class RepositoryPage extends RootPage { private Map submodules; - private final Map registeredPages; private boolean showAdmin; private boolean isOwner; @@ -150,12 +150,11 @@ public abstract class RepositoryPage extends RootPage { } } - // register the available page links for this page and user - registeredPages = registerPages(); + // register the available navigation links for this page and user + List navLinks = registerNavLinks(); - // standard page links - List pages = new ArrayList(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() @@ -183,45 +182,56 @@ public abstract class RepositoryPage extends RootPage { return new BugtraqProcessor(app().settings()); } - private Map registerPages() { + private List registerNavLinks() { PageParameters params = null; if (!StringUtils.isEmpty(repositoryName)) { params = WicketUtils.newRepositoryParameter(repositoryName); } - Map pages = new LinkedHashMap(); + List navLinks = new ArrayList(); 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)); + navLinks.add(new PageNavLink("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)); + navLinks.add(new PageNavLink("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)); + 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(getRepositoryModel()) || app().tickets().hasTickets(getRepositoryModel()))) { PageParameters tParams = new PageParameters(params); for (String state : TicketsUI.openStatii) { tParams.add(Lucene.status.name(), state); } - pages.put("tickets", new PageRegistration("gb.tickets", TicketsPage.class, tParams)); + 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); + } + + UserModel user = UserModel.ANONYMOUS; + if (GitBlitWebSession.get().isLoggedIn()) { + user = GitBlitWebSession.get().getUser(); + } + + // add repository nav link extensions + List extensions = app().plugins().getExtensions(RepositoryNavLinkExtension.class); + for (RepositoryNavLinkExtension ext : extensions) { + navLinks.addAll(ext.getNavLinks(user, model)); } // Conditionally add edit link @@ -233,9 +243,8 @@ public abstract class RepositoryPage extends RootPage { showAdmin = app().settings().getBoolean(Keys.web.allowAdministration, false); } isOwner = GitBlitWebSession.get().isLoggedIn() - && (model.isOwner(GitBlitWebSession.get() - .getUsername())); - return pages; + && (model.isOwner(GitBlitWebSession.get().getUsername())); + return navLinks; } protected boolean allowForkControls() { diff --git a/src/main/java/com/gitblit/wicket/pages/RootPage.java b/src/main/java/com/gitblit/wicket/pages/RootPage.java index 3003c70e..a2f3a497 100644 --- a/src/main/java/com/gitblit/wicket/pages/RootPage.java +++ b/src/main/java/com/gitblit/wicket/pages/RootPage.java @@ -50,6 +50,7 @@ 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; @@ -57,13 +58,14 @@ 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.PageRegistration; import com.gitblit.wicket.SessionlessForm; import com.gitblit.wicket.WicketUtils; import com.gitblit.wicket.panels.GravatarImage; @@ -174,50 +176,37 @@ public abstract class RootPage extends BasePage { } // navigation links - List pages = new ArrayList(); + List navLinks = new ArrayList(); if (!authenticateView || (authenticateView && isLoggedIn)) { - pages.add(new PageRegistration(isLoggedIn ? "gb.myDashboard" : "gb.dashboard", MyDashboardPage.class, + navLinks.add(new PageNavLink(isLoggedIn ? "gb.myDashboard" : "gb.dashboard", MyDashboardPage.class, getRootPageParameters())); if (isLoggedIn && app().tickets().isReady()) { - pages.add(new PageRegistration("gb.myTickets", MyTicketsPage.class)); + navLinks.add(new PageNavLink("gb.myTickets", MyTicketsPage.class)); } - pages.add(new PageRegistration("gb.repositories", RepositoriesPage.class, + navLinks.add(new PageNavLink("gb.repositories", RepositoriesPage.class, getRootPageParameters())); - pages.add(new PageRegistration("gb.activity", ActivityPage.class, getRootPageParameters())); + navLinks.add(new PageNavLink("gb.activity", ActivityPage.class, getRootPageParameters())); if (allowLucene) { - pages.add(new PageRegistration("gb.search", LuceneSearchPage.class)); + navLinks.add(new PageNavLink("gb.search", LuceneSearchPage.class)); } - UserModel user = GitBlitWebSession.get().getUser(); - - if (showAdmin) { - // admin dropdown menu - DropDownMenuRegistration adminMenu = new DropDownMenuRegistration("gb.adminMenuItem", MyDashboardPage.class); - - adminMenu.menuItems.add(new PageLinkMenuItem(getString("gb.users"), UsersPage.class)); - - boolean showRegistrations = app().federation().canFederate() - && app().settings().getBoolean(Keys.web.showFederationRegistrations, false); - if (showRegistrations) { - adminMenu.menuItems.add(new PageLinkMenuItem(getString("gb.federation"), FederationPage.class)); - } - - // allow plugins to contribute admin menu items - List extensions = app().plugins().getExtensions(AdminMenuExtension.class); - for (AdminMenuExtension ext : extensions) { - adminMenu.menuItems.add(new MenuDivider()); - adminMenu.menuItems.addAll(ext.getMenuItems(user)); - } + if (!authenticateView || (authenticateView && isLoggedIn)) { + addDropDownMenus(navLinks); + } - pages.add(adminMenu); + UserModel user = UserModel.ANONYMOUS; + if (isLoggedIn) { + user = GitBlitWebSession.get().getUser(); } - if (!authenticateView || (authenticateView && isLoggedIn)) { - addDropDownMenus(pages); + // add nav link extensions + List extensions = app().plugins().getExtensions(NavLinkExtension.class); + for (NavLinkExtension ext : extensions) { + navLinks.addAll(ext.getNavLinks(user)); } } - NavigationPanel navPanel = new NavigationPanel("navPanel", getRootNavPageClass(), pages); + NavigationPanel navPanel = new NavigationPanel("navPanel", getRootNavPageClass(), navLinks); add(navPanel); // display an error message cached from a redirect @@ -309,7 +298,7 @@ public abstract class RootPage extends BasePage { return repositoryModels; } - protected void addDropDownMenus(List pages) { + protected void addDropDownMenus(List navLinks) { } diff --git a/src/main/java/com/gitblit/wicket/pages/UserPage.java b/src/main/java/com/gitblit/wicket/pages/UserPage.java index 0767621c..6cb791eb 100644 --- a/src/main/java/com/gitblit/wicket/pages/UserPage.java +++ b/src/main/java/com/gitblit/wicket/pages/UserPage.java @@ -30,6 +30,8 @@ import org.eclipse.jgit.lib.PersonIdent; 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.models.RepositoryModel; import com.gitblit.models.UserModel; @@ -37,8 +39,6 @@ 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.DropDownMenuRegistration; import com.gitblit.wicket.WicketUtils; import com.gitblit.wicket.panels.GravatarImage; import com.gitblit.wicket.panels.LinkPanel; @@ -127,10 +127,10 @@ public class UserPage extends RootPage { } @Override - protected void addDropDownMenus(List pages) { + protected void addDropDownMenus(List 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)); @@ -143,6 +143,6 @@ public class UserPage extends RootPage { menu.menuItems.add(new ParameterMenuItem(getString("gb.reset"))); } - pages.add(menu); + navLinks.add(menu); } } diff --git a/src/main/java/com/gitblit/wicket/panels/DropDownMenu.java b/src/main/java/com/gitblit/wicket/panels/DropDownMenu.java index f561143d..4e7ae54c 100644 --- a/src/main/java/com/gitblit/wicket/panels/DropDownMenu.java +++ b/src/main/java/com/gitblit/wicket/panels/DropDownMenu.java @@ -21,24 +21,24 @@ 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.models.Menu.MenuDivider; 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.wicket.PageRegistration.DropDownMenuRegistration; +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 items = new ListDataProvider( - menu.menuItems); + ListDataProvider items = new ListDataProvider(menu.menuItems); DataView view = new DataView("menuItems", items) { private static final long serialVersionUID = 1L; @@ -76,4 +76,39 @@ public class DropDownMenu extends Panel { add(view); setRenderBodyOnly(true); } + + public DropDownMenu(String id, String label, final DropDownMenuNavLink menu) { + super(id); + + add(new Label("label", label).setRenderBodyOnly(true)); + ListDataProvider items = new ListDataProvider(menu.menuItems); + DataView view = new DataView("menuItems", items) { + private static final long serialVersionUID = 1L; + + @Override + public void populateItem(final Item 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())); + } + } + }; + add(view); + setRenderBodyOnly(true); + } } \ No newline at end of file diff --git a/src/main/java/com/gitblit/wicket/panels/NavigationPanel.java b/src/main/java/com/gitblit/wicket/panels/NavigationPanel.java index 7db29fa2..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,52 +36,59 @@ public class NavigationPanel extends Panel { private static final long serialVersionUID = 1L; public NavigationPanel(String id, final Class pageClass, - List registeredPages) { + List navLinks) { super(id); - ListDataProvider refsDp = new ListDataProvider( - registeredPages); - DataView refsView = new DataView("navLink", refsDp) { + ListDataProvider refsDp = new ListDataProvider(navLinks); + DataView linksView = new DataView("navLink", refsDp) { private static final long serialVersionUID = 1L; @Override - public void populateItem(final Item item) { - PageRegistration entry = item.getModelObject(); - String linkText = entry.translationKey; + public void populateItem(final Item item) { + NavLink navLink = item.getModelObject(); + String linkText = navLink.translationKey; try { // try to lookup translation key - linkText = getString(entry.translationKey); + linkText = getString(navLink.translationKey); } catch (Exception e) { } - if (entry.hiddenPhone) { + if (navLink.hiddenPhone) { WicketUtils.setCssClass(item, "hidden-phone"); } - if (entry instanceof OtherPageLink) { + if (navLink instanceof ExternalNavLink) { // other link - OtherPageLink link = (OtherPageLink) entry; + ExternalNavLink link = (ExternalNavLink) navLink; Component c = new LinkPanel("link", null, linkText, link.url); c.setRenderBodyOnly(true); item.add(c); - } else if (entry instanceof DropDownMenuRegistration) { + } else if (navLink instanceof DropDownPageMenuNavLink) { // drop down menu - DropDownMenuRegistration reg = (DropDownMenuRegistration) entry; + DropDownPageMenuNavLink reg = (DropDownPageMenuNavLink) navLink; Component c = new DropDownMenu("link", linkText, reg); c.setRenderBodyOnly(true); item.add(c); WicketUtils.setCssClass(item, "dropdown"); - } else { + } else if (navLink instanceof DropDownMenuNavLink) { + // drop down menu + DropDownMenuNavLink reg = (DropDownMenuNavLink) navLink; + Component c = new DropDownMenu("link", linkText, reg); + c.setRenderBodyOnly(true); + item.add(c); + WicketUtils.setCssClass(item, "dropdown"); + } else if (navLink instanceof PageNavLink) { + PageNavLink reg = (PageNavLink) navLink; // standard page link Component c = new LinkPanel("link", null, linkText, - entry.pageClass, entry.params); + 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/site/plugins_extensions.mkd b/src/site/plugins_extensions.mkd index 18a7e325..7bf63c17 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 mounted 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 @@ -225,7 +256,32 @@ public class MyUserMenuContributor extends UserMenuExtension { @Override public List getMenuItems(UserModel user) { - return Arrays.asList((MenuItem) new ExternalLinkMenuItem("Github", String.format("https://github.com/%s", user.username)); + 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 getNavLinks(UserModel user) { + NavLink link = new ExternalLinkMenuItem("Github", String.format("https://github.com/%s", user.username)); + return Arrays.asList(link); } } ``` -- cgit v1.2.3 From 0ee8041f980dab7fee723a326fa2a2f5d1fea054 Mon Sep 17 00:00:00 2001 From: James Moger Date: Wed, 7 May 2014 15:37:03 -0400 Subject: Fix class loading failure when there are no STARTED plugins --- src/main/java/com/gitblit/wicket/GitBlitWebApp.java | 4 +++- .../java/com/gitblit/wicket/PluginClassResolver.java | 20 +++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) (limited to 'src/main/java/com/gitblit/wicket/GitBlitWebApp.java') diff --git a/src/main/java/com/gitblit/wicket/GitBlitWebApp.java b/src/main/java/com/gitblit/wicket/GitBlitWebApp.java index d3aa62fd..7291d039 100644 --- a/src/main/java/com/gitblit/wicket/GitBlitWebApp.java +++ b/src/main/java/com/gitblit/wicket/GitBlitWebApp.java @@ -25,6 +25,7 @@ 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; @@ -226,7 +227,8 @@ public class GitBlitWebApp extends WebApplication implements GitblitWicketApp { } // customize the Wicket class resolver to load from plugins - PluginClassResolver classResolver = new PluginClassResolver(pluginManager); + IClassResolver coreResolver = getApplicationSettings().getClassResolver(); + PluginClassResolver classResolver = new PluginClassResolver(coreResolver, pluginManager); getApplicationSettings().setClassResolver(classResolver); getMarkupSettings().setDefaultMarkupEncoding("UTF-8"); diff --git a/src/main/java/com/gitblit/wicket/PluginClassResolver.java b/src/main/java/com/gitblit/wicket/PluginClassResolver.java index ba53b04b..476f9611 100644 --- a/src/main/java/com/gitblit/wicket/PluginClassResolver.java +++ b/src/main/java/com/gitblit/wicket/PluginClassResolver.java @@ -23,7 +23,6 @@ import java.util.Iterator; import java.util.Set; import java.util.TreeSet; -import org.apache.wicket.Application; import org.apache.wicket.WicketRuntimeException; import org.apache.wicket.application.IClassResolver; import org.slf4j.Logger; @@ -40,9 +39,11 @@ import com.gitblit.manager.IPluginManager; public class PluginClassResolver implements IClassResolver { private static final Logger logger = LoggerFactory.getLogger(PluginClassResolver.class); + private final IClassResolver coreResolver; private final IPluginManager pluginManager; - public PluginClassResolver(IPluginManager pluginManager) { + public PluginClassResolver(IClassResolver coreResolver, IPluginManager pluginManager) { + this.coreResolver = coreResolver; this.pluginManager = pluginManager; } @@ -65,7 +66,7 @@ public class PluginClassResolver implements IClassResolver { } } - throw new ClassNotFoundException(className); + return coreResolver.resolveClass(className); } @Override @@ -85,6 +86,11 @@ public class PluginClassResolver implements IClassResolver { } } + Iterator it = coreResolver.getResources(name); + while (it.hasNext()) { + URL url = it.next(); + urls.add(url); + } return urls.iterator(); } @@ -94,14 +100,6 @@ public class PluginClassResolver implements IClassResolver { // Try the classloader for the wicket jar/bundle Enumeration resources = plugin.getPluginClassLoader().getResources(name); loadResources(resources, loadedFiles); - - // Try the classloader for the user's application jar/bundle - resources = Application.get().getClass().getClassLoader().getResources(name); - loadResources(resources, loadedFiles); - - // Try the context class loader - resources = Thread.currentThread().getContextClassLoader().getResources(name); - loadResources(resources, loadedFiles); } catch (IOException e) { throw new WicketRuntimeException(e); } -- cgit v1.2.3 From 0047fbba99b804d268a66ed7504a568596de6168 Mon Sep 17 00:00:00 2001 From: James Moger Date: Tue, 27 May 2014 18:13:30 -0400 Subject: Simplified repository creation with a NewRepositoryPage --- .gitmodules | 3 + build.xml | 6 + releases.moxie | 2 + src/main/distrib/data/gitblit.properties | 5 + src/main/distrib/data/gitignore | 1 + src/main/java/com/gitblit/Constants.java | 8 + .../java/com/gitblit/servlet/GitblitContext.java | 16 + .../java/com/gitblit/wicket/GitBlitWebApp.java | 10 + .../com/gitblit/wicket/GitBlitWebApp.properties | 17 +- .../gitblit/wicket/pages/EditRepositoryPage.java | 16 +- .../gitblit/wicket/pages/NewRepositoryPage.html | 98 ++++ .../gitblit/wicket/pages/NewRepositoryPage.java | 493 +++++++++++++++++++++ .../java/com/gitblit/wicket/pages/RootPage.java | 2 +- .../java/com/gitblit/wicket/pages/UserPage.java | 4 +- .../wicket/panels/FilterableRepositoryList.java | 3 +- .../gitblit/wicket/panels/RepositoriesPanel.java | 5 +- 16 files changed, 674 insertions(+), 15 deletions(-) create mode 100644 .gitmodules create mode 160000 src/main/distrib/data/gitignore create mode 100644 src/main/java/com/gitblit/wicket/pages/NewRepositoryPage.html create mode 100644 src/main/java/com/gitblit/wicket/pages/NewRepositoryPage.java (limited to 'src/main/java/com/gitblit/wicket/GitBlitWebApp.java') diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..01eaa2c8 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "src/main/distrib/data/gitignore"] + path = src/main/distrib/data/gitignore + url = https://github.com/github/gitignore.git diff --git a/build.xml b/build.xml index f45c4ca9..be6f1dd2 100644 --- a/build.xml +++ b/build.xml @@ -919,6 +919,12 @@ + + + + + + diff --git a/releases.moxie b/releases.moxie index 4332238d..5aa207fa 100644 --- a/releases.moxie +++ b/releases.moxie @@ -36,6 +36,7 @@ r24: { - Add FORK_REPOSITORY RPC request type (issue-371, pr-161, ticket-65) - Add object type (ot) parameter for RSS queries to retrieve tag details (pr-165, ticket-66) - Add setting to allow STARTTLS without requiring SMTPS (pr-183) + - Simplified repository creation, offer simple README generation, and insertion of a pre-defined .gitignore file (ticket-76) - Added an extension point for monitoring onStartup and onShutdown (ticket-79) - Tag server-side merges when incremental push tags are enabled (issue-432, ticket-85) - Add setting to control default thread pool size for miscellaneous background tasks (ticket-92) @@ -55,6 +56,7 @@ r24: { - { name: 'web.allowDeletingNonEmptyRepositories', defaultValue: 'true' } - { name: 'mail.starttls', defaultValue: 'false' } - { name: 'execution.defaultThreadPoolSize', defaultValue: '1' } + - { name: 'git.gitignoreFolder', defaultValue: '${baseFolder}/gitignore' } } # diff --git a/src/main/distrib/data/gitblit.properties b/src/main/distrib/data/gitblit.properties index 6f55a3eb..d5623cd5 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! diff --git a/src/main/distrib/data/gitignore b/src/main/distrib/data/gitignore new file mode 160000 index 00000000..097db81c --- /dev/null +++ b/src/main/distrib/data/gitignore @@ -0,0 +1 @@ +Subproject commit 097db81c08b138dea7cb031eb18eeb16afe44bdf diff --git a/src/main/java/com/gitblit/Constants.java b/src/main/java/com/gitblit/Constants.java index 95eb944a..3e307537 100644 --- a/src/main/java/com/gitblit/Constants.java +++ b/src/main/java/com/gitblit/Constants.java @@ -122,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) { diff --git a/src/main/java/com/gitblit/servlet/GitblitContext.java b/src/main/java/com/gitblit/servlet/GitblitContext.java index 110e553c..50f22d5a 100644 --- a/src/main/java/com/gitblit/servlet/GitblitContext.java +++ b/src/main/java/com/gitblit/servlet/GitblitContext.java @@ -372,6 +372,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/wicket/GitBlitWebApp.java b/src/main/java/com/gitblit/wicket/GitBlitWebApp.java index 7291d039..f63ff3d9 100644 --- a/src/main/java/com/gitblit/wicket/GitBlitWebApp.java +++ b/src/main/java/com/gitblit/wicket/GitBlitWebApp.java @@ -57,6 +57,7 @@ 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; @@ -71,6 +72,7 @@ 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; @@ -92,6 +94,8 @@ public class GitBlitWebApp extends WebApplication implements GitblitWicketApp { private final Class homePageClass = MyDashboardPage.class; + private final Class newRepositoryPageClass = NewRepositoryPage.class; + private final Map cacheablePages = new HashMap(); private final IStoredSettings settings; @@ -207,6 +211,8 @@ public class GitBlitWebApp extends WebApplication implements GitblitWicketApp { 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"); @@ -262,6 +268,10 @@ public class GitBlitWebApp extends WebApplication implements GitblitWicketApp { return homePageClass; } + public Class getNewRepositoryPage() { + return newRepositoryPageClass; + } + /* (non-Javadoc) * @see com.gitblit.wicket.Webapp#isCacheablePage(java.lang.String) */ diff --git a/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties b/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties index 12430ade..ac589558 100644 --- a/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties +++ b/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties @@ -685,4 +685,19 @@ gb.closedMilestones = closed milestones gb.administration = administration gb.plugins = plugins gb.extensions = extensions - +gb.anonymous = Anonymous +gb.anonymousRepoDescription = Anyone can see, clone, and push to this repository. +gb.public = Public +gb.publicRepoDescription = Anyone can see and clone this repository. You choose who can push. +gb.protected = Protected +gb.protectedRepoDescription = Anyone can see this repository. You choose who can clone and push. +gb.private = Private +gb.privateRepoDescription = You choose who can see, clone, and push to this repository. +gb.initialCommit = Initial Commit +gb.initialCommitDescription = This will allow you to git clone this repository immediately. Skip this step if you have already run git init 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.initWithGitflow = Include a .gitflow file +gb.initWithGitflowDescription = This will generate a config file which guides Git clients in setting up Gitflow branches. \ 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 412c0ecc..c18cd514 100644 --- a/src/main/java/com/gitblit/wicket/pages/EditRepositoryPage.java +++ b/src/main/java/com/gitblit/wicket/pages/EditRepositoryPage.java @@ -429,11 +429,7 @@ 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)); } }; @@ -632,7 +628,15 @@ public class EditRepositoryPage extends RootSubPage { if (canDelete) { if (app().repositories().deleteRepositoryModel(latestModel)) { info(MessageFormat.format(getString("gb.repositoryDeleted"), latestModel)); - setResponsePage(RepositoriesPage.class); + 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)); } 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..e9f12027 --- /dev/null +++ b/src/main/java/com/gitblit/wicket/pages/NewRepositoryPage.html @@ -0,0 +1,98 @@ + + + + + +
    +
    +
    + + + + + + + + + + + + + + +
    /  
    + +
    +
    + +
    + +
    + + +
    +
    + + +
    +
    +
    + +
    +
    +
    + +
    + +

    +
    +

    +
    + +
    +
    + +
    +
    +
    +

    +
    +
    + +
    + +
    + +
    +
    + +

    +
    +
    + +
    +
    + +
    +
    +
    +

    +

    +
    +
    + + + + + \ 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..b0cc3e95 --- /dev/null +++ b/src/main/java/com/gitblit/wicket/pages/NewRepositoryPage.java @@ -0,0 +1,493 @@ +/* + * 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.Serializable; +import java.io.UnsupportedEncodingException; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.apache.wicket.ajax.AjaxRequestTarget; +import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior; +import org.apache.wicket.behavior.SimpleAttributeModifier; +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.Radio; +import org.apache.wicket.markup.html.form.RadioGroup; +import org.apache.wicket.markup.html.form.TextField; +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.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; + +public class NewRepositoryPage extends RootSubPage { + + private final RepositoryModel repositoryModel; + private RadioGroup permissionGroup; + private IModel addReadmeModel; + private Model gitignoreModel; + private IModel addGitflowModel; + private IModel addGitignoreModel; + + 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 getRootNavPageClass() { + return RepositoriesPage.class; + } + + @Override + protected void onInitialize() { + super.onInitialize(); + + CompoundPropertyModel rModel = new CompoundPropertyModel<>(repositoryModel); + Form form = new Form("editForm", rModel) { + + private static final long serialVersionUID = 1L; + + @Override + protected void onSubmit() { + + // confirm a repository name was entered + if (StringUtils.isEmpty(repositoryModel.name)) { + error(getString("gb.pleaseSetRepositoryName")); + return; + } + + String project = repositoryModel.projectPath; + String fullName = (project + "/" + repositoryModel.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); + } + + try { + if (fullName.contains("../")) { + error(getString("gb.illegalRelativeSlash")); + return; + } + if (fullName.contains("/../")) { + error(getString("gb.illegalRelativeSlash")); + return; + } + + // confirm valid characters in repository name + Character c = StringUtils.findInvalidCharacter(fullName); + if (c != null) { + error(MessageFormat.format(getString("gb.illegalCharacterRepositoryName"), + c)); + return; + } + + repositoryModel.name = fullName; + repositoryModel.projectPath = null; + + Permission permisison = permissionGroup.getModelObject(); + repositoryModel.accessRestriction = permisison.type; + repositoryModel.authorizationControl = AuthorizationControl.NAMED; + + repositoryModel.owners = new ArrayList(); + 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("Please select a .gitignore file"); + } + } + + // 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()); + + // restore project and name fields on error condition + repositoryModel.projectPath = StringUtils.getFirstPathElement(fullName); + if (!StringUtils.isEmpty(repositoryModel.projectPath)) { + repositoryModel.name = fullName.substring(repositoryModel.projectPath.length() + 1); + } + return; + } + setRedirect(true); + setResponsePage(SummaryPage.class, WicketUtils.newRepositoryParameter(fullName)); + } + }; + + GitBlitWebSession session = GitBlitWebSession.get(); + UserModel user = session.getUser(); + + // build project list for repository destination + String defaultProject = null; + List projects = new ArrayList(); + + if (user.canAdmin()) { + String main = app().settings().getString(Keys.web.repositoryRootGroupName, "main"); + projects.add(main); + defaultProject = main; + } + + if (user.canCreate()) { + projects.add(user.getPersonalPath()); + if (defaultProject == null) { + // only prefer personal namespace if default is not already set + defaultProject = user.getPersonalPath(); + } + } + + repositoryModel.projectPath = defaultProject; + + // do not let the browser pre-populate these fields + form.add(new SimpleAttributeModifier("autocomplete", "off")); + + form.add(new DropDownChoice("projectPath", projects)); + form.add(new TextField("name")); + form.add(new TextField("description")); + + Permission anonymousPermission = new Permission(getString("gb.anonymous"), getString("gb.anonymousRepoDescription"), "blank.png", AccessRestrictionType.NONE); + Permission publicPermission = new Permission(getString("gb.public"), getString("gb.publicRepoDescription"), "lock_go_16x16.png", AccessRestrictionType.PUSH); + Permission protectedPermission = new Permission(getString("gb.protected"), getString("gb.protectedRepoDescription"), "lock_pull_16x16.png", AccessRestrictionType.CLONE); + Permission privatePermission = new Permission(getString("gb.private"), getString("gb.privateRepoDescription"), "shield_16x16.png", AccessRestrictionType.VIEW); + + List permissions = new ArrayList(); + if (app().settings().getBoolean(Keys.git.allowAnonymousPushes, false)) { + permissions.add(anonymousPermission); + } + permissions.add(publicPermission); + permissions.add(protectedPermission); + permissions.add(privatePermission); + + // determine default permission selection + AccessRestrictionType defaultRestriction = AccessRestrictionType.fromName( + app().settings().getString(Keys.git.defaultAccessRestriction, AccessRestrictionType.PUSH.name())); + if (AccessRestrictionType.NONE == defaultRestriction) { + defaultRestriction = AccessRestrictionType.PUSH; + } + + Permission defaultPermission = publicPermission; + for (Permission permission : permissions) { + if (permission.type == defaultRestriction) { + defaultPermission = permission; + } + } + + permissionGroup = new RadioGroup<>("permissionsGroup", new Model(defaultPermission)); + form.add(permissionGroup); + + ListView permissionsList = new ListView("permissions", permissions) { + + private static final long serialVersionUID = 1L; + + @Override + protected void populateItem(ListItem item) { + Permission p = item.getModelObject(); + item.add(new Radio("radio", item.getModel())); + item.add(WicketUtils.newImage("image", p.image)); + item.add(new Label("name", p.name)); + item.add(new Label("description", p.description)); + } + }; + permissionGroup.add(permissionsList); + + // + // initial commit options + // + + // add README + addReadmeModel = Model.of(false); + form.add(new CheckBox("addReadme", 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 gitignores = new ArrayList(); + for (File file : files) { + if (file.isFile() && file.getName().endsWith(".gitignore")) { + gitignores.add(StringUtils.stripFileExtension(file.getName())); + } + } + Collections.sort(gitignores); + gitignoreModel = Model.of(""); + final DropDownChoice gitignoreChoice = new DropDownChoice("gitignore", gitignoreModel, gitignores); + gitignoreChoice.setOutputMarkupId(true); + form.add(gitignoreChoice.setEnabled(false)); + + addGitignoreModel = Model.of(false); + final CheckBox gitignoreCheckbox = new CheckBox("addGitignore", addGitignoreModel); + form.add(gitignoreCheckbox); + + gitignoreCheckbox.add(new AjaxFormComponentUpdatingBehavior("onchange") { + + private static final long serialVersionUID = 1L; + + @Override + protected void onUpdate(AjaxRequestTarget target) { + gitignoreChoice.setEnabled(addGitignoreModel.getObject()); + target.addComponent(gitignoreChoice); + } + }); + + // TODO add .gitflow + addGitflowModel = Model.of(false); + form.add(new CheckBox("addGitflow", addGitflowModel)); + + 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; + } + + private static class Permission implements Serializable { + + private static final long serialVersionUID = 1L; + + final String name; + final String description; + final String image; + final AccessRestrictionType type; + + Permission(String name, String description, String img, AccessRestrictionType type) { + this.name = name; + this.description = description; + this.image = img; + this.type = type; + } + } +} diff --git a/src/main/java/com/gitblit/wicket/pages/RootPage.java b/src/main/java/com/gitblit/wicket/pages/RootPage.java index a2f3a497..b1c3639d 100644 --- a/src/main/java/com/gitblit/wicket/pages/RootPage.java +++ b/src/main/java/com/gitblit/wicket/pages/RootPage.java @@ -607,7 +607,7 @@ public abstract class RootPage extends BasePage { List standardItems = new ArrayList(); standardItems.add(new MenuDivider()); if (user.canAdmin() || user.canCreate()) { - standardItems.add(new PageLinkMenuItem("gb.newRepository", EditRepositoryPage.class)); + standardItems.add(new PageLinkMenuItem("gb.newRepository", app().getNewRepositoryPage())); } standardItems.add(new PageLinkMenuItem("gb.myProfile", UserPage.class, WicketUtils.newUsernameParameter(user.username))); diff --git a/src/main/java/com/gitblit/wicket/pages/UserPage.java b/src/main/java/com/gitblit/wicket/pages/UserPage.java index 6cb791eb..29b49b33 100644 --- a/src/main/java/com/gitblit/wicket/pages/UserPage.java +++ b/src/main/java/com/gitblit/wicket/pages/UserPage.java @@ -30,8 +30,8 @@ import org.eclipse.jgit.lib.PersonIdent; 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.NavLink.DropDownPageMenuNavLink; import com.gitblit.models.ProjectModel; import com.gitblit.models.RepositoryModel; import com.gitblit.models.UserModel; @@ -95,7 +95,7 @@ public class UserPage extends RootPage { UserModel sessionUser = GitBlitWebSession.get().getUser(); if (sessionUser != null && user.canCreate() && sessionUser.equals(user)) { // user can create personal repositories - add(new BookmarkablePageLink("newRepository", EditRepositoryPage.class)); + add(new BookmarkablePageLink("newRepository", app().getNewRepositoryPage())); } else { add(new Label("newRepository").setVisible(false)); } 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/RepositoriesPanel.java b/src/main/java/com/gitblit/wicket/panels/RepositoriesPanel.java index dd208e23..8573e1a6 100644 --- a/src/main/java/com/gitblit/wicket/panels/RepositoriesPanel.java +++ b/src/main/java/com/gitblit/wicket/panels/RepositoriesPanel.java @@ -51,7 +51,6 @@ 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.ProjectPage; import com.gitblit.wicket.pages.RepositoriesPage; import com.gitblit.wicket.pages.SummaryPage; @@ -87,12 +86,12 @@ public class RepositoriesPanel extends BasePanel { setResponsePage(RepositoriesPage.class); } }.setVisible(app().settings().getBoolean(Keys.git.cacheRepositoryList, true))); - managementLinks.add(new BookmarkablePageLink("newRepository", EditRepositoryPage.class)); + managementLinks.add(new BookmarkablePageLink("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("newRepository", EditRepositoryPage.class)); + managementLinks.add(new BookmarkablePageLink("newRepository", app().getNewRepositoryPage())); add(managementLinks); } else { // user has no management permissions -- cgit v1.2.3