James Moger
2014-09-30 02ea71c71dbf162561f0a5d4151bc436fca38373
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
5e3521 18 import java.io.IOException;
JM 19 import java.io.InputStream;
b0e164 20 import java.text.MessageFormat;
13a3f5 21 import java.util.ArrayList;
JM 22 import java.util.Calendar;
23 import java.util.Collections;
24 import java.util.Date;
25 import java.util.HashSet;
f98825 26 import java.util.LinkedHashMap;
13a3f5 27 import java.util.List;
f98825 28 import java.util.Map;
9adf62 29 import java.util.ResourceBundle;
13a3f5 30 import java.util.Set;
bc10f9 31 import java.util.TimeZone;
13a3f5 32 import java.util.regex.Pattern;
bc10f9 33
cebf45 34 import javax.servlet.http.HttpServletRequest;
JM 35
5e3521 36 import org.apache.commons.io.IOUtils;
3cc6e2 37 import org.apache.wicket.Application;
1078f8 38 import org.apache.wicket.Page;
5fe7df 39 import org.apache.wicket.PageParameters;
d97e52 40 import org.apache.wicket.RedirectToUrlException;
62cec2 41 import org.apache.wicket.markup.html.CSSPackageResource;
5fe7df 42 import org.apache.wicket.markup.html.basic.Label;
94750e 43 import org.apache.wicket.markup.html.link.ExternalLink;
c1c3c6 44 import org.apache.wicket.markup.html.panel.FeedbackPanel;
d97e52 45 import org.apache.wicket.protocol.http.RequestUtils;
a7db57 46 import org.apache.wicket.protocol.http.WebResponse;
cebf45 47 import org.apache.wicket.protocol.http.servlet.ServletWebRequest;
2598bf 48 import org.apache.wicket.request.target.basic.RedirectRequestTarget;
a7db57 49 import org.apache.wicket.util.time.Duration;
JM 50 import org.apache.wicket.util.time.Time;
5fe7df 51 import org.slf4j.Logger;
JM 52 import org.slf4j.LoggerFactory;
53
cebf45 54 import com.gitblit.Constants;
b0e164 55 import com.gitblit.Constants.AccessPermission;
f98825 56 import com.gitblit.Constants.AccessRestrictionType;
092f0a 57 import com.gitblit.Constants.AuthorizationControl;
831469 58 import com.gitblit.Constants.FederationStrategy;
155bf7 59 import com.gitblit.Keys;
13a3f5 60 import com.gitblit.models.ProjectModel;
JM 61 import com.gitblit.models.TeamModel;
85c2e6 62 import com.gitblit.models.UserModel;
13a3f5 63 import com.gitblit.utils.StringUtils;
9adf62 64 import com.gitblit.utils.TimeUtils;
a7db57 65 import com.gitblit.wicket.CacheControl;
6ef8d7 66 import com.gitblit.wicket.GitBlitWebApp;
1f9dae 67 import com.gitblit.wicket.GitBlitWebSession;
94750e 68 import com.gitblit.wicket.WicketUtils;
5fe7df 69
0f3cb2 70 public abstract class BasePage extends SessionPage {
5fe7df 71
e92cef 72     private transient Logger logger;
699e71 73
9adf62 74     private transient TimeUtils timeUtils;
5fe7df 75
JM 76     public BasePage() {
77         super();
62cec2 78         customizeHeader();
5fe7df 79     }
JM 80
81     public BasePage(PageParameters params) {
82         super(params);
62cec2 83         customizeHeader();
e92cef 84     }
JM 85
86     protected Logger logger() {
87         if (logger == null) {
88             logger = LoggerFactory.getLogger(getClass());
89         }
90         return logger;
85c2e6 91     }
699e71 92
62cec2 93     private void customizeHeader() {
99d0d4 94         if (app().settings().getBoolean(Keys.web.useResponsiveLayout, true)) {
62cec2 95             add(CSSPackageResource.getHeaderContribution("bootstrap/css/bootstrap-responsive.css"));
JM 96         }
5ec752 97         if (app().settings().getBoolean(Keys.web.hideHeader, false)) {
JM 98             add(CSSPackageResource.getHeaderContribution("hideheader.css"));
99         }
62cec2 100     }
699e71 101
ff17f7 102     protected String getContextUrl() {
JM 103         return getRequest().getRelativePathPrefixToContextRoot();
104     }
105
307910 106     protected String getCanonicalUrl() {
JM 107         return getCanonicalUrl(getClass(), getPageParameters());
108     }
109
110     protected String getCanonicalUrl(Class<? extends BasePage> clazz, PageParameters params) {
111         String relativeUrl = urlFor(clazz, params).toString();
112         String canonicalUrl = RequestUtils.toAbsolutePath(relativeUrl);
113         return canonicalUrl;
2598bf 114     }
JM 115
60bbdf 116     protected void redirectTo(Class<? extends BasePage> pageClass) {
JM 117         redirectTo(pageClass, null);
2598bf 118     }
JM 119
60bbdf 120     protected void redirectTo(Class<? extends BasePage> pageClass, PageParameters parameters) {
2598bf 121         String absoluteUrl = getCanonicalUrl(pageClass, parameters);
JM 122         getRequestCycle().setRequestTarget(new RedirectRequestTarget(absoluteUrl));
307910 123     }
JM 124
9adf62 125     protected String getLanguageCode() {
JM 126         return GitBlitWebSession.get().getLocale().getLanguage();
127     }
699e71 128
55037b 129     protected String getCountryCode() {
JM 130         return GitBlitWebSession.get().getLocale().getCountry().toLowerCase();
131     }
699e71 132
9adf62 133     protected TimeUtils getTimeUtils() {
JM 134         if (timeUtils == null) {
699e71 135             ResourceBundle bundle;
9adf62 136             try {
JM 137                 bundle = ResourceBundle.getBundle("com.gitblit.wicket.GitBlitWebApp", GitBlitWebSession.get().getLocale());
138             } catch (Throwable t) {
139                 bundle = ResourceBundle.getBundle("com.gitblit.wicket.GitBlitWebApp");
140             }
9b26b7 141             timeUtils = new TimeUtils(bundle, getTimeZone());
9adf62 142         }
JM 143         return timeUtils;
144     }
699e71 145
3cc6e2 146     @Override
JM 147     protected void onBeforeRender() {
99d0d4 148         if (app().isDebugMode()) {
3cc6e2 149             // strip Wicket tags in debug mode for jQuery DOM traversal
JM 150             Application.get().getMarkupSettings().setStripWicketTags(true);
151         }
152         super.onBeforeRender();
153     }
154
155     @Override
156     protected void onAfterRender() {
99d0d4 157         if (app().isDebugMode()) {
3cc6e2 158             // restore Wicket debug tags
JM 159             Application.get().getMarkupSettings().setStripWicketTags(false);
160         }
161         super.onAfterRender();
a7db57 162     }
699e71 163
a7db57 164     @Override
JM 165     protected void setHeaders(WebResponse response)    {
307910 166         // set canonical link as http header for SEO (issue-304)
JM 167         // https://support.google.com/webmasters/answer/139394?hl=en
99d0d4 168         response.setHeader("Link", MessageFormat.format("<{0}>; rel=\"canonical\"", getCanonicalUrl()));
JM 169         int expires = app().settings().getInteger(Keys.web.pageCacheExpires, 0);
a7db57 170         if (expires > 0) {
JM 171             // pages are personalized for the authenticated user so they must be
172             // marked private to prohibit proxy servers from caching them
173             response.setHeader("Cache-Control", "private, must-revalidate");
174             setLastModified();
175         } else {
176             // use default Wicket caching behavior
177             super.setHeaders(response);
178         }
71647a 179
JM 180         // XRF vulnerability. issue-500 / ticket-166
181         response.setHeader("X-Frame-Options", "SAMEORIGIN");
a7db57 182     }
699e71 183
a7db57 184     /**
JM 185      * Sets the last-modified header date, if appropriate, for this page.  The
186      * date used is determined by the CacheControl annotation.
699e71 187      *
a7db57 188      */
JM 189     protected void setLastModified() {
190         if (getClass().isAnnotationPresent(CacheControl.class)) {
191             CacheControl cacheControl = getClass().getAnnotation(CacheControl.class);
192             switch (cacheControl.value()) {
193             case ACTIVITY:
99d0d4 194                 setLastModified(app().getLastActivityDate());
a7db57 195                 break;
JM 196             case BOOT:
99d0d4 197                 setLastModified(app().getBootDate());
a7db57 198                 break;
JM 199             case NONE:
200                 break;
201             default:
e92cef 202                 logger().warn(getClass().getSimpleName() + ": unhandled LastModified type " + cacheControl.value());
a7db57 203                 break;
JM 204             }
205         }
206     }
699e71 207
a7db57 208     /**
JM 209      * Sets the last-modified header field and the expires field.
699e71 210      *
a7db57 211      * @param when
JM 212      */
213     protected final void setLastModified(Date when) {
214         if (when == null) {
215             return;
216         }
699e71 217
99d0d4 218         if (when.before(app().getBootDate())) {
a7db57 219             // last-modified can not be before the Gitblit boot date
JM 220             // this helps ensure that pages are properly refreshed after a
221             // server config change
99d0d4 222             when = app().getBootDate();
a7db57 223         }
699e71 224
99d0d4 225         int expires = app().settings().getInteger(Keys.web.pageCacheExpires, 0);
a7db57 226         WebResponse response = (WebResponse) getResponse();
JM 227         response.setLastModifiedTime(Time.valueOf(when));
228         response.setDateHeader("Expires", System.currentTimeMillis() + Duration.minutes(expires).getMilliseconds());
229     }
155bf7 230
81c90e 231     protected String getPageTitle(String repositoryName) {
99d0d4 232         String siteName = app().settings().getString(Keys.web.siteName, Constants.NAME);
9b26b7 233         if (StringUtils.isEmpty(siteName)) {
JM 234             siteName = Constants.NAME;
235         }
cebf45 236         if (repositoryName != null && repositoryName.trim().length() > 0) {
81c90e 237             return repositoryName + " - " + siteName;
cebf45 238         } else {
81c90e 239             return siteName;
cebf45 240         }
81c90e 241     }
JM 242
243     protected void setupPage(String repositoryName, String pageName) {
244         add(new Label("title", getPageTitle(repositoryName)));
155bf7 245
99d0d4 246         String rootLinkUrl = app().settings().getString(Keys.web.rootLink, urlFor(GitBlitWebApp.get().getHomePage(), null).toString());
a0831d 247         ExternalLink rootLink = new ExternalLink("rootLink", rootLinkUrl);
99d0d4 248         WicketUtils.setHtmlTooltip(rootLink, app().settings().getString(Keys.web.siteName, Constants.NAME));
94750e 249         add(rootLink);
JM 250
c1c3c6 251         // Feedback panel for info, warning, and non-fatal error messages
JM 252         add(new FeedbackPanel("feedback"));
20165d 253
f6b200 254         add(new Label("gbVersion", "v" + Constants.getVersion()));
99d0d4 255         if (app().settings().getBoolean(Keys.web.aggressiveHeapManagement, false)) {
cebf45 256             System.gc();
JM 257         }
258     }
155bf7 259
f98825 260     protected Map<AccessRestrictionType, String> getAccessRestrictions() {
JM 261         Map<AccessRestrictionType, String> map = new LinkedHashMap<AccessRestrictionType, String>();
262         for (AccessRestrictionType type : AccessRestrictionType.values()) {
263             switch (type) {
264             case NONE:
265                 map.put(type, getString("gb.notRestricted"));
266                 break;
267             case PUSH:
268                 map.put(type, getString("gb.pushRestricted"));
269                 break;
270             case CLONE:
271                 map.put(type, getString("gb.cloneRestricted"));
272                 break;
273             case VIEW:
274                 map.put(type, getString("gb.viewRestricted"));
275                 break;
276             }
277         }
278         return map;
279     }
699e71 280
b0e164 281     protected Map<AccessPermission, String> getAccessPermissions() {
JM 282         Map<AccessPermission, String> map = new LinkedHashMap<AccessPermission, String>();
283         for (AccessPermission type : AccessPermission.values()) {
284             switch (type) {
285             case NONE:
286                 map.put(type, MessageFormat.format(getString("gb.noPermission"), type.code));
287                 break;
2d48e2 288             case EXCLUDE:
JM 289                 map.put(type, MessageFormat.format(getString("gb.excludePermission"), type.code));
290                 break;
b0e164 291             case VIEW:
JM 292                 map.put(type, MessageFormat.format(getString("gb.viewPermission"), type.code));
293                 break;
294             case CLONE:
295                 map.put(type, MessageFormat.format(getString("gb.clonePermission"), type.code));
296                 break;
297             case PUSH:
298                 map.put(type, MessageFormat.format(getString("gb.pushPermission"), type.code));
299                 break;
300             case CREATE:
301                 map.put(type, MessageFormat.format(getString("gb.createPermission"), type.code));
302                 break;
303             case DELETE:
304                 map.put(type, MessageFormat.format(getString("gb.deletePermission"), type.code));
305                 break;
306             case REWIND:
307                 map.put(type, MessageFormat.format(getString("gb.rewindPermission"), type.code));
308                 break;
309             }
310         }
311         return map;
312     }
699e71 313
831469 314     protected Map<FederationStrategy, String> getFederationTypes() {
JM 315         Map<FederationStrategy, String> map = new LinkedHashMap<FederationStrategy, String>();
316         for (FederationStrategy type : FederationStrategy.values()) {
317             switch (type) {
318             case EXCLUDE:
319                 map.put(type, getString("gb.excludeFromFederation"));
320                 break;
321             case FEDERATE_THIS:
322                 map.put(type, getString("gb.federateThis"));
323                 break;
324             case FEDERATE_ORIGIN:
325                 map.put(type, getString("gb.federateOrigin"));
326                 break;
327             }
328         }
329         return map;
330     }
699e71 331
092f0a 332     protected Map<AuthorizationControl, String> getAuthorizationControls() {
JM 333         Map<AuthorizationControl, String> map = new LinkedHashMap<AuthorizationControl, String>();
334         for (AuthorizationControl type : AuthorizationControl.values()) {
335             switch (type) {
336             case AUTHENTICATED:
337                 map.put(type, getString("gb.allowAuthenticatedDescription"));
338                 break;
339             case NAMED:
340                 map.put(type, getString("gb.allowNamedDescription"));
341                 break;
342             }
343         }
344         return map;
345     }
f98825 346
bc10f9 347     protected TimeZone getTimeZone() {
99d0d4 348         return app().settings().getBoolean(Keys.web.useClientTimezone, false) ? GitBlitWebSession.get()
JM 349                 .getTimezone() : app().getTimezone();
bc10f9 350     }
155bf7 351
cebf45 352     protected String getServerName() {
JM 353         ServletWebRequest servletWebRequest = (ServletWebRequest) getRequest();
354         HttpServletRequest req = servletWebRequest.getHttpServletRequest();
355         return req.getServerName();
356     }
699e71 357
13a3f5 358     protected List<ProjectModel> getProjectModels() {
JM 359         final UserModel user = GitBlitWebSession.get().getUser();
99d0d4 360         List<ProjectModel> projects = app().projects().getProjectModels(user, true);
13a3f5 361         return projects;
JM 362     }
699e71 363
13a3f5 364     protected List<ProjectModel> getProjects(PageParameters params) {
JM 365         if (params == null) {
366             return getProjectModels();
367         }
368
369         boolean hasParameter = false;
370         String regex = WicketUtils.getRegEx(params);
371         String team = WicketUtils.getTeam(params);
372         int daysBack = params.getInt("db", 0);
99d0d4 373         int maxDaysBack = app().settings().getInteger(Keys.web.activityDurationMaximum, 30);
13a3f5 374
JM 375         List<ProjectModel> availableModels = getProjectModels();
376         Set<ProjectModel> models = new HashSet<ProjectModel>();
377
378         if (!StringUtils.isEmpty(regex)) {
379             // filter the projects by the regex
380             hasParameter = true;
381             Pattern pattern = Pattern.compile(regex);
382             for (ProjectModel model : availableModels) {
383                 if (pattern.matcher(model.name).find()) {
384                     models.add(model);
385                 }
386             }
387         }
388
389         if (!StringUtils.isEmpty(team)) {
390             // filter the projects by the specified teams
391             hasParameter = true;
392             List<String> teams = StringUtils.getStringsFromValue(team, ",");
393
394             // need TeamModels first
395             List<TeamModel> teamModels = new ArrayList<TeamModel>();
396             for (String name : teams) {
99d0d4 397                 TeamModel teamModel = app().users().getTeamModel(name);
13a3f5 398                 if (teamModel != null) {
JM 399                     teamModels.add(teamModel);
400                 }
401             }
402
403             // brute-force our way through finding the matching models
404             for (ProjectModel projectModel : availableModels) {
405                 for (String repositoryName : projectModel.repositories) {
406                     for (TeamModel teamModel : teamModels) {
20714a 407                         if (teamModel.hasRepositoryPermission(repositoryName)) {
13a3f5 408                             models.add(projectModel);
JM 409                         }
410                     }
411                 }
412             }
413         }
414
415         if (!hasParameter) {
416             models.addAll(availableModels);
417         }
418
419         // time-filter the list
420         if (daysBack > 0) {
da7151 421             if (maxDaysBack > 0 && daysBack > maxDaysBack) {
OL 422                 daysBack = maxDaysBack;
423             }
13a3f5 424             Calendar cal = Calendar.getInstance();
JM 425             cal.set(Calendar.HOUR_OF_DAY, 0);
426             cal.set(Calendar.MINUTE, 0);
427             cal.set(Calendar.SECOND, 0);
428             cal.set(Calendar.MILLISECOND, 0);
429             cal.add(Calendar.DATE, -1 * daysBack);
430             Date threshold = cal.getTime();
431             Set<ProjectModel> timeFiltered = new HashSet<ProjectModel>();
432             for (ProjectModel model : models) {
433                 if (model.lastChange.after(threshold)) {
434                     timeFiltered.add(model);
435                 }
436             }
437             models = timeFiltered;
438         }
439
440         List<ProjectModel> list = new ArrayList<ProjectModel>(models);
441         Collections.sort(list);
442         return list;
443     }
85c2e6 444
5450d0 445     public void warn(String message, Throwable t) {
e92cef 446         logger().warn(message, t);
5450d0 447     }
699e71 448
bc9d4a 449     public void error(String message, boolean redirect) {
1078f8 450         error(message, null, redirect ? getApplication().getHomePage() : null);
bc9d4a 451     }
JM 452
453     public void error(String message, Throwable t, boolean redirect) {
1078f8 454         error(message, t, getApplication().getHomePage());
JM 455     }
699e71 456
1078f8 457     public void error(String message, Throwable t, Class<? extends Page> toPage) {
JM 458         error(message, t, toPage, null);
459     }
699e71 460
1078f8 461     public void error(String message, Throwable t, Class<? extends Page> toPage, PageParameters params) {
JM 462         if (t == null) {
e92cef 463             logger().error(message  + " for " + GitBlitWebSession.get().getUsername());
1078f8 464         } else {
e92cef 465             logger().error(message  + " for " + GitBlitWebSession.get().getUsername(), t);
1078f8 466         }
JM 467         if (toPage != null) {
bc9d4a 468             GitBlitWebSession.get().cacheErrorMessage(message);
1078f8 469             String relativeUrl = urlFor(toPage, params).toString();
JM 470             String absoluteUrl = RequestUtils.toAbsolutePath(relativeUrl);
471             throw new RedirectToUrlException(absoluteUrl);
bc9d4a 472         } else {
JM 473             super.error(message);
474         }
5fe7df 475     }
85c2e6 476
JM 477     public void authenticationError(String message) {
e92cef 478         logger().error(getRequest().getURL() + " for " + GitBlitWebSession.get().getUsername());
d97e52 479         if (!GitBlitWebSession.get().isLoggedIn()) {
JM 480             // cache the request if we have not authenticated.
481             // the request will continue after authentication.
482             GitBlitWebSession.get().cacheRequest(getClass());
85c2e6 483         }
d97e52 484         error(message, true);
20165d 485     }
5e3521 486
JM 487     protected String readResource(String resource) {
488         StringBuilder sb = new StringBuilder();
489         InputStream is = null;
490         try {
491             is = getClass().getResourceAsStream(resource);
492             List<String> lines = IOUtils.readLines(is);
493             for (String line : lines) {
494                 sb.append(line).append('\n');
495             }
496         } catch (IOException e) {
497
498         } finally {
499             if (is != null) {
500                 try {
501                     is.close();
502                 } catch (IOException e) {
503                 }
504             }
505         }
506         return sb.toString();
507     }
2598bf 508
5fe7df 509 }