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