James Moger
2012-09-10 fabe060d3a435f116128851f828e35c2af5fde67
commit | author | age
a7571b 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  */
16 package com.gitblit.wicket.pages;
17
18 import java.text.MessageFormat;
9e5bee 19 import java.util.ArrayList;
bc57cd 20 import java.util.Arrays;
JM 21 import java.util.Calendar;
a22eb4 22 import java.util.Collections;
bc57cd 23 import java.util.Date;
a22eb4 24 import java.util.HashMap;
bc57cd 25 import java.util.HashSet;
31bcbe 26 import java.util.LinkedHashSet;
9e5bee 27 import java.util.List;
a22eb4 28 import java.util.Map;
31bcbe 29 import java.util.Set;
a22eb4 30 import java.util.concurrent.atomic.AtomicInteger;
cb57ec 31 import java.util.regex.Pattern;
a7571b 32
d376ab 33 import org.apache.wicket.PageParameters;
a7571b 34 import org.apache.wicket.markup.html.form.PasswordTextField;
JM 35 import org.apache.wicket.markup.html.form.TextField;
36 import org.apache.wicket.model.IModel;
37 import org.apache.wicket.model.Model;
38 import org.apache.wicket.protocol.http.WebResponse;
39
40 import com.gitblit.Constants;
41 import com.gitblit.GitBlit;
42 import com.gitblit.Keys;
cb57ec 43 import com.gitblit.models.RepositoryModel;
JM 44 import com.gitblit.models.TeamModel;
a7571b 45 import com.gitblit.models.UserModel;
JM 46 import com.gitblit.utils.StringUtils;
47 import com.gitblit.wicket.GitBlitWebSession;
9e5bee 48 import com.gitblit.wicket.PageRegistration;
31bcbe 49 import com.gitblit.wicket.PageRegistration.DropDownMenuItem;
ed295f 50 import com.gitblit.wicket.SessionlessForm;
8c5d72 51 import com.gitblit.wicket.WicketUtils;
9e5bee 52 import com.gitblit.wicket.panels.NavigationPanel;
a7571b 53
d376ab 54 /**
JM 55  * Root page is a topbar, navigable page like Repositories, Users, or
56  * Federation.
57  * 
58  * @author James Moger
59  * 
60  */
a7571b 61 public abstract class RootPage extends BasePage {
JM 62
d376ab 63     boolean showAdmin;
a7571b 64
JM 65     IModel<String> username = new Model<String>("");
66     IModel<String> password = new Model<String>("");
2a8178 67     List<RepositoryModel> repositoryModels = new ArrayList<RepositoryModel>();
a7571b 68
JM 69     public RootPage() {
70         super();
d376ab 71     }
a7571b 72
d376ab 73     public RootPage(PageParameters params) {
JM 74         super(params);
75     }
a7571b 76
d376ab 77     @Override
JM 78     protected void setupPage(String repositoryName, String pageName) {
31bcbe 79         boolean authenticateView = GitBlit.getBoolean(Keys.web.authenticateViewPages, false);
JM 80         boolean authenticateAdmin = GitBlit.getBoolean(Keys.web.authenticateAdminPages, true);
81         boolean allowAdmin = GitBlit.getBoolean(Keys.web.allowAdministration, true);
a22eb4 82
JM 83         if (authenticateAdmin) {
a7571b 84             showAdmin = allowAdmin && GitBlitWebSession.get().canAdmin();
JM 85             // authentication requires state and session
86             setStatelessHint(false);
87         } else {
31bcbe 88             showAdmin = allowAdmin;
JM 89             if (authenticateView) {
a7571b 90                 // authentication requires state and session
JM 91                 setStatelessHint(false);
92             } else {
93                 // no authentication required, no state and no session required
94                 setStatelessHint(true);
95             }
96         }
97         boolean showRegistrations = GitBlit.canFederate()
98                 && GitBlit.getBoolean(Keys.web.showFederationRegistrations, false);
99
100         // navigation links
9e5bee 101         List<PageRegistration> pages = new ArrayList<PageRegistration>();
bc57cd 102         pages.add(new PageRegistration("gb.repositories", RepositoriesPage.class,
JM 103                 getRootPageParameters()));
7f14da 104         pages.add(new PageRegistration("gb.activity", ActivityPage.class, getRootPageParameters()));
7db092 105         if (GitBlit.getBoolean(Keys.web.allowLuceneIndexing, true)) {
JM 106             pages.add(new PageRegistration("gb.search", LuceneSearchPage.class));
107         }
9e5bee 108         if (showAdmin) {
JM 109             pages.add(new PageRegistration("gb.users", UsersPage.class));
110         }
111         if (showAdmin || showRegistrations) {
112             pages.add(new PageRegistration("gb.federation", FederationPage.class));
cb57ec 113         }
31bcbe 114
JM 115         if (!authenticateView || (authenticateView && GitBlitWebSession.get().isLoggedIn())) {
116             addDropDownMenus(pages);
117         }
118
9e5bee 119         NavigationPanel navPanel = new NavigationPanel("navPanel", getClass(), pages);
JM 120         add(navPanel);
a7571b 121
JM 122         // login form
ed295f 123         SessionlessForm<Void> loginForm = new SessionlessForm<Void>("loginForm", getClass(), getPageParameters()) {
a7571b 124
JM 125             private static final long serialVersionUID = 1L;
126
127             @Override
128             public void onSubmit() {
129                 String username = RootPage.this.username.getObject();
130                 char[] password = RootPage.this.password.getObject().toCharArray();
131
132                 UserModel user = GitBlit.self().authenticate(username, password);
133                 if (user == null) {
6caa93 134                     error(getString("gb.invalidUsernameOrPassword"));
a7571b 135                 } else if (user.username.equals(Constants.FEDERATION_USER)) {
JM 136                     // disallow the federation user from logging in via the
137                     // web ui
6caa93 138                     error(getString("gb.invalidUsernameOrPassword"));
a7571b 139                     user = null;
JM 140                 } else {
141                     loginUser(user);
142                 }
143             }
144         };
8c5d72 145         TextField<String> unameField = new TextField<String>("username", username);
JM 146         WicketUtils.setInputPlaceholder(unameField, getString("gb.username"));
147         loginForm.add(unameField);
148         PasswordTextField pwField = new PasswordTextField("password", password);
149         WicketUtils.setInputPlaceholder(pwField, getString("gb.password"));
150         loginForm.add(pwField);
a7571b 151         add(loginForm);
a22eb4 152
31bcbe 153         if (authenticateView || authenticateAdmin) {
a7571b 154             loginForm.setVisible(!GitBlitWebSession.get().isLoggedIn());
JM 155         } else {
156             loginForm.setVisible(false);
157         }
158
159         // display an error message cached from a redirect
160         String cachedMessage = GitBlitWebSession.get().clearErrorMessage();
161         if (!StringUtils.isEmpty(cachedMessage)) {
162             error(cachedMessage);
163         } else if (showAdmin) {
164             int pendingProposals = GitBlit.self().getPendingFederationProposals().size();
165             if (pendingProposals == 1) {
6caa93 166                 info(getString("gb.OneProposalToReview"));
a7571b 167             } else if (pendingProposals > 1) {
6caa93 168                 info(MessageFormat.format(getString("gb.nFederationProposalsToReview"),
a7571b 169                         pendingProposals));
JM 170             }
171         }
172
d376ab 173         super.setupPage(repositoryName, pageName);
a7571b 174     }
bc57cd 175
7f14da 176     private PageParameters getRootPageParameters() {
bc57cd 177         if (reusePageParameters()) {
8f86e2 178             PageParameters pp = getPageParameters();
JM 179             if (pp != null) {
180                 PageParameters params = new PageParameters(pp);
13a3f5 181                 // remove named project parameter
JM 182                 params.remove("p");
183
bc57cd 184                 // remove named repository parameter
JM 185                 params.remove("r");
8f86e2 186
JM 187                 // remove days back parameter if it is the default value
188                 if (params.containsKey("db")
189                         && params.getInt("db") == GitBlit.getInteger(Keys.web.activityDuration, 14)) {
190                     params.remove("db");
191                 }
192                 return params;
193             }            
7f14da 194         }
bc57cd 195         return null;
JM 196     }
197
198     protected boolean reusePageParameters() {
199         return false;
7f14da 200     }
a7571b 201
JM 202     private void loginUser(UserModel user) {
203         if (user != null) {
204             // Set the user into the session
e78838 205             GitBlitWebSession session = GitBlitWebSession.get();
JM 206             // issue 62: fix session fixation vulnerability
207             session.replaceSession();
208             session.setUser(user);
a7571b 209
JM 210             // Set Cookie
211             if (GitBlit.getBoolean(Keys.web.allowCookieAuthentication, false)) {
212                 WebResponse response = (WebResponse) getRequestCycle().getResponse();
213                 GitBlit.self().setCookie(response, user);
214             }
215
d97e52 216             if (!session.continueRequest()) {
ed295f 217                 PageParameters params = getPageParameters();
JM 218                 if (params == null) {
219                     // redirect to this page
220                     setResponsePage(getClass());
221                 } else {
222                     // Strip username and password and redirect to this page
223                     params.remove("username");
224                     params.remove("password");
225                     setResponsePage(getClass(), params);
226                 }
a7571b 227             }
JM 228         }
31bcbe 229     }
2a8178 230     
JM 231     protected List<RepositoryModel> getRepositoryModels() {
232         if (repositoryModels.isEmpty()) {
233             final UserModel user = GitBlitWebSession.get().getUser();
234             List<RepositoryModel> repositories = GitBlit.self().getRepositoryModels(user);
235             repositoryModels.addAll(repositories);
13a3f5 236             Collections.sort(repositoryModels);
2a8178 237         }
JM 238         return repositoryModels;
239     }
31bcbe 240
JM 241     protected void addDropDownMenus(List<PageRegistration> pages) {
242
243     }
244
bc57cd 245     protected List<DropDownMenuItem> getRepositoryFilterItems(PageParameters params) {
31bcbe 246         final UserModel user = GitBlitWebSession.get().getUser();
JM 247         Set<DropDownMenuItem> filters = new LinkedHashSet<DropDownMenuItem>();
2a8178 248         List<RepositoryModel> repositories = getRepositoryModels();
31bcbe 249
JM 250         // accessible repositories by federation set
a22eb4 251         Map<String, AtomicInteger> setMap = new HashMap<String, AtomicInteger>();
JM 252         for (RepositoryModel repository : repositories) {
31bcbe 253             for (String set : repository.federationSets) {
a22eb4 254                 String key = set.toLowerCase();
JM 255                 if (setMap.containsKey(key)) {
256                     setMap.get(key).incrementAndGet();
257                 } else {
258                     setMap.put(key, new AtomicInteger(1));
259                 }
31bcbe 260             }
JM 261         }
a22eb4 262         if (setMap.size() > 0) {
JM 263             List<String> sets = new ArrayList<String>(setMap.keySet());
264             Collections.sort(sets);
265             for (String set : sets) {
266                 filters.add(new DropDownMenuItem(MessageFormat.format("{0} ({1})", set,
bc57cd 267                         setMap.get(set).get()), "set", set, params));
a22eb4 268             }
31bcbe 269             // divider
JM 270             filters.add(new DropDownMenuItem());
271         }
272
273         // user's team memberships
274         if (user != null && user.teams.size() > 0) {
a22eb4 275             List<TeamModel> teams = new ArrayList<TeamModel>(user.teams);
JM 276             Collections.sort(teams);
277             for (TeamModel team : teams) {
278                 filters.add(new DropDownMenuItem(MessageFormat.format("{0} ({1})", team.name,
bc57cd 279                         team.repositories.size()), "team", team.name, params));
31bcbe 280             }
JM 281             // divider
282             filters.add(new DropDownMenuItem());
283         }
284
285         // custom filters
286         String customFilters = GitBlit.getString(Keys.web.customFilters, null);
287         if (!StringUtils.isEmpty(customFilters)) {
e76fee 288             boolean addedExpression = false;
31bcbe 289             List<String> expressions = StringUtils.getStringsFromValue(customFilters, "!!!");
JM 290             for (String expression : expressions) {
e76fee 291                 if (!StringUtils.isEmpty(expression)) {
JM 292                     addedExpression = true;
bc57cd 293                     filters.add(new DropDownMenuItem(null, "x", expression, params));
e76fee 294                 }
JM 295             }
296             // if we added any custom expressions, add a divider
297             if (addedExpression) {
298                 filters.add(new DropDownMenuItem());
31bcbe 299             }
8f86e2 300         }
31bcbe 301         return new ArrayList<DropDownMenuItem>(filters);
bc57cd 302     }
JM 303
304     protected List<DropDownMenuItem> getTimeFilterItems(PageParameters params) {
305         // days back choices - additive parameters
306         int daysBack = GitBlit.getInteger(Keys.web.activityDuration, 14);
307         if (daysBack < 1) {
308             daysBack = 14;
309         }
310         List<DropDownMenuItem> items = new ArrayList<DropDownMenuItem>();
311         Set<Integer> choicesSet = new HashSet<Integer>(Arrays.asList(daysBack, 14, 28, 60, 90, 180));
312         List<Integer> choices = new ArrayList<Integer>(choicesSet);
313         Collections.sort(choices);
017749 314         String lastDaysPattern = getString("gb.lastNDays");
bc57cd 315         for (Integer db : choices) {
017749 316             String txt = MessageFormat.format(lastDaysPattern, db);
bc57cd 317             items.add(new DropDownMenuItem(txt, "db", db.toString(), params));
JM 318         }
319         items.add(new DropDownMenuItem());
320         return items;
a7571b 321     }
cb57ec 322
JM 323     protected List<RepositoryModel> getRepositories(PageParameters params) {
324         if (params == null) {
2a8178 325             return getRepositoryModels();
cb57ec 326         }
JM 327
bc57cd 328         boolean hasParameter = false;
13a3f5 329         String projectName = WicketUtils.getProjectName(params);
cb57ec 330         String repositoryName = WicketUtils.getRepositoryName(params);
JM 331         String set = WicketUtils.getSet(params);
332         String regex = WicketUtils.getRegEx(params);
333         String team = WicketUtils.getTeam(params);
bc57cd 334         int daysBack = params.getInt("db", 0);
cb57ec 335
2a8178 336         List<RepositoryModel> availableModels = getRepositoryModels();
bc57cd 337         Set<RepositoryModel> models = new HashSet<RepositoryModel>();
cb57ec 338
JM 339         if (!StringUtils.isEmpty(repositoryName)) {
340             // try named repository
bc57cd 341             hasParameter = true;
JM 342             for (RepositoryModel model : availableModels) {
343                 if (model.name.equalsIgnoreCase(repositoryName)) {
344                     models.add(model);
345                     break;
13a3f5 346                 }
JM 347             }
348         }
349
350         if (!StringUtils.isEmpty(projectName)) {
351             // try named project
352             hasParameter = true;            
353             if (projectName.equalsIgnoreCase(GitBlit.getString(Keys.web.repositoryRootGroupName, "main"))) {
354                 // root project/group
355                 for (RepositoryModel model : availableModels) {
356                     if (model.name.indexOf('/') == -1) {
357                         models.add(model);
358                     }
359                 }
360             } else {
361                 // named project/group
362                 String group = projectName.toLowerCase() + "/";
363                 for (RepositoryModel model : availableModels) {
364                     if (model.name.toLowerCase().startsWith(group)) {
365                         models.add(model);
366                     }
bc57cd 367                 }
cb57ec 368             }
JM 369         }
370
371         if (!StringUtils.isEmpty(regex)) {
372             // filter the repositories by the regex
bc57cd 373             hasParameter = true;
cb57ec 374             Pattern pattern = Pattern.compile(regex);
bc57cd 375             for (RepositoryModel model : availableModels) {
JM 376                 if (pattern.matcher(model.name).find()) {
377                     models.add(model);
cb57ec 378                 }
JM 379             }
bc57cd 380         }
JM 381
382         if (!StringUtils.isEmpty(set)) {
cb57ec 383             // filter the repositories by the specified sets
bc57cd 384             hasParameter = true;
cb57ec 385             List<String> sets = StringUtils.getStringsFromValue(set, ",");
bc57cd 386             for (RepositoryModel model : availableModels) {
cb57ec 387                 for (String curr : sets) {
JM 388                     if (model.federationSets.contains(curr)) {
bc57cd 389                         models.add(model);
cb57ec 390                     }
JM 391                 }
392             }
bc57cd 393         }
JM 394
395         if (!StringUtils.isEmpty(team)) {
cb57ec 396             // filter the repositories by the specified teams
bc57cd 397             hasParameter = true;
cb57ec 398             List<String> teams = StringUtils.getStringsFromValue(team, ",");
31bcbe 399
cb57ec 400             // need TeamModels first
JM 401             List<TeamModel> teamModels = new ArrayList<TeamModel>();
402             for (String name : teams) {
bc57cd 403                 TeamModel teamModel = GitBlit.self().getTeamModel(name);
JM 404                 if (teamModel != null) {
405                     teamModels.add(teamModel);
cb57ec 406                 }
JM 407             }
31bcbe 408
cb57ec 409             // brute-force our way through finding the matching models
bc57cd 410             for (RepositoryModel repositoryModel : availableModels) {
cb57ec 411                 for (TeamModel teamModel : teamModels) {
JM 412                     if (teamModel.hasRepository(repositoryModel.name)) {
bc57cd 413                         models.add(repositoryModel);
cb57ec 414                     }
JM 415                 }
416             }
417         }
bc57cd 418
JM 419         if (!hasParameter) {
420             models.addAll(availableModels);
421         }
8f86e2 422
bc57cd 423         // time-filter the list
JM 424         if (daysBack > 0) {
425             Calendar cal = Calendar.getInstance();
426             cal.set(Calendar.HOUR_OF_DAY, 0);
427             cal.set(Calendar.MINUTE, 0);
428             cal.set(Calendar.SECOND, 0);
429             cal.set(Calendar.MILLISECOND, 0);
430             cal.add(Calendar.DATE, -1 * daysBack);
431             Date threshold = cal.getTime();
432             Set<RepositoryModel> timeFiltered = new HashSet<RepositoryModel>();
433             for (RepositoryModel model : models) {
434                 if (model.lastChange.after(threshold)) {
435                     timeFiltered.add(model);
436                 }
437             }
438             models = timeFiltered;
439         }
13a3f5 440         
JM 441         List<RepositoryModel> list = new ArrayList<RepositoryModel>(models);
442         Collections.sort(list);
443         return list;
cb57ec 444     }
a7571b 445 }