Paul Martin
2016-03-31 10f798a1fab5f7b23f498e2746b1b6a50010a288
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) {
10f798 272         
PM 273         //This method should only be called once in the page lifecycle.
274         //However, it must be called after the constructor has run, hence not in onInitialize
275         //It may be attempted to be called again if an info or error message is displayed.
276         if (get("projectTitle") != null) { return; }
277         
1e1b85 278         String projectName = StringUtils.getFirstPathElement(repositoryName);
99d0d4 279         ProjectModel project = app().projects().getProjectModel(projectName);
10f798 280
1e1b85 281         if (project.isUserProject()) {
JM 282             // user-as-project
283             add(new LinkPanel("projectTitle", null, project.getDisplayName(),
284                     UserPage.class, WicketUtils.newUsernameParameter(project.name.substring(1))));
ccab3a 285         } else {
1e1b85 286             // project
JM 287             add(new LinkPanel("projectTitle", null, project.name,
288                     ProjectPage.class, WicketUtils.newProjectParameter(project.name)));
289         }
5dd805 290
1e1b85 291         String name = StringUtils.stripDotGit(repositoryName);
JM 292         if (!StringUtils.isEmpty(projectName) && name.startsWith(projectName)) {
293             name = name.substring(projectName.length() + 1);
294         }
295         add(new LinkPanel("repositoryName", null, name, SummaryPage.class,
296                 WicketUtils.newRepositoryParameter(repositoryName)));
5dd805 297
eb1405 298         UserModel user = GitBlitWebSession.get().getUser();
616590 299         if (user == null) {
JM 300             user = UserModel.ANONYMOUS;
301         }
eb1405 302
1e1b85 303         // indicate origin repository
JM 304         RepositoryModel model = getRepositoryModel();
305         if (StringUtils.isEmpty(model.originRepository)) {
c44dd0 306             if (model.isMirror) {
86bdc2 307                 add(new Fragment("repoIcon", "mirrorIconFragment", this));
c44dd0 308                 Fragment mirrorFrag = new Fragment("originRepository", "mirrorFragment", this);
JM 309                 Label lbl = new Label("originRepository", MessageFormat.format(getString("gb.mirrorOf"), "<b>" + model.origin + "</b>"));
310                 mirrorFrag.add(lbl.setEscapeModelStrings(false));
311                 add(mirrorFrag);
312             } else {
86bdc2 313                 if (model.isBare) {
JM 314                     add(new Fragment("repoIcon", "repoIconFragment", this));
315                 } else {
316                     add(new Fragment("repoIcon", "cloneIconFragment", this));
317                 }
318                 add(new Label("originRepository", Optional.of(model.description).or("")));
c44dd0 319             }
1e1b85 320         } else {
99d0d4 321             RepositoryModel origin = app().repositories().getRepositoryModel(model.originRepository);
eb1405 322             if (origin == null) {
86bdc2 323                 // no origin repository, show description if available
JM 324                 if (model.isBare) {
325                     add(new Fragment("repoIcon", "repoIconFragment", this));
326                 } else {
327                     add(new Fragment("repoIcon", "cloneIconFragment", this));
328                 }
329                 add(new Label("originRepository", Optional.of(model.description).or("")));
20714a 330             } else if (!user.canView(origin)) {
eb1405 331                 // show origin repository without link
86bdc2 332                 add(new Fragment("repoIcon", "forkIconFragment", this));
eb1405 333                 Fragment forkFrag = new Fragment("originRepository", "originFragment", this);
JM 334                 forkFrag.add(new Label("originRepository", StringUtils.stripDotGit(model.originRepository)));
335                 add(forkFrag);
336             } else {
337                 // link to origin repository
86bdc2 338                 add(new Fragment("repoIcon", "forkIconFragment", this));
eb1405 339                 Fragment forkFrag = new Fragment("originRepository", "originFragment", this);
5dd805 340                 forkFrag.add(new LinkPanel("originRepository", null, StringUtils.stripDotGit(model.originRepository),
eb1405 341                         SummaryPage.class, WicketUtils.newRepositoryParameter(model.originRepository)));
JM 342                 add(forkFrag);
343             }
1e1b85 344         }
5dd805 345
5e3521 346         // new ticket button
JM 347         if (user.isAuthenticated && app().tickets().isAcceptingNewTickets(getRepositoryModel())) {
348             String newTicketUrl = getRequestCycle().urlFor(NewTicketPage.class, WicketUtils.newRepositoryParameter(repositoryName)).toString();
349             addToolbarButton("newTicketLink", "fa fa-ticket", getString("gb.new"), newTicketUrl);
350         } else {
351             add(new Label("newTicketLink").setVisible(false));
352         }
353
9b26b7 354         // (un)star link allows a user to star a repository
9e7b14 355         if (user.isAuthenticated && model.hasCommits) {
79d324 356             PageParameters starParams = DeepCopier.copy(getPageParameters());
JM 357             starParams.put(PARAM_STAR, !user.getPreferences().isStarredRepository(model.name));
358             String toggleStarUrl = getRequestCycle().urlFor(getClass(), starParams).toString();
359             if (user.getPreferences().isStarredRepository(model.name)) {
360                 // show unstar button
361                 add(new Label("starLink").setVisible(false));
362                 addToolbarButton("unstarLink", "icon-star-empty", getString("gb.unstar"), toggleStarUrl);
363             } else {
364                 // show star button
365                 addToolbarButton("starLink", "icon-star", getString("gb.star"), toggleStarUrl);
366                 add(new Label("unstarLink").setVisible(false));
367             }
368         } else {
9b26b7 369             // anonymous user
79d324 370             add(new Label("starLink").setVisible(false));
JM 371             add(new Label("unstarLink").setVisible(false));
372         }
373
eb1405 374         // fork controls
253957 375         if (!allowForkControls() || !user.isAuthenticated) {
eb1405 376             // must be logged-in to fork, hide all fork controls
JM 377             add(new ExternalLink("forkLink", "").setVisible(false));
378             add(new ExternalLink("myForkLink", "").setVisible(false));
1e1b85 379         } else {
99d0d4 380             String fork = app().repositories().getFork(user.username, model.name);
c332d9 381             String userRepo = ModelUtils.getPersonalPath(user.username) + "/" + StringUtils.stripDotGit(StringUtils.getLastPathElement(model.name));
JM 382             boolean hasUserRepo = app().repositories().hasRepository(userRepo);
eb1405 383             boolean hasFork = fork != null;
c332d9 384             boolean canFork = user.canFork(model) && model.hasCommits && !hasUserRepo;
eb1405 385
JM 386             if (hasFork || !canFork) {
387                 // user not allowed to fork or fork already exists or repo forbids forking
388                 add(new ExternalLink("forkLink", "").setVisible(false));
5dd805 389
eb1405 390                 if (hasFork && !fork.equals(model.name)) {
JM 391                     // user has fork, view my fork link
392                     String url = getRequestCycle().urlFor(SummaryPage.class, WicketUtils.newRepositoryParameter(fork)).toString();
393                     add(new ExternalLink("myForkLink", url));
394                 } else {
395                     // no fork, hide view my fork link
396                     add(new ExternalLink("myForkLink", "").setVisible(false));
397                 }
398             } else if (canFork) {
399                 // can fork and we do not have one
400                 add(new ExternalLink("myForkLink", "").setVisible(false));
1f52c8 401                 String url = getRequestCycle().urlFor(ForkPage.class, WicketUtils.newRepositoryParameter(model.name)).toString();
JM 402                 add(new ExternalLink("forkLink", url));
1e1b85 403             }
JM 404         }
5dd805 405
067a2f 406         if (showAdmin || isOwner) {
JM 407             String url = getRequestCycle().urlFor(EditRepositoryPage.class, WicketUtils.newRepositoryParameter(model.name)).toString();
5dd805 408             add(new ExternalLink("editLink", url));
067a2f 409         } else {
JM 410             add(new Label("editLink").setVisible(false));
411         }
5dd805 412
1f5915 413         super.setupPage(repositoryName, pageName);
8c9a20 414     }
5dd805 415
79d324 416     protected void addToolbarButton(String wicketId, String iconClass, String label, String url) {
JM 417         Fragment button = new Fragment(wicketId, "toolbarLinkFragment", this);
418         Label icon = new Label("icon");
419         WicketUtils.setCssClass(icon, iconClass);
420         button.add(icon);
421         button.add(new Label("label", label));
422         button.add(new SimpleAttributeModifier("href", url));
423         add(button);
424     }
8c9a20 425
JM 426     protected void addSyndicationDiscoveryLink() {
427         add(WicketUtils.syndicationDiscoveryLink(SyndicationServlet.getTitle(repositoryName,
428                 objectId), SyndicationServlet.asLink(getRequest()
429                 .getRelativePathPrefixToContextRoot(), repositoryName, objectId, 0)));
5fe7df 430     }
JM 431
432     protected Repository getRepository() {
433         if (r == null) {
99d0d4 434             Repository r = app().repositories().getRepository(repositoryName);
7ba0ec 435             if (r == null) {
6caa93 436                 error(getString("gb.canNotLoadRepository") + " " + repositoryName, true);
7ba0ec 437                 return null;
JM 438             }
439             this.r = r;
5fe7df 440         }
JM 441         return r;
442     }
bc9d4a 443
f97bf0 444     protected RepositoryModel getRepositoryModel() {
JM 445         if (m == null) {
99d0d4 446             RepositoryModel model = app().repositories().getRepositoryModel(
2a7306 447                     GitBlitWebSession.get().getUser(), repositoryName);
dfb889 448             if (model == null) {
99d0d4 449                 if (app().repositories().hasRepository(repositoryName, true)) {
d97e52 450                     // has repository, but unauthorized
JM 451                     authenticationError(getString("gb.unauthorizedAccessForRepository") + " " + repositoryName);
452                 } else {
453                     // does not have repository
454                     error(getString("gb.canNotLoadRepository") + " " + repositoryName, true);
455                 }
00afd7 456                 return null;
dfb889 457             }
JM 458             m = model;
f97bf0 459         }
JM 460         return m;
44c005 461     }
JM 462
463     protected String getRepositoryName() {
464         return getRepositoryModel().name;
bc9d4a 465     }
dfb889 466
bc9d4a 467     protected RevCommit getCommit() {
JM 468         RevCommit commit = JGitUtils.getCommit(r, objectId);
469         if (commit == null) {
6caa93 470             error(MessageFormat.format(getString("gb.failedToFindCommit"),
1078f8 471                     objectId, repositoryName, getPageName()), null, LogPage.class,
JM 472                     WicketUtils.newRepositoryParameter(repositoryName));
bc9d4a 473         }
eb870f 474         getSubmodules(commit);
bc9d4a 475         return commit;
f97bf0 476     }
5dd805 477
ab1e11 478     protected String getBestCommitId(RevCommit commit) {
JM 479         String head = null;
480         try {
481             head = r.resolve(getRepositoryModel().HEAD).getName();
482         } catch (Exception e) {
483         }
484
485         String id = commit.getName();
486         if (!StringUtils.isEmpty(head) && head.equals(id)) {
487             // match default branch
488             return Repository.shortenRefName(getRepositoryModel().HEAD);
489         }
490
491         // find first branch match
492         for (RefModel ref : JGitUtils.getLocalBranches(r, false, -1)) {
493             if (ref.getObjectId().getName().equals(id)) {
062519 494                 return Repository.shortenRefName(ref.getName());
ab1e11 495             }
JM 496         }
497
498         // return sha
499         return id;
500     }
501
5dd805 502     protected Map<String, SubmoduleModel> getSubmodules(RevCommit commit) {
eb870f 503         if (submodules == null) {
JM 504             submodules = new HashMap<String, SubmoduleModel>();
505             for (SubmoduleModel model : JGitUtils.getSubmodules(r, commit.getTree())) {
506                 submodules.put(model.path, model);
507             }
508         }
509         return submodules;
510     }
5dd805 511
eb870f 512     protected SubmoduleModel getSubmodule(String path) {
80d636 513         SubmoduleModel model = null;
JM 514         if (submodules != null) {
515             model = submodules.get(path);
516         }
eb870f 517         if (model == null) {
JM 518             // undefined submodule?!
519             model = new SubmoduleModel(path.substring(path.lastIndexOf('/') + 1), path, path);
520             model.hasSubmodule = false;
521             model.gitblitPath = model.name;
522             return model;
523         } else {
524             // extract the repository name from the clone url
99d0d4 525             List<String> patterns = app().settings().getStrings(Keys.git.submoduleUrlPatterns);
eb870f 526             String submoduleName = StringUtils.extractRepositoryPath(model.url, patterns.toArray(new String[0]));
5dd805 527
eb870f 528             // determine the current path for constructing paths relative
JM 529             // to the current repository
530             String currentPath = "";
531             if (repositoryName.indexOf('/') > -1) {
532                 currentPath = repositoryName.substring(0, repositoryName.lastIndexOf('/') + 1);
533             }
534
535             // try to locate the submodule repository
536             // prefer bare to non-bare names
537             List<String> candidates = new ArrayList<String>();
538
539             // relative
540             candidates.add(currentPath + StringUtils.stripDotGit(submoduleName));
541             candidates.add(candidates.get(candidates.size() - 1) + ".git");
542
543             // relative, no subfolder
544             if (submoduleName.lastIndexOf('/') > -1) {
545                 String name = submoduleName.substring(submoduleName.lastIndexOf('/') + 1);
546                 candidates.add(currentPath + StringUtils.stripDotGit(name));
5e3771 547                 candidates.add(candidates.get(candidates.size() - 1) + ".git");
eb870f 548             }
JM 549
550             // absolute
551             candidates.add(StringUtils.stripDotGit(submoduleName));
552             candidates.add(candidates.get(candidates.size() - 1) + ".git");
553
554             // absolute, no subfolder
555             if (submoduleName.lastIndexOf('/') > -1) {
556                 String name = submoduleName.substring(submoduleName.lastIndexOf('/') + 1);
557                 candidates.add(StringUtils.stripDotGit(name));
558                 candidates.add(candidates.get(candidates.size() - 1) + ".git");
559             }
560
561             // create a unique, ordered set of candidate paths
562             Set<String> paths = new LinkedHashSet<String>(candidates);
563             for (String candidate : paths) {
99d0d4 564                 if (app().repositories().hasRepository(candidate)) {
eb870f 565                     model.hasSubmodule = true;
JM 566                     model.gitblitPath = candidate;
567                     return model;
568                 }
569             }
5dd805 570
eb870f 571             // we do not have a copy of the submodule, but we need a path
JM 572             model.gitblitPath = candidates.get(0);
573             return model;
5dd805 574         }
eb870f 575     }
5fe7df 576
008322 577     protected String getShortObjectId(String objectId) {
99d0d4 578         return objectId.substring(0, app().settings().getInteger(Keys.web.shortCommitIdLength, 6));
008322 579     }
JM 580
5fe7df 581     protected void addRefs(Repository r, RevCommit c) {
1e1b85 582         add(new RefsPanel("refsPanel", repositoryName, c, JGitUtils.getAllRefs(r, getRepositoryModel().showRemoteBranches)));
5fe7df 583     }
JM 584
cd9461 585     protected void addFullText(String wicketId, String text) {
BKI 586         RepositoryModel model = getRepositoryModel();
fef234 587         String content = bugtraqProcessor().processCommitMessage(r, model, text);
cd9461 588         String html;
BKI 589         switch (model.commitMessageRenderer) {
590         case MARKDOWN:
7fdc29 591             String safeContent = app().xssFilter().relaxed(content);
JM 592             html = MessageFormat.format("<div class='commit_message'>{0}</div>", safeContent);
cd9461 593             break;
BKI 594         default:
595             html = MessageFormat.format("<pre class='commit_message'>{0}</pre>", content);
596             break;
f339f5 597         }
JM 598         add(new Label(wicketId, html).setEscapeModelStrings(false));
5fe7df 599     }
1e47ab 600
cebf45 601     protected abstract String getPageName();
5fe7df 602
5d5e55 603     protected boolean isCommitPage() {
JM 604         return false;
605     }
606
2a7306 607     protected Component createPersonPanel(String wicketId, PersonIdent identity,
33d8d8 608             Constants.SearchType searchType) {
e11f48 609         String name = identity == null ? "" : identity.getName();
JM 610         String address = identity == null ? "" : identity.getEmailAddress();
ac7e9a 611         name = StringUtils.removeNewlines(name);
JM 612         address = StringUtils.removeNewlines(address);
99d0d4 613         boolean showEmail = app().settings().getBoolean(Keys.web.showEmailAddresses, false);
e11f48 614         if (!showEmail || StringUtils.isEmpty(name) || StringUtils.isEmpty(address)) {
JM 615             String value = name;
98ce17 616             if (StringUtils.isEmpty(value)) {
f5d0ad 617                 if (showEmail) {
e11f48 618                     value = address;
f5d0ad 619                 } else {
JM 620                     value = getString("gb.missingUsername");
621                 }
98ce17 622             }
JM 623             Fragment partial = new Fragment(wicketId, "partialPersonIdent", this);
4e1930 624             LinkPanel link = new LinkPanel("personName", "list", value, GitSearchPage.class,
2a7306 625                     WicketUtils.newSearchParameter(repositoryName, objectId, value, searchType));
98ce17 626             setPersonSearchTooltip(link, value, searchType);
JM 627             partial.add(link);
628             return partial;
629         } else {
630             Fragment fullPerson = new Fragment(wicketId, "fullPersonIdent", this);
4e1930 631             LinkPanel nameLink = new LinkPanel("personName", "list", name, GitSearchPage.class,
e11f48 632                     WicketUtils.newSearchParameter(repositoryName, objectId, name, searchType));
JM 633             setPersonSearchTooltip(nameLink, name, searchType);
98ce17 634             fullPerson.add(nameLink);
3e087a 635
227736 636             LinkPanel addressLink = new LinkPanel("personAddress", "hidden-phone list", "<" + address + ">",
4e1930 637                     GitSearchPage.class, WicketUtils.newSearchParameter(repositoryName, objectId,
e11f48 638                             address, searchType));
JM 639             setPersonSearchTooltip(addressLink, address, searchType);
98ce17 640             fullPerson.add(addressLink);
JM 641             return fullPerson;
642         }
643     }
3e087a 644
11924d 645     protected void setPersonSearchTooltip(Component component, String value,
JM 646             Constants.SearchType searchType) {
33d8d8 647         if (searchType.equals(Constants.SearchType.AUTHOR)) {
1e8390 648             WicketUtils.setHtmlTooltip(component, getString("gb.searchForAuthor") + " " + value);
33d8d8 649         } else if (searchType.equals(Constants.SearchType.COMMITTER)) {
1e8390 650             WicketUtils.setHtmlTooltip(component, getString("gb.searchForCommitter") + " " + value);
98ce17 651         }
JM 652     }
3e087a 653
9bc17d 654     protected void setChangeTypeTooltip(Component container, ChangeType type) {
JM 655         switch (type) {
656         case ADD:
1e8390 657             WicketUtils.setHtmlTooltip(container, getString("gb.addition"));
9bc17d 658             break;
JM 659         case COPY:
660         case RENAME:
1e8390 661             WicketUtils.setHtmlTooltip(container, getString("gb.rename"));
9bc17d 662             break;
JM 663         case DELETE:
1e8390 664             WicketUtils.setHtmlTooltip(container, getString("gb.deletion"));
9bc17d 665             break;
JM 666         case MODIFY:
1e8390 667             WicketUtils.setHtmlTooltip(container, getString("gb.modification"));
9bc17d 668             break;
JM 669         }
670     }
3e087a 671
932e93 672
PM 673     @Override
1e47ab 674     protected void onBeforeRender() {
JM 675         // dispose of repository object
676         if (r != null) {
677             r.close();
678             r = null;
679         }
10f798 680         
PM 681         // setup page header and footer
682         setupPage(getRepositoryName(), "/ " + getPageName());
932e93 683
1e47ab 684         super.onBeforeRender();
5fe7df 685     }
5dd805 686
a7db57 687     @Override
JM 688     protected void setLastModified() {
689         if (getClass().isAnnotationPresent(CacheControl.class)) {
690             CacheControl cacheControl = getClass().getAnnotation(CacheControl.class);
691             switch (cacheControl.value()) {
692             case REPOSITORY:
693                 RepositoryModel repository = getRepositoryModel();
694                 if (repository != null) {
695                     setLastModified(repository.lastChange);
696                 }
697                 break;
698             case COMMIT:
699                 RevCommit commit = getCommit();
700                 if (commit != null) {
701                     Date commitDate = JGitUtils.getCommitDate(commit);
702                     setLastModified(commitDate);
703                 }
704                 break;
705             default:
706                 super.setLastModified();
707             }
708         }
709     }
5fe7df 710
JM 711     protected PageParameters newRepositoryParameter() {
698678 712         return WicketUtils.newRepositoryParameter(repositoryName);
5fe7df 713     }
7ba0ec 714
5fe7df 715     protected PageParameters newCommitParameter() {
ef5c58 716         return WicketUtils.newObjectParameter(repositoryName, objectId);
5fe7df 717     }
7ba0ec 718
5fe7df 719     protected PageParameters newCommitParameter(String commitId) {
ef5c58 720         return WicketUtils.newObjectParameter(repositoryName, commitId);
5fe7df 721     }
JM 722
33622b 723     public boolean isShowAdmin() {
JM 724         return showAdmin;
725     }
5dd805 726
33622b 727     public boolean isOwner() {
JM 728         return isOwner;
a7317a 729     }
JM 730
ed295f 731     private class SearchForm extends SessionlessForm<Void> implements Serializable {
3e087a 732         private static final long serialVersionUID = 1L;
JM 733
734         private final String repositoryName;
735
736         private final IModel<String> searchBoxModel = new Model<String>("");
737
11924d 738         private final IModel<Constants.SearchType> searchTypeModel = new Model<Constants.SearchType>(
JM 739                 Constants.SearchType.COMMIT);
3e087a 740
JM 741         public SearchForm(String id, String repositoryName) {
ed295f 742             super(id, RepositoryPage.this.getClass(), RepositoryPage.this.getPageParameters());
3e087a 743             this.repositoryName = repositoryName;
11924d 744             DropDownChoice<Constants.SearchType> searchType = new DropDownChoice<Constants.SearchType>(
JM 745                     "searchType", Arrays.asList(Constants.SearchType.values()));
3e087a 746             searchType.setModel(searchTypeModel);
99d0d4 747             add(searchType.setVisible(app().settings().getBoolean(Keys.web.showSearchTypeSelection, false)));
3e087a 748             TextField<String> searchBox = new TextField<String>("searchBox", searchBoxModel);
JM 749             add(searchBox);
750         }
bc9d4a 751
JM 752         void setTranslatedAttributes() {
3e087a 753             WicketUtils.setHtmlTooltip(get("searchType"), getString("gb.searchTypeTooltip"));
9e5bee 754             WicketUtils.setHtmlTooltip(get("searchBox"),
JM 755                     MessageFormat.format(getString("gb.searchTooltip"), repositoryName));
bc9d4a 756             WicketUtils.setInputPlaceholder(get("searchBox"), getString("gb.search"));
3e087a 757         }
JM 758
759         @Override
760         public void onSubmit() {
33d8d8 761             Constants.SearchType searchType = searchTypeModel.getObject();
3e087a 762             String searchString = searchBoxModel.getObject();
6ef8d7 763             if (StringUtils.isEmpty(searchString)) {
5dd805 764                 // redirect to self to avoid wicket page update bug
307910 765                 String absoluteUrl = getCanonicalUrl();
6ef8d7 766                 getRequestCycle().setRequestTarget(new RedirectRequestTarget(absoluteUrl));
8c5d72 767                 return;
JM 768             }
33d8d8 769             for (Constants.SearchType type : Constants.SearchType.values()) {
3e087a 770                 if (searchString.toLowerCase().startsWith(type.name().toLowerCase() + ":")) {
JM 771                     searchType = type;
2a7306 772                     searchString = searchString.substring(type.name().toLowerCase().length() + 1)
JM 773                             .trim();
3e087a 774                     break;
JM 775                 }
776             }
4e1930 777             Class<? extends BasePage> searchPageClass = GitSearchPage.class;
99d0d4 778             RepositoryModel model = app().repositories().getRepositoryModel(repositoryName);
JM 779             if (app().settings().getBoolean(Keys.web.allowLuceneIndexing, true)
7db092 780                     && !ArrayUtils.isEmpty(model.indexedBranches)) {
4e1930 781                 // this repository is Lucene-indexed
JM 782                 searchPageClass = LuceneSearchPage.class;
783             }
77e1d2 784             // use an absolute url to workaround Wicket-Tomcat problems with
JM 785             // mounted url parameters (issue-111)
786             PageParameters params = WicketUtils.newSearchParameter(repositoryName, null, searchString, searchType);
307910 787             String absoluteUrl = getCanonicalUrl(searchPageClass, params);
77e1d2 788             getRequestCycle().setRequestTarget(new RedirectRequestTarget(absoluteUrl));
3e087a 789         }
JM 790     }
428b22 791 }