James Moger
2015-11-22 ed552ba47c02779c270ffd62841d6d1048dade70
commit | author | age
f13c4c 1 /*
JM 2  * Copyright 2011 gitblit.com.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
1f9dae 16 package com.gitblit.wicket.pages;
5fe7df 17
85c2e6 18 import java.io.Serializable;
bc9d4a 19 import java.text.MessageFormat;
3e087a 20 import java.util.ArrayList;
JM 21 import java.util.Arrays;
a7db57 22 import java.util.Date;
eb870f 23 import java.util.HashMap;
JM 24 import java.util.LinkedHashSet;
5fe7df 25 import java.util.List;
JM 26 import java.util.Map;
eb870f 27 import java.util.Set;
5fe7df 28
98ce17 29 import org.apache.wicket.Component;
5fe7df 30 import org.apache.wicket.PageParameters;
41aaf7 31 import org.apache.wicket.RestartResponseException;
79d324 32 import org.apache.wicket.behavior.SimpleAttributeModifier;
5fe7df 33 import org.apache.wicket.markup.html.basic.Label;
3e087a 34 import org.apache.wicket.markup.html.form.DropDownChoice;
JM 35 import org.apache.wicket.markup.html.form.TextField;
c22722 36 import org.apache.wicket.markup.html.link.ExternalLink;
98ce17 37 import org.apache.wicket.markup.html.panel.Fragment;
3e087a 38 import org.apache.wicket.model.IModel;
JM 39 import org.apache.wicket.model.Model;
77e1d2 40 import org.apache.wicket.request.target.basic.RedirectRequestTarget;
9bc17d 41 import org.eclipse.jgit.diff.DiffEntry.ChangeType;
98ce17 42 import org.eclipse.jgit.lib.PersonIdent;
5fe7df 43 import org.eclipse.jgit.lib.Repository;
JM 44 import org.eclipse.jgit.revwalk.RevCommit;
79d324 45 import org.slf4j.Logger;
JM 46 import org.slf4j.LoggerFactory;
5fe7df 47
33d8d8 48 import com.gitblit.Constants;
79d324 49 import com.gitblit.GitBlitException;
87cc1e 50 import com.gitblit.Keys;
7a401a 51 import com.gitblit.extensions.RepositoryNavLinkExtension;
JM 52 import com.gitblit.models.NavLink;
53 import com.gitblit.models.NavLink.ExternalNavLink;
54 import com.gitblit.models.NavLink.PageNavLink;
1e1b85 55 import com.gitblit.models.ProjectModel;
85f639 56 import com.gitblit.models.RefModel;
1f9dae 57 import com.gitblit.models.RepositoryModel;
eb870f 58 import com.gitblit.models.SubmoduleModel;
1e1b85 59 import com.gitblit.models.UserModel;
79d324 60 import com.gitblit.models.UserRepositoryPreferences;
7bf6e1 61 import com.gitblit.servlet.PagesServlet;
JM 62 import com.gitblit.servlet.SyndicationServlet;
4e1930 63 import com.gitblit.utils.ArrayUtils;
fef234 64 import com.gitblit.utils.BugtraqProcessor;
79d324 65 import com.gitblit.utils.DeepCopier;
5fe7df 66 import com.gitblit.utils.JGitUtils;
c332d9 67 import com.gitblit.utils.ModelUtils;
ff7d3c 68 import com.gitblit.utils.RefLogUtils;
f5d0ad 69 import com.gitblit.utils.StringUtils;
a7db57 70 import com.gitblit.wicket.CacheControl;
1f9dae 71 import com.gitblit.wicket.GitBlitWebSession;
ed295f 72 import com.gitblit.wicket.SessionlessForm;
1f9dae 73 import com.gitblit.wicket.WicketUtils;
JM 74 import com.gitblit.wicket.panels.LinkPanel;
9e5bee 75 import com.gitblit.wicket.panels.NavigationPanel;
5fe7df 76 import com.gitblit.wicket.panels.RefsPanel;
86bdc2 77 import com.google.common.base.Optional;
5fe7df 78
6ef8d7 79 public abstract class RepositoryPage extends RootPage {
5dd805 80
0365f6 81     protected final Logger logger = LoggerFactory.getLogger(getClass());
5fe7df 82
79d324 83     private final String PARAM_STAR = "star";
5dd805 84
13a3f5 85     protected final String projectName;
5fe7df 86     protected final String repositoryName;
bc9d4a 87     protected final String objectId;
5dd805 88
2a7306 89     private transient Repository r;
bc9d4a 90
2a7306 91     private RepositoryModel m;
5fe7df 92
eb870f 93     private Map<String, SubmoduleModel> submodules;
5dd805 94
eb9979 95     private boolean showAdmin;
33622b 96     private boolean isOwner;
5dd805 97
cebf45 98     public RepositoryPage(PageParameters params) {
5fe7df 99         super(params);
7d35e2 100         repositoryName = WicketUtils.getRepositoryName(params);
5e3521 101         String root = StringUtils.getFirstPathElement(repositoryName);
1e1b85 102         if (StringUtils.isEmpty(root)) {
99d0d4 103             projectName = app().settings().getString(Keys.web.repositoryRootGroupName, "main");
1e1b85 104         } else {
JM 105             projectName = root;
13a3f5 106         }
7d35e2 107         objectId = WicketUtils.getObject(params);
5dd805 108
e92c6d 109         if (StringUtils.isEmpty(repositoryName)) {
JM 110             error(MessageFormat.format(getString("gb.repositoryNotSpecifiedFor"), getPageName()), true);
111         }
112
9e7b14 113         if (!getRepositoryModel().hasCommits && getClass() != EmptyRepositoryPage.class) {
41aaf7 114             throw new RestartResponseException(EmptyRepositoryPage.class, params);
e92c6d 115         }
5dd805 116
e92c6d 117         if (getRepositoryModel().isCollectingGarbage) {
JM 118             error(MessageFormat.format(getString("gb.busyCollectingGarbage"), getRepositoryModel().name), true);
119         }
5dd805 120
85f639 121         if (objectId != null) {
LM 122             RefModel branch = null;
123             if ((branch = JGitUtils.getBranch(getRepository(), objectId)) != null) {
6d23ca 124                 UserModel user = GitBlitWebSession.get().getUser();
JM 125                 if (user == null) {
126                     // workaround until get().getUser() is reviewed throughout the app
127                     user = UserModel.ANONYMOUS;
128                 }
92d477 129                 boolean canAccess = user.canView(getRepositoryModel(),
85f639 130                                 branch.reference.getName());
LM 131                 if (!canAccess) {
13f880 132                     error(getString("gb.accessDenied"), true);
79d324 133                 }
JM 134             }
135         }
5dd805 136
79d324 137         if (params.containsKey(PARAM_STAR)) {
JM 138             // set starred state
139             boolean star = params.getBoolean(PARAM_STAR);
140             UserModel user = GitBlitWebSession.get().getUser();
141             if (user != null && user.isAuthenticated) {
142                 UserRepositoryPreferences prefs = user.getPreferences().getRepositoryPreferences(getRepositoryModel().name);
143                 prefs.starred = star;
144                 try {
5ae529 145                     app().gitblit().reviseUser(user.username, user);
79d324 146                 } catch (GitBlitException e) {
JM 147                     logger.error("Failed to update user " + user.username, e);
148                     error(getString("gb.failedToUpdateUser"), false);
85f639 149                 }
LM 150             }
82df52 151         }
9e7b14 152
JM 153         showAdmin = false;
154         if (app().settings().getBoolean(Keys.web.authenticateAdminPages, true)) {
155             boolean allowAdmin = app().settings().getBoolean(Keys.web.allowAdministration, false);
156             showAdmin = allowAdmin && GitBlitWebSession.get().canAdmin();
157         } else {
158             showAdmin = app().settings().getBoolean(Keys.web.allowAdministration, false);
159         }
160         isOwner = GitBlitWebSession.get().isLoggedIn()
161                 && (getRepositoryModel().isOwner(GitBlitWebSession.get().getUsername()));
82df52 162
7a401a 163         // register the available navigation links for this page and user
JM 164         List<NavLink> navLinks = registerNavLinks();
2a7306 165
7a401a 166         // standard navigation links
JM 167         NavigationPanel navigationPanel = new NavigationPanel("repositoryNavPanel", getRepoNavPageClass(), navLinks);
9e5bee 168         add(navigationPanel);
8c9a20 169
c22722 170         add(new ExternalLink("syndication", SyndicationServlet.asLink(getRequest()
44c005 171                 .getRelativePathPrefixToContextRoot(), getRepositoryName(), null, 0)));
3e087a 172
JM 173         // add floating search form
44c005 174         SearchForm searchForm = new SearchForm("searchForm", getRepositoryName());
3e087a 175         add(searchForm);
JM 176         searchForm.setTranslatedAttributes();
bc9d4a 177
3e087a 178         // set stateless page preference
5fe7df 179         setStatelessHint(true);
3e087a 180     }
5dd805 181
6ef8d7 182     @Override
JM 183     protected Class<? extends BasePage> getRootNavPageClass() {
184         return RepositoriesPage.class;
185     }
9e5bee 186
6ef8d7 187     protected Class<? extends BasePage> getRepoNavPageClass() {
JM 188         return getClass();
189     }
5dd805 190
fef234 191     protected BugtraqProcessor bugtraqProcessor() {
JM 192         return new BugtraqProcessor(app().settings());
99d0d4 193     }
JM 194
7a401a 195     private List<NavLink> registerNavLinks() {
062519 196         Repository r = getRepository();
JM 197         RepositoryModel model = getRepositoryModel();
198
9e5bee 199         PageParameters params = null;
062519 200         PageParameters objectParams = null;
9e5bee 201         if (!StringUtils.isEmpty(repositoryName)) {
44c005 202             params = WicketUtils.newRepositoryParameter(getRepositoryName());
062519 203             objectParams = params;
JM 204
205             // preserve the objectid iff the objectid directly (or indirectly) refers to a ref
5d5e55 206             if (isCommitPage() && !StringUtils.isEmpty(objectId)) {
062519 207                 RevCommit commit = JGitUtils.getCommit(r, objectId);
5d5e55 208                 if (commit != null) {
JM 209                     String bestId = getBestCommitId(commit);
210                     if (!commit.getName().equals(bestId)) {
211                         objectParams = WicketUtils.newObjectParameter(getRepositoryName(), bestId);
212                     }
062519 213                 }
JM 214             }
9e5bee 215         }
7a401a 216         List<NavLink> navLinks = new ArrayList<NavLink>();
9e5bee 217
6ef8d7 218
9e5bee 219         // standard links
ff7d3c 220         if (RefLogUtils.getRefLogBranch(r) == null) {
7a401a 221             navLinks.add(new PageNavLink("gb.summary", SummaryPage.class, params));
6ef8d7 222         } else {
7a401a 223             navLinks.add(new PageNavLink("gb.summary", SummaryPage.class, params));
9e7b14 224             //            pages.put("overview", new PageRegistration("gb.overview", OverviewPage.class, params));
7a401a 225             navLinks.add(new PageNavLink("gb.reflog", ReflogPage.class, params));
5dd805 226         }
9e7b14 227
JM 228         if (!model.hasCommits) {
229             return navLinks;
230         }
231
062519 232         navLinks.add(new PageNavLink("gb.commits", LogPage.class, objectParams));
JM 233         navLinks.add(new PageNavLink("gb.tree", TreePage.class, objectParams));
3b23dc 234         if (app().tickets().isReady() && (app().tickets().isAcceptingNewTickets(model) || app().tickets().hasTickets(model))) {
44c005 235             PageParameters tParams = WicketUtils.newOpenTicketsParameter(getRepositoryName());
7a401a 236             navLinks.add(new PageNavLink("gb.tickets", TicketsPage.class, tParams));
5e3521 237         }
062519 238         navLinks.add(new PageNavLink("gb.docs", DocsPage.class, objectParams, true));
99d0d4 239         if (app().settings().getBoolean(Keys.web.allowForking, true)) {
7a401a 240             navLinks.add(new PageNavLink("gb.forks", ForksPage.class, params, true));
428b22 241         }
7a401a 242         navLinks.add(new PageNavLink("gb.compare", ComparePage.class, params, true));
9e5bee 243
JM 244         // conditional links
7a401a 245         // per-repository extra navlinks
11924d 246         if (JGitUtils.getPagesBranch(r) != null) {
7a401a 247             ExternalNavLink pagesLink = new ExternalNavLink("gb.pages", PagesServlet.asLink(
44c005 248                     getRequest().getRelativePathPrefixToContextRoot(), getRepositoryName(), null), true);
7a401a 249             navLinks.add(pagesLink);
JM 250         }
251
252         UserModel user = UserModel.ANONYMOUS;
253         if (GitBlitWebSession.get().isLoggedIn()) {
254             user = GitBlitWebSession.get().getUser();
255         }
256
257         // add repository nav link extensions
258         List<RepositoryNavLinkExtension> extensions = app().plugins().getExtensions(RepositoryNavLinkExtension.class);
259         for (RepositoryNavLinkExtension ext : extensions) {
260             navLinks.addAll(ext.getNavLinks(user, model));
11924d 261         }
JM 262
7a401a 263         return navLinks;
9e5bee 264     }
5dd805 265
1f52c8 266     protected boolean allowForkControls() {
99d0d4 267         return app().settings().getBoolean(Keys.web.allowForking, true);
1f52c8 268     }
9e5bee 269
1f5915 270     @Override
11924d 271     protected void setupPage(String repositoryName, String pageName) {
1e1b85 272         String projectName = StringUtils.getFirstPathElement(repositoryName);
99d0d4 273         ProjectModel project = app().projects().getProjectModel(projectName);
1e1b85 274         if (project.isUserProject()) {
JM 275             // user-as-project
276             add(new LinkPanel("projectTitle", null, project.getDisplayName(),
277                     UserPage.class, WicketUtils.newUsernameParameter(project.name.substring(1))));
ccab3a 278         } else {
1e1b85 279             // project
JM 280             add(new LinkPanel("projectTitle", null, project.name,
281                     ProjectPage.class, WicketUtils.newProjectParameter(project.name)));
282         }
5dd805 283
1e1b85 284         String name = StringUtils.stripDotGit(repositoryName);
JM 285         if (!StringUtils.isEmpty(projectName) && name.startsWith(projectName)) {
286             name = name.substring(projectName.length() + 1);
287         }
288         add(new LinkPanel("repositoryName", null, name, SummaryPage.class,
289                 WicketUtils.newRepositoryParameter(repositoryName)));
5dd805 290
eb1405 291         UserModel user = GitBlitWebSession.get().getUser();
616590 292         if (user == null) {
JM 293             user = UserModel.ANONYMOUS;
294         }
eb1405 295
1e1b85 296         // indicate origin repository
JM 297         RepositoryModel model = getRepositoryModel();
298         if (StringUtils.isEmpty(model.originRepository)) {
c44dd0 299             if (model.isMirror) {
86bdc2 300                 add(new Fragment("repoIcon", "mirrorIconFragment", this));
c44dd0 301                 Fragment mirrorFrag = new Fragment("originRepository", "mirrorFragment", this);
JM 302                 Label lbl = new Label("originRepository", MessageFormat.format(getString("gb.mirrorOf"), "<b>" + model.origin + "</b>"));
303                 mirrorFrag.add(lbl.setEscapeModelStrings(false));
304                 add(mirrorFrag);
305             } else {
86bdc2 306                 if (model.isBare) {
JM 307                     add(new Fragment("repoIcon", "repoIconFragment", this));
308                 } else {
309                     add(new Fragment("repoIcon", "cloneIconFragment", this));
310                 }
311                 add(new Label("originRepository", Optional.of(model.description).or("")));
c44dd0 312             }
1e1b85 313         } else {
99d0d4 314             RepositoryModel origin = app().repositories().getRepositoryModel(model.originRepository);
eb1405 315             if (origin == null) {
86bdc2 316                 // no origin repository, show description if available
JM 317                 if (model.isBare) {
318                     add(new Fragment("repoIcon", "repoIconFragment", this));
319                 } else {
320                     add(new Fragment("repoIcon", "cloneIconFragment", this));
321                 }
322                 add(new Label("originRepository", Optional.of(model.description).or("")));
20714a 323             } else if (!user.canView(origin)) {
eb1405 324                 // show origin repository without link
86bdc2 325                 add(new Fragment("repoIcon", "forkIconFragment", this));
eb1405 326                 Fragment forkFrag = new Fragment("originRepository", "originFragment", this);
JM 327                 forkFrag.add(new Label("originRepository", StringUtils.stripDotGit(model.originRepository)));
328                 add(forkFrag);
329             } else {
330                 // link to origin repository
86bdc2 331                 add(new Fragment("repoIcon", "forkIconFragment", this));
eb1405 332                 Fragment forkFrag = new Fragment("originRepository", "originFragment", this);
5dd805 333                 forkFrag.add(new LinkPanel("originRepository", null, StringUtils.stripDotGit(model.originRepository),
eb1405 334                         SummaryPage.class, WicketUtils.newRepositoryParameter(model.originRepository)));
JM 335                 add(forkFrag);
336             }
1e1b85 337         }
5dd805 338
5e3521 339         // new ticket button
JM 340         if (user.isAuthenticated && app().tickets().isAcceptingNewTickets(getRepositoryModel())) {
341             String newTicketUrl = getRequestCycle().urlFor(NewTicketPage.class, WicketUtils.newRepositoryParameter(repositoryName)).toString();
342             addToolbarButton("newTicketLink", "fa fa-ticket", getString("gb.new"), newTicketUrl);
343         } else {
344             add(new Label("newTicketLink").setVisible(false));
345         }
346
9b26b7 347         // (un)star link allows a user to star a repository
9e7b14 348         if (user.isAuthenticated && model.hasCommits) {
79d324 349             PageParameters starParams = DeepCopier.copy(getPageParameters());
JM 350             starParams.put(PARAM_STAR, !user.getPreferences().isStarredRepository(model.name));
351             String toggleStarUrl = getRequestCycle().urlFor(getClass(), starParams).toString();
352             if (user.getPreferences().isStarredRepository(model.name)) {
353                 // show unstar button
354                 add(new Label("starLink").setVisible(false));
355                 addToolbarButton("unstarLink", "icon-star-empty", getString("gb.unstar"), toggleStarUrl);
356             } else {
357                 // show star button
358                 addToolbarButton("starLink", "icon-star", getString("gb.star"), toggleStarUrl);
359                 add(new Label("unstarLink").setVisible(false));
360             }
361         } else {
9b26b7 362             // anonymous user
79d324 363             add(new Label("starLink").setVisible(false));
JM 364             add(new Label("unstarLink").setVisible(false));
365         }
366
eb1405 367         // fork controls
253957 368         if (!allowForkControls() || !user.isAuthenticated) {
eb1405 369             // must be logged-in to fork, hide all fork controls
JM 370             add(new ExternalLink("forkLink", "").setVisible(false));
371             add(new ExternalLink("myForkLink", "").setVisible(false));
1e1b85 372         } else {
99d0d4 373             String fork = app().repositories().getFork(user.username, model.name);
c332d9 374             String userRepo = ModelUtils.getPersonalPath(user.username) + "/" + StringUtils.stripDotGit(StringUtils.getLastPathElement(model.name));
JM 375             boolean hasUserRepo = app().repositories().hasRepository(userRepo);
eb1405 376             boolean hasFork = fork != null;
c332d9 377             boolean canFork = user.canFork(model) && model.hasCommits && !hasUserRepo;
eb1405 378
JM 379             if (hasFork || !canFork) {
380                 // user not allowed to fork or fork already exists or repo forbids forking
381                 add(new ExternalLink("forkLink", "").setVisible(false));
5dd805 382
eb1405 383                 if (hasFork && !fork.equals(model.name)) {
JM 384                     // user has fork, view my fork link
385                     String url = getRequestCycle().urlFor(SummaryPage.class, WicketUtils.newRepositoryParameter(fork)).toString();
386                     add(new ExternalLink("myForkLink", url));
387                 } else {
388                     // no fork, hide view my fork link
389                     add(new ExternalLink("myForkLink", "").setVisible(false));
390                 }
391             } else if (canFork) {
392                 // can fork and we do not have one
393                 add(new ExternalLink("myForkLink", "").setVisible(false));
1f52c8 394                 String url = getRequestCycle().urlFor(ForkPage.class, WicketUtils.newRepositoryParameter(model.name)).toString();
JM 395                 add(new ExternalLink("forkLink", url));
1e1b85 396             }
JM 397         }
5dd805 398
067a2f 399         if (showAdmin || isOwner) {
JM 400             String url = getRequestCycle().urlFor(EditRepositoryPage.class, WicketUtils.newRepositoryParameter(model.name)).toString();
5dd805 401             add(new ExternalLink("editLink", url));
067a2f 402         } else {
JM 403             add(new Label("editLink").setVisible(false));
404         }
5dd805 405
1f5915 406         super.setupPage(repositoryName, pageName);
8c9a20 407     }
5dd805 408
79d324 409     protected void addToolbarButton(String wicketId, String iconClass, String label, String url) {
JM 410         Fragment button = new Fragment(wicketId, "toolbarLinkFragment", this);
411         Label icon = new Label("icon");
412         WicketUtils.setCssClass(icon, iconClass);
413         button.add(icon);
414         button.add(new Label("label", label));
415         button.add(new SimpleAttributeModifier("href", url));
416         add(button);
417     }
8c9a20 418
JM 419     protected void addSyndicationDiscoveryLink() {
420         add(WicketUtils.syndicationDiscoveryLink(SyndicationServlet.getTitle(repositoryName,
421                 objectId), SyndicationServlet.asLink(getRequest()
422                 .getRelativePathPrefixToContextRoot(), repositoryName, objectId, 0)));
5fe7df 423     }
JM 424
425     protected Repository getRepository() {
426         if (r == null) {
99d0d4 427             Repository r = app().repositories().getRepository(repositoryName);
7ba0ec 428             if (r == null) {
6caa93 429                 error(getString("gb.canNotLoadRepository") + " " + repositoryName, true);
7ba0ec 430                 return null;
JM 431             }
432             this.r = r;
5fe7df 433         }
JM 434         return r;
435     }
bc9d4a 436
f97bf0 437     protected RepositoryModel getRepositoryModel() {
JM 438         if (m == null) {
99d0d4 439             RepositoryModel model = app().repositories().getRepositoryModel(
2a7306 440                     GitBlitWebSession.get().getUser(), repositoryName);
dfb889 441             if (model == null) {
99d0d4 442                 if (app().repositories().hasRepository(repositoryName, true)) {
d97e52 443                     // has repository, but unauthorized
JM 444                     authenticationError(getString("gb.unauthorizedAccessForRepository") + " " + repositoryName);
445                 } else {
446                     // does not have repository
447                     error(getString("gb.canNotLoadRepository") + " " + repositoryName, true);
448                 }
00afd7 449                 return null;
dfb889 450             }
JM 451             m = model;
f97bf0 452         }
JM 453         return m;
44c005 454     }
JM 455
456     protected String getRepositoryName() {
457         return getRepositoryModel().name;
bc9d4a 458     }
dfb889 459
bc9d4a 460     protected RevCommit getCommit() {
JM 461         RevCommit commit = JGitUtils.getCommit(r, objectId);
462         if (commit == null) {
6caa93 463             error(MessageFormat.format(getString("gb.failedToFindCommit"),
1078f8 464                     objectId, repositoryName, getPageName()), null, LogPage.class,
JM 465                     WicketUtils.newRepositoryParameter(repositoryName));
bc9d4a 466         }
eb870f 467         getSubmodules(commit);
bc9d4a 468         return commit;
f97bf0 469     }
5dd805 470
ab1e11 471     protected String getBestCommitId(RevCommit commit) {
JM 472         String head = null;
473         try {
474             head = r.resolve(getRepositoryModel().HEAD).getName();
475         } catch (Exception e) {
476         }
477
478         String id = commit.getName();
479         if (!StringUtils.isEmpty(head) && head.equals(id)) {
480             // match default branch
481             return Repository.shortenRefName(getRepositoryModel().HEAD);
482         }
483
484         // find first branch match
485         for (RefModel ref : JGitUtils.getLocalBranches(r, false, -1)) {
486             if (ref.getObjectId().getName().equals(id)) {
062519 487                 return Repository.shortenRefName(ref.getName());
ab1e11 488             }
JM 489         }
490
491         // return sha
492         return id;
493     }
494
5dd805 495     protected Map<String, SubmoduleModel> getSubmodules(RevCommit commit) {
eb870f 496         if (submodules == null) {
JM 497             submodules = new HashMap<String, SubmoduleModel>();
498             for (SubmoduleModel model : JGitUtils.getSubmodules(r, commit.getTree())) {
499                 submodules.put(model.path, model);
500             }
501         }
502         return submodules;
503     }
5dd805 504
eb870f 505     protected SubmoduleModel getSubmodule(String path) {
80d636 506         SubmoduleModel model = null;
JM 507         if (submodules != null) {
508             model = submodules.get(path);
509         }
eb870f 510         if (model == null) {
JM 511             // undefined submodule?!
512             model = new SubmoduleModel(path.substring(path.lastIndexOf('/') + 1), path, path);
513             model.hasSubmodule = false;
514             model.gitblitPath = model.name;
515             return model;
516         } else {
517             // extract the repository name from the clone url
99d0d4 518             List<String> patterns = app().settings().getStrings(Keys.git.submoduleUrlPatterns);
eb870f 519             String submoduleName = StringUtils.extractRepositoryPath(model.url, patterns.toArray(new String[0]));
5dd805 520
eb870f 521             // determine the current path for constructing paths relative
JM 522             // to the current repository
523             String currentPath = "";
524             if (repositoryName.indexOf('/') > -1) {
525                 currentPath = repositoryName.substring(0, repositoryName.lastIndexOf('/') + 1);
526             }
527
528             // try to locate the submodule repository
529             // prefer bare to non-bare names
530             List<String> candidates = new ArrayList<String>();
531
532             // relative
533             candidates.add(currentPath + StringUtils.stripDotGit(submoduleName));
534             candidates.add(candidates.get(candidates.size() - 1) + ".git");
535
536             // relative, no subfolder
537             if (submoduleName.lastIndexOf('/') > -1) {
538                 String name = submoduleName.substring(submoduleName.lastIndexOf('/') + 1);
539                 candidates.add(currentPath + StringUtils.stripDotGit(name));
5e3771 540                 candidates.add(candidates.get(candidates.size() - 1) + ".git");
eb870f 541             }
JM 542
543             // absolute
544             candidates.add(StringUtils.stripDotGit(submoduleName));
545             candidates.add(candidates.get(candidates.size() - 1) + ".git");
546
547             // absolute, no subfolder
548             if (submoduleName.lastIndexOf('/') > -1) {
549                 String name = submoduleName.substring(submoduleName.lastIndexOf('/') + 1);
550                 candidates.add(StringUtils.stripDotGit(name));
551                 candidates.add(candidates.get(candidates.size() - 1) + ".git");
552             }
553
554             // create a unique, ordered set of candidate paths
555             Set<String> paths = new LinkedHashSet<String>(candidates);
556             for (String candidate : paths) {
99d0d4 557                 if (app().repositories().hasRepository(candidate)) {
eb870f 558                     model.hasSubmodule = true;
JM 559                     model.gitblitPath = candidate;
560                     return model;
561                 }
562             }
5dd805 563
eb870f 564             // we do not have a copy of the submodule, but we need a path
JM 565             model.gitblitPath = candidates.get(0);
566             return model;
5dd805 567         }
eb870f 568     }
5fe7df 569
008322 570     protected String getShortObjectId(String objectId) {
99d0d4 571         return objectId.substring(0, app().settings().getInteger(Keys.web.shortCommitIdLength, 6));
008322 572     }
JM 573
5fe7df 574     protected void addRefs(Repository r, RevCommit c) {
1e1b85 575         add(new RefsPanel("refsPanel", repositoryName, c, JGitUtils.getAllRefs(r, getRepositoryModel().showRemoteBranches)));
5fe7df 576     }
JM 577
cd9461 578     protected void addFullText(String wicketId, String text) {
BKI 579         RepositoryModel model = getRepositoryModel();
fef234 580         String content = bugtraqProcessor().processCommitMessage(r, model, text);
cd9461 581         String html;
BKI 582         switch (model.commitMessageRenderer) {
583         case MARKDOWN:
7fdc29 584             String safeContent = app().xssFilter().relaxed(content);
JM 585             html = MessageFormat.format("<div class='commit_message'>{0}</div>", safeContent);
cd9461 586             break;
BKI 587         default:
588             html = MessageFormat.format("<pre class='commit_message'>{0}</pre>", content);
589             break;
f339f5 590         }
JM 591         add(new Label(wicketId, html).setEscapeModelStrings(false));
5fe7df 592     }
1e47ab 593
cebf45 594     protected abstract String getPageName();
5fe7df 595
5d5e55 596     protected boolean isCommitPage() {
JM 597         return false;
598     }
599
2a7306 600     protected Component createPersonPanel(String wicketId, PersonIdent identity,
33d8d8 601             Constants.SearchType searchType) {
e11f48 602         String name = identity == null ? "" : identity.getName();
JM 603         String address = identity == null ? "" : identity.getEmailAddress();
ac7e9a 604         name = StringUtils.removeNewlines(name);
JM 605         address = StringUtils.removeNewlines(address);
99d0d4 606         boolean showEmail = app().settings().getBoolean(Keys.web.showEmailAddresses, false);
e11f48 607         if (!showEmail || StringUtils.isEmpty(name) || StringUtils.isEmpty(address)) {
JM 608             String value = name;
98ce17 609             if (StringUtils.isEmpty(value)) {
f5d0ad 610                 if (showEmail) {
e11f48 611                     value = address;
f5d0ad 612                 } else {
JM 613                     value = getString("gb.missingUsername");
614                 }
98ce17 615             }
JM 616             Fragment partial = new Fragment(wicketId, "partialPersonIdent", this);
4e1930 617             LinkPanel link = new LinkPanel("personName", "list", value, GitSearchPage.class,
2a7306 618                     WicketUtils.newSearchParameter(repositoryName, objectId, value, searchType));
98ce17 619             setPersonSearchTooltip(link, value, searchType);
JM 620             partial.add(link);
621             return partial;
622         } else {
623             Fragment fullPerson = new Fragment(wicketId, "fullPersonIdent", this);
4e1930 624             LinkPanel nameLink = new LinkPanel("personName", "list", name, GitSearchPage.class,
e11f48 625                     WicketUtils.newSearchParameter(repositoryName, objectId, name, searchType));
JM 626             setPersonSearchTooltip(nameLink, name, searchType);
98ce17 627             fullPerson.add(nameLink);
3e087a 628
227736 629             LinkPanel addressLink = new LinkPanel("personAddress", "hidden-phone list", "<" + address + ">",
4e1930 630                     GitSearchPage.class, WicketUtils.newSearchParameter(repositoryName, objectId,
e11f48 631                             address, searchType));
JM 632             setPersonSearchTooltip(addressLink, address, searchType);
98ce17 633             fullPerson.add(addressLink);
JM 634             return fullPerson;
635         }
636     }
3e087a 637
11924d 638     protected void setPersonSearchTooltip(Component component, String value,
JM 639             Constants.SearchType searchType) {
33d8d8 640         if (searchType.equals(Constants.SearchType.AUTHOR)) {
1e8390 641             WicketUtils.setHtmlTooltip(component, getString("gb.searchForAuthor") + " " + value);
33d8d8 642         } else if (searchType.equals(Constants.SearchType.COMMITTER)) {
1e8390 643             WicketUtils.setHtmlTooltip(component, getString("gb.searchForCommitter") + " " + value);
98ce17 644         }
JM 645     }
3e087a 646
9bc17d 647     protected void setChangeTypeTooltip(Component container, ChangeType type) {
JM 648         switch (type) {
649         case ADD:
1e8390 650             WicketUtils.setHtmlTooltip(container, getString("gb.addition"));
9bc17d 651             break;
JM 652         case COPY:
653         case RENAME:
1e8390 654             WicketUtils.setHtmlTooltip(container, getString("gb.rename"));
9bc17d 655             break;
JM 656         case DELETE:
1e8390 657             WicketUtils.setHtmlTooltip(container, getString("gb.deletion"));
9bc17d 658             break;
JM 659         case MODIFY:
1e8390 660             WicketUtils.setHtmlTooltip(container, getString("gb.modification"));
9bc17d 661             break;
JM 662         }
663     }
3e087a 664
1e47ab 665     @Override
JM 666     protected void onBeforeRender() {
667         // dispose of repository object
668         if (r != null) {
669             r.close();
670             r = null;
671         }
672         // setup page header and footer
44c005 673         setupPage(getRepositoryName(), "/ " + getPageName());
1e47ab 674         super.onBeforeRender();
5fe7df 675     }
5dd805 676
a7db57 677     @Override
JM 678     protected void setLastModified() {
679         if (getClass().isAnnotationPresent(CacheControl.class)) {
680             CacheControl cacheControl = getClass().getAnnotation(CacheControl.class);
681             switch (cacheControl.value()) {
682             case REPOSITORY:
683                 RepositoryModel repository = getRepositoryModel();
684                 if (repository != null) {
685                     setLastModified(repository.lastChange);
686                 }
687                 break;
688             case COMMIT:
689                 RevCommit commit = getCommit();
690                 if (commit != null) {
691                     Date commitDate = JGitUtils.getCommitDate(commit);
692                     setLastModified(commitDate);
693                 }
694                 break;
695             default:
696                 super.setLastModified();
697             }
698         }
699     }
5fe7df 700
JM 701     protected PageParameters newRepositoryParameter() {
698678 702         return WicketUtils.newRepositoryParameter(repositoryName);
5fe7df 703     }
7ba0ec 704
5fe7df 705     protected PageParameters newCommitParameter() {
ef5c58 706         return WicketUtils.newObjectParameter(repositoryName, objectId);
5fe7df 707     }
7ba0ec 708
5fe7df 709     protected PageParameters newCommitParameter(String commitId) {
ef5c58 710         return WicketUtils.newObjectParameter(repositoryName, commitId);
5fe7df 711     }
JM 712
33622b 713     public boolean isShowAdmin() {
JM 714         return showAdmin;
715     }
5dd805 716
33622b 717     public boolean isOwner() {
JM 718         return isOwner;
a7317a 719     }
JM 720
ed295f 721     private class SearchForm extends SessionlessForm<Void> implements Serializable {
3e087a 722         private static final long serialVersionUID = 1L;
JM 723
724         private final String repositoryName;
725
726         private final IModel<String> searchBoxModel = new Model<String>("");
727
11924d 728         private final IModel<Constants.SearchType> searchTypeModel = new Model<Constants.SearchType>(
JM 729                 Constants.SearchType.COMMIT);
3e087a 730
JM 731         public SearchForm(String id, String repositoryName) {
ed295f 732             super(id, RepositoryPage.this.getClass(), RepositoryPage.this.getPageParameters());
3e087a 733             this.repositoryName = repositoryName;
11924d 734             DropDownChoice<Constants.SearchType> searchType = new DropDownChoice<Constants.SearchType>(
JM 735                     "searchType", Arrays.asList(Constants.SearchType.values()));
3e087a 736             searchType.setModel(searchTypeModel);
99d0d4 737             add(searchType.setVisible(app().settings().getBoolean(Keys.web.showSearchTypeSelection, false)));
3e087a 738             TextField<String> searchBox = new TextField<String>("searchBox", searchBoxModel);
JM 739             add(searchBox);
740         }
bc9d4a 741
JM 742         void setTranslatedAttributes() {
3e087a 743             WicketUtils.setHtmlTooltip(get("searchType"), getString("gb.searchTypeTooltip"));
9e5bee 744             WicketUtils.setHtmlTooltip(get("searchBox"),
JM 745                     MessageFormat.format(getString("gb.searchTooltip"), repositoryName));
bc9d4a 746             WicketUtils.setInputPlaceholder(get("searchBox"), getString("gb.search"));
3e087a 747         }
JM 748
749         @Override
750         public void onSubmit() {
33d8d8 751             Constants.SearchType searchType = searchTypeModel.getObject();
3e087a 752             String searchString = searchBoxModel.getObject();
6ef8d7 753             if (StringUtils.isEmpty(searchString)) {
5dd805 754                 // redirect to self to avoid wicket page update bug
307910 755                 String absoluteUrl = getCanonicalUrl();
6ef8d7 756                 getRequestCycle().setRequestTarget(new RedirectRequestTarget(absoluteUrl));
8c5d72 757                 return;
JM 758             }
33d8d8 759             for (Constants.SearchType type : Constants.SearchType.values()) {
3e087a 760                 if (searchString.toLowerCase().startsWith(type.name().toLowerCase() + ":")) {
JM 761                     searchType = type;
2a7306 762                     searchString = searchString.substring(type.name().toLowerCase().length() + 1)
JM 763                             .trim();
3e087a 764                     break;
JM 765                 }
766             }
4e1930 767             Class<? extends BasePage> searchPageClass = GitSearchPage.class;
99d0d4 768             RepositoryModel model = app().repositories().getRepositoryModel(repositoryName);
JM 769             if (app().settings().getBoolean(Keys.web.allowLuceneIndexing, true)
7db092 770                     && !ArrayUtils.isEmpty(model.indexedBranches)) {
4e1930 771                 // this repository is Lucene-indexed
JM 772                 searchPageClass = LuceneSearchPage.class;
773             }
77e1d2 774             // use an absolute url to workaround Wicket-Tomcat problems with
JM 775             // mounted url parameters (issue-111)
776             PageParameters params = WicketUtils.newSearchParameter(repositoryName, null, searchString, searchType);
307910 777             String absoluteUrl = getCanonicalUrl(searchPageClass, params);
77e1d2 778             getRequestCycle().setRequestTarget(new RedirectRequestTarget(absoluteUrl));
3e087a 779         }
JM 780     }
428b22 781 }