James Moger
2012-02-04 ccab3a2abf1f4c3b61e6955afa50b03efdcc707c
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;
9e5bee 22 import java.util.LinkedHashMap;
5fe7df 23 import java.util.List;
JM 24 import java.util.Map;
25
98ce17 26 import org.apache.wicket.Component;
5fe7df 27 import org.apache.wicket.PageParameters;
JM 28 import org.apache.wicket.markup.html.basic.Label;
3e087a 29 import org.apache.wicket.markup.html.form.DropDownChoice;
JM 30 import org.apache.wicket.markup.html.form.StatelessForm;
31 import org.apache.wicket.markup.html.form.TextField;
c22722 32 import org.apache.wicket.markup.html.link.ExternalLink;
98ce17 33 import org.apache.wicket.markup.html.panel.Fragment;
3e087a 34 import org.apache.wicket.model.IModel;
JM 35 import org.apache.wicket.model.Model;
9bc17d 36 import org.eclipse.jgit.diff.DiffEntry.ChangeType;
98ce17 37 import org.eclipse.jgit.lib.PersonIdent;
5fe7df 38 import org.eclipse.jgit.lib.Repository;
JM 39 import org.eclipse.jgit.revwalk.RevCommit;
40
33d8d8 41 import com.gitblit.Constants;
fc948c 42 import com.gitblit.GitBlit;
87cc1e 43 import com.gitblit.Keys;
11924d 44 import com.gitblit.PagesServlet;
c22722 45 import com.gitblit.SyndicationServlet;
1f9dae 46 import com.gitblit.models.RepositoryModel;
5fe7df 47 import com.gitblit.utils.JGitUtils;
f5d0ad 48 import com.gitblit.utils.StringUtils;
793f76 49 import com.gitblit.utils.TicgitUtils;
1f9dae 50 import com.gitblit.wicket.GitBlitWebSession;
9e5bee 51 import com.gitblit.wicket.PageRegistration;
11924d 52 import com.gitblit.wicket.PageRegistration.OtherPageLink;
1f9dae 53 import com.gitblit.wicket.WicketUtils;
JM 54 import com.gitblit.wicket.panels.LinkPanel;
9e5bee 55 import com.gitblit.wicket.panels.NavigationPanel;
5fe7df 56 import com.gitblit.wicket.panels.RefsPanel;
JM 57
58 public abstract class RepositoryPage extends BasePage {
59
60     protected final String repositoryName;
bc9d4a 61     protected final String objectId;
7ba0ec 62
2a7306 63     private transient Repository r;
bc9d4a 64
2a7306 65     private RepositoryModel m;
5fe7df 66
9e5bee 67     private final Map<String, PageRegistration> registeredPages;
3e087a 68
cebf45 69     public RepositoryPage(PageParameters params) {
5fe7df 70         super(params);
7d35e2 71         repositoryName = WicketUtils.getRepositoryName(params);
JM 72         objectId = WicketUtils.getObject(params);
5fe7df 73
bc9d4a 74         if (StringUtils.isEmpty(repositoryName)) {
JM 75             error(MessageFormat.format("Repository not specified for {0}!", getPageName()), true);
76         }
77
82df52 78         if (!getRepositoryModel().hasCommits) {
JM 79             setResponsePage(EmptyRepositoryPage.class, params);
80         }
81
9e5bee 82         // register the available page links for this page and user
JM 83         registeredPages = registerPages();
2a7306 84
3e087a 85         // standard page links
9e5bee 86         List<PageRegistration> pages = new ArrayList<PageRegistration>(registeredPages.values());
JM 87         NavigationPanel navigationPanel = new NavigationPanel("navPanel", getClass(), pages);
88         add(navigationPanel);
8c9a20 89
c22722 90         add(new ExternalLink("syndication", SyndicationServlet.asLink(getRequest()
JM 91                 .getRelativePathPrefixToContextRoot(), repositoryName, null, 0)));
3e087a 92
JM 93         // add floating search form
94         SearchForm searchForm = new SearchForm("searchForm", repositoryName);
95         add(searchForm);
96         searchForm.setTranslatedAttributes();
bc9d4a 97
3e087a 98         // set stateless page preference
5fe7df 99         setStatelessHint(true);
3e087a 100     }
9e5bee 101
JM 102     private Map<String, PageRegistration> registerPages() {
103         PageParameters params = null;
104         if (!StringUtils.isEmpty(repositoryName)) {
105             params = WicketUtils.newRepositoryParameter(repositoryName);
106         }
107         Map<String, PageRegistration> pages = new LinkedHashMap<String, PageRegistration>();
108
109         // standard links
110         pages.put("repositories", new PageRegistration("gb.repositories", RepositoriesPage.class));
111         pages.put("summary", new PageRegistration("gb.summary", SummaryPage.class, params));
112         pages.put("log", new PageRegistration("gb.log", LogPage.class, params));
113         pages.put("branches", new PageRegistration("gb.branches", BranchesPage.class, params));
114         pages.put("tags", new PageRegistration("gb.tags", TagsPage.class, params));
115         pages.put("tree", new PageRegistration("gb.tree", TreePage.class, params));
116
117         // conditional links
118         Repository r = getRepository();
119         RepositoryModel model = getRepositoryModel();
120
121         // per-repository extra page links
122         if (model.useTickets && TicgitUtils.getTicketsBranch(r) != null) {
123             pages.put("tickets", new PageRegistration("gb.tickets", TicketsPage.class, params));
124         }
125         if (model.useDocs) {
126             pages.put("docs", new PageRegistration("gb.docs", DocsPage.class, params));
127         }
11924d 128         if (JGitUtils.getPagesBranch(r) != null) {
JM 129             OtherPageLink pagesLink = new OtherPageLink("gb.pages", PagesServlet.asLink(
130                     getRequest().getRelativePathPrefixToContextRoot(), repositoryName, null));
131             pages.put("pages", pagesLink);
132         }
133
9e5bee 134         // Conditionally add edit link
JM 135         final boolean showAdmin;
136         if (GitBlit.getBoolean(Keys.web.authenticateAdminPages, true)) {
137             boolean allowAdmin = GitBlit.getBoolean(Keys.web.allowAdministration, false);
138             showAdmin = allowAdmin && GitBlitWebSession.get().canAdmin();
139         } else {
140             showAdmin = GitBlit.getBoolean(Keys.web.allowAdministration, false);
141         }
142         if (showAdmin
143                 || GitBlitWebSession.get().isLoggedIn()
144                 && (model.owner != null && model.owner.equalsIgnoreCase(GitBlitWebSession.get()
145                         .getUser().username))) {
146             pages.put("edit", new PageRegistration("gb.edit", EditRepositoryPage.class, params));
147         }
148         return pages;
149     }
150
1f5915 151     @Override
11924d 152     protected void setupPage(String repositoryName, String pageName) {
JM 153         add(new LinkPanel("repositoryName", null, StringUtils.stripDotGit(repositoryName),
154                 SummaryPage.class, WicketUtils.newRepositoryParameter(repositoryName)));
1f5915 155         add(new Label("pageName", pageName));
ccab3a 156         if (getRepositoryModel().isBare) {
JM 157             add(new Label("workingCopy").setVisible(false));
158         } else {
159             Label lbl = new Label("workingCopy", getString("gb.workingCopy"));
160             WicketUtils.setHtmlTooltip(lbl,  getString("gb.workingCopyWarning"));
161             add(lbl);
162         }
1f5915 163
JM 164         super.setupPage(repositoryName, pageName);
8c9a20 165     }
JM 166
167     protected void addSyndicationDiscoveryLink() {
168         add(WicketUtils.syndicationDiscoveryLink(SyndicationServlet.getTitle(repositoryName,
169                 objectId), SyndicationServlet.asLink(getRequest()
170                 .getRelativePathPrefixToContextRoot(), repositoryName, objectId, 0)));
5fe7df 171     }
JM 172
173     protected Repository getRepository() {
174         if (r == null) {
f5d0ad 175             Repository r = GitBlit.self().getRepository(repositoryName);
7ba0ec 176             if (r == null) {
00afd7 177                 error("Can not load repository " + repositoryName, true);
7ba0ec 178                 return null;
JM 179             }
180             this.r = r;
5fe7df 181         }
JM 182         return r;
183     }
bc9d4a 184
f97bf0 185     protected RepositoryModel getRepositoryModel() {
JM 186         if (m == null) {
2a7306 187             RepositoryModel model = GitBlit.self().getRepositoryModel(
JM 188                     GitBlitWebSession.get().getUser(), repositoryName);
dfb889 189             if (model == null) {
85c2e6 190                 authenticationError("Unauthorized access for repository " + repositoryName);
00afd7 191                 return null;
dfb889 192             }
JM 193             m = model;
f97bf0 194         }
JM 195         return m;
bc9d4a 196     }
dfb889 197
bc9d4a 198     protected RevCommit getCommit() {
JM 199         RevCommit commit = JGitUtils.getCommit(r, objectId);
200         if (commit == null) {
2a7306 201             error(MessageFormat.format("Failed to find commit \"{0}\" in {1} for {2} page!",
JM 202                     objectId, repositoryName, getPageName()), true);
bc9d4a 203         }
JM 204         return commit;
f97bf0 205     }
5fe7df 206
008322 207     protected String getShortObjectId(String objectId) {
JM 208         return objectId.substring(0, 8);
209     }
210
5fe7df 211     protected void addRefs(Repository r, RevCommit c) {
155bf7 212         add(new RefsPanel("refsPanel", repositoryName, c, JGitUtils.getAllRefs(r)));
5fe7df 213     }
JM 214
215     protected void addFullText(String wicketId, String text, boolean substituteRegex) {
f339f5 216         String html;
5fe7df 217         if (substituteRegex) {
8c9a20 218             html = GitBlit.self().processCommitMessage(repositoryName, text);
f339f5 219         } else {
JM 220             html = StringUtils.breakLinesForHtml(text);
221         }
222         add(new Label(wicketId, html).setEscapeModelStrings(false));
5fe7df 223     }
1e47ab 224
cebf45 225     protected abstract String getPageName();
5fe7df 226
2a7306 227     protected Component createPersonPanel(String wicketId, PersonIdent identity,
33d8d8 228             Constants.SearchType searchType) {
e11f48 229         String name = identity == null ? "" : identity.getName();
JM 230         String address = identity == null ? "" : identity.getEmailAddress();
2a7306 231         boolean showEmail = GitBlit.getBoolean(Keys.web.showEmailAddresses, false);
e11f48 232         if (!showEmail || StringUtils.isEmpty(name) || StringUtils.isEmpty(address)) {
JM 233             String value = name;
98ce17 234             if (StringUtils.isEmpty(value)) {
f5d0ad 235                 if (showEmail) {
e11f48 236                     value = address;
f5d0ad 237                 } else {
JM 238                     value = getString("gb.missingUsername");
239                 }
98ce17 240             }
JM 241             Fragment partial = new Fragment(wicketId, "partialPersonIdent", this);
2a7306 242             LinkPanel link = new LinkPanel("personName", "list", value, SearchPage.class,
JM 243                     WicketUtils.newSearchParameter(repositoryName, objectId, value, searchType));
98ce17 244             setPersonSearchTooltip(link, value, searchType);
JM 245             partial.add(link);
246             return partial;
247         } else {
248             Fragment fullPerson = new Fragment(wicketId, "fullPersonIdent", this);
e11f48 249             LinkPanel nameLink = new LinkPanel("personName", "list", name, SearchPage.class,
JM 250                     WicketUtils.newSearchParameter(repositoryName, objectId, name, searchType));
251             setPersonSearchTooltip(nameLink, name, searchType);
98ce17 252             fullPerson.add(nameLink);
3e087a 253
e11f48 254             LinkPanel addressLink = new LinkPanel("personAddress", "list", "<" + address + ">",
JM 255                     SearchPage.class, WicketUtils.newSearchParameter(repositoryName, objectId,
256                             address, searchType));
257             setPersonSearchTooltip(addressLink, address, searchType);
98ce17 258             fullPerson.add(addressLink);
JM 259             return fullPerson;
260         }
261     }
3e087a 262
11924d 263     protected void setPersonSearchTooltip(Component component, String value,
JM 264             Constants.SearchType searchType) {
33d8d8 265         if (searchType.equals(Constants.SearchType.AUTHOR)) {
1e8390 266             WicketUtils.setHtmlTooltip(component, getString("gb.searchForAuthor") + " " + value);
33d8d8 267         } else if (searchType.equals(Constants.SearchType.COMMITTER)) {
1e8390 268             WicketUtils.setHtmlTooltip(component, getString("gb.searchForCommitter") + " " + value);
98ce17 269         }
JM 270     }
3e087a 271
9bc17d 272     protected void setChangeTypeTooltip(Component container, ChangeType type) {
JM 273         switch (type) {
274         case ADD:
1e8390 275             WicketUtils.setHtmlTooltip(container, getString("gb.addition"));
9bc17d 276             break;
JM 277         case COPY:
278         case RENAME:
1e8390 279             WicketUtils.setHtmlTooltip(container, getString("gb.rename"));
9bc17d 280             break;
JM 281         case DELETE:
1e8390 282             WicketUtils.setHtmlTooltip(container, getString("gb.deletion"));
9bc17d 283             break;
JM 284         case MODIFY:
1e8390 285             WicketUtils.setHtmlTooltip(container, getString("gb.modification"));
9bc17d 286             break;
JM 287         }
288     }
3e087a 289
1e47ab 290     @Override
JM 291     protected void onBeforeRender() {
292         // dispose of repository object
293         if (r != null) {
294             r.close();
295             r = null;
296         }
297         // setup page header and footer
cebf45 298         setupPage(repositoryName, "/ " + getPageName());
1e47ab 299         super.onBeforeRender();
5fe7df 300     }
JM 301
302     protected PageParameters newRepositoryParameter() {
698678 303         return WicketUtils.newRepositoryParameter(repositoryName);
5fe7df 304     }
7ba0ec 305
5fe7df 306     protected PageParameters newCommitParameter() {
ef5c58 307         return WicketUtils.newObjectParameter(repositoryName, objectId);
5fe7df 308     }
7ba0ec 309
5fe7df 310     protected PageParameters newCommitParameter(String commitId) {
ef5c58 311         return WicketUtils.newObjectParameter(repositoryName, commitId);
5fe7df 312     }
JM 313
85c2e6 314     private static class SearchForm extends StatelessForm<Void> implements Serializable {
3e087a 315         private static final long serialVersionUID = 1L;
JM 316
317         private final String repositoryName;
318
319         private final IModel<String> searchBoxModel = new Model<String>("");
320
11924d 321         private final IModel<Constants.SearchType> searchTypeModel = new Model<Constants.SearchType>(
JM 322                 Constants.SearchType.COMMIT);
3e087a 323
JM 324         public SearchForm(String id, String repositoryName) {
325             super(id);
326             this.repositoryName = repositoryName;
11924d 327             DropDownChoice<Constants.SearchType> searchType = new DropDownChoice<Constants.SearchType>(
JM 328                     "searchType", Arrays.asList(Constants.SearchType.values()));
3e087a 329             searchType.setModel(searchTypeModel);
2a7306 330             add(searchType.setVisible(GitBlit.getBoolean(Keys.web.showSearchTypeSelection, false)));
3e087a 331             TextField<String> searchBox = new TextField<String>("searchBox", searchBoxModel);
JM 332             add(searchBox);
333         }
bc9d4a 334
JM 335         void setTranslatedAttributes() {
3e087a 336             WicketUtils.setHtmlTooltip(get("searchType"), getString("gb.searchTypeTooltip"));
9e5bee 337             WicketUtils.setHtmlTooltip(get("searchBox"),
JM 338                     MessageFormat.format(getString("gb.searchTooltip"), repositoryName));
bc9d4a 339             WicketUtils.setInputPlaceholder(get("searchBox"), getString("gb.search"));
3e087a 340         }
JM 341
342         @Override
343         public void onSubmit() {
33d8d8 344             Constants.SearchType searchType = searchTypeModel.getObject();
3e087a 345             String searchString = searchBoxModel.getObject();
8c5d72 346             if (searchString == null) {
JM 347                 // FIXME IE intermittently has no searchString. Wicket bug?
348                 return;
349             }
33d8d8 350             for (Constants.SearchType type : Constants.SearchType.values()) {
3e087a 351                 if (searchString.toLowerCase().startsWith(type.name().toLowerCase() + ":")) {
JM 352                     searchType = type;
2a7306 353                     searchString = searchString.substring(type.name().toLowerCase().length() + 1)
JM 354                             .trim();
3e087a 355                     break;
JM 356                 }
357             }
2a7306 358             setResponsePage(SearchPage.class,
JM 359                     WicketUtils.newSearchParameter(repositoryName, null, searchString, searchType));
3e087a 360         }
JM 361     }
5fe7df 362 }