Mercurial Hosting > nabble
comparison src/nabble/view/lib/Jtp.java @ 0:7ecd1a4ef557
add content
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Thu, 21 Mar 2019 19:15:52 -0600 |
parents | |
children | 18cf4872fd7f |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:7ecd1a4ef557 |
---|---|
1 package nabble.view.lib; | |
2 | |
3 import fschmidt.util.java.HtmlUtils; | |
4 import fschmidt.util.servlet.ServletUtils; | |
5 import nabble.model.DailyNumber; | |
6 import nabble.model.Init; | |
7 import nabble.model.ModelHome; | |
8 import nabble.model.Node; | |
9 import nabble.model.NodeIterator; | |
10 import nabble.model.NodeSearcher; | |
11 import nabble.model.Person; | |
12 import nabble.model.Site; | |
13 import nabble.model.User; | |
14 import nabble.naml.compiler.Template; | |
15 import nabble.naml.compiler.TemplatePrintWriter; | |
16 import nabble.naml.namespaces.BasicNamespace; | |
17 import nabble.view.web.forum.Permalink; | |
18 import nabble.view.web.template.NabbleNamespace; | |
19 import nabble.view.web.template.NodeNamespace; | |
20 import nabble.view.web.template.UserNamespace; | |
21 import org.apache.commons.fileupload.DiskFileUpload; | |
22 import org.apache.commons.fileupload.FileItem; | |
23 import org.apache.commons.fileupload.FileUploadException; | |
24 import org.slf4j.Logger; | |
25 import org.slf4j.LoggerFactory; | |
26 | |
27 import javax.servlet.ServletException; | |
28 import javax.servlet.http.HttpServletRequest; | |
29 import javax.servlet.http.HttpServletResponse; | |
30 import java.io.IOException; | |
31 import java.io.StringWriter; | |
32 import java.util.ArrayList; | |
33 import java.util.Collection; | |
34 import java.util.Collections; | |
35 import java.util.Date; | |
36 import java.util.HashMap; | |
37 import java.util.LinkedHashSet; | |
38 import java.util.List; | |
39 import java.util.Map; | |
40 import java.util.Set; | |
41 import java.util.StringTokenizer; | |
42 import java.util.TreeSet; | |
43 import java.util.regex.Matcher; | |
44 import java.util.regex.Pattern; | |
45 | |
46 | |
47 public final class Jtp { | |
48 | |
49 private static final Logger logger = LoggerFactory.getLogger(Jtp.class); | |
50 | |
51 private static final String ANONYMOUS_COOKIE_ID = "anonymousId"; | |
52 private static final String ANONYMOUS_COOKIE_NAME = "anonymousName"; | |
53 | |
54 private Jtp() {} // never | |
55 | |
56 public static Person getVisitor(HttpServletRequest request, HttpServletResponse response) | |
57 throws ServletException | |
58 { | |
59 Person visitor = getUser(request,response); | |
60 if( visitor != null ) | |
61 return visitor; | |
62 return getOrCreateAnonymous(request,response); | |
63 } | |
64 | |
65 private static Person getAnonymous(HttpServletRequest request) { | |
66 String anonymousId = ServletUtils.getCookieValue(request, ANONYMOUS_COOKIE_ID); | |
67 if (anonymousId == null) | |
68 return null; | |
69 String anonymousName = ServletUtils.getCookieValue(request, ANONYMOUS_COOKIE_NAME); | |
70 return getSite(request).getAnonymous(anonymousId, anonymousName == null? null : HtmlUtils.urlDecode(anonymousName)); | |
71 } | |
72 | |
73 private static Person getOrCreateAnonymous(HttpServletRequest request, HttpServletResponse response) | |
74 throws ServletException | |
75 { | |
76 Person anonymous = getAnonymous(request); | |
77 if (anonymous == null) { | |
78 Site site = getSiteNotNull(request); | |
79 String cookie = site.newAnonymousCookie(); | |
80 anonymous = site.getAnonymous(cookie,null); | |
81 ServletUtils.setCookie(request, response, ANONYMOUS_COOKIE_ID, cookie, true, null); | |
82 } | |
83 return anonymous; | |
84 } | |
85 | |
86 public static User getUser(HttpServletRequest request, HttpServletResponse response) | |
87 throws ServletException | |
88 { | |
89 return getUser(request); | |
90 } | |
91 | |
92 public static User getUser(HttpServletRequest request) | |
93 throws ServletException | |
94 { | |
95 User user = null; | |
96 String userId = ServletUtils.getCookieValue(request,"userId"); | |
97 if (userId != null && userId.length() > 0) { | |
98 user = getSiteNotNull(request).getUser(Long.valueOf(userId)); | |
99 } | |
100 if( user==null ) | |
101 return null; | |
102 if( ServletUtils.getCookieValue(request,"username")==null) | |
103 return null; | |
104 String pwd = ServletUtils.getCookieValue(request,"password"); | |
105 if ( pwd==null ) | |
106 return null; | |
107 String passcookie = HtmlUtils.urlDecode(pwd); | |
108 if( ! (user.isRegistered() && user.checkPasscookie(passcookie)) ) | |
109 return null; | |
110 trackUser(request, user); | |
111 return user; | |
112 } | |
113 | |
114 public static void doLogin(HttpServletRequest request, HttpServletResponse response, User user, boolean save) | |
115 throws IOException | |
116 { | |
117 if( !user.isRegistered() ) | |
118 throw new RuntimeException("user must be registered to login"); | |
119 | |
120 ServletUtils.setCookie(request,response,"userId", String.valueOf(user.getId()), save, null); | |
121 ServletUtils.setCookie(request,response,"password", HtmlUtils.urlEncode(user.getPasscookie()), save, null); | |
122 ServletUtils.setCookie(request,response,"username", HtmlUtils.urlEncode(user.getName()), save, null); | |
123 dontCache(response); | |
124 | |
125 DailyNumber.logins.inc(); | |
126 trackUser(request, user); | |
127 | |
128 // fix anonymous nodes from this user | |
129 String anonymousId = ServletUtils.getCookieValue(request, ANONYMOUS_COOKIE_ID); | |
130 if (anonymousId != null) { | |
131 user.moveToRegisteredAccount(anonymousId); | |
132 ServletUtils.removeCookie(request, response, ANONYMOUS_COOKIE_ID, null); | |
133 ServletUtils.removeCookie(request, response, ANONYMOUS_COOKIE_NAME, null); | |
134 } | |
135 } | |
136 | |
137 private static Set trackUsers = Init.get("trackUsers", Collections.EMPTY_SET); | |
138 | |
139 private static void trackUser(HttpServletRequest request, User user) { | |
140 long siteId = user.getSite().getId(); | |
141 String siteAndUser = siteId + "|" + user.getId(); | |
142 if (trackUsers.contains(siteAndUser)) | |
143 logger.error("Track User [site=" + siteId + " | user = " + user.getId() + "] = " + getClientIpAddr(request)); | |
144 } | |
145 | |
146 public static String getClientIpAddr(HttpServletRequest request) { | |
147 return JtpContextServlet.getClientIpAddr(request); | |
148 } | |
149 | |
150 public static void logout(HttpServletRequest request,HttpServletResponse response) { | |
151 request.getSession().removeAttribute("nextUrl"); | |
152 ServletUtils.removeCookie(request,response,"userId", null); | |
153 ServletUtils.removeCookie(request,response,"password", null); | |
154 ServletUtils.removeCookie(request,response,"username", null); | |
155 } | |
156 | |
157 public static void login(String msg, HttpServletRequest request, HttpServletResponse response) | |
158 throws IOException, ServletException | |
159 { | |
160 String nextUrl = getCurrentPath(request); | |
161 //if(nextUrl.endsWith("NamlServlet.jtp")) logger.error("nextUrl = "+nextUrl); | |
162 response.sendRedirect(loginPath(getSiteNotNull(request),msg,nextUrl)); | |
163 } | |
164 | |
165 public static String getCurrentPath(HttpServletRequest request) { | |
166 String s = request.getServletPath(); | |
167 String q = request.getQueryString(); | |
168 if( q != null ) | |
169 s += "?" + q; | |
170 return s; | |
171 } | |
172 | |
173 public static String loginPath(Site site,String message,String nextUrl) { | |
174 Map<String,Object> args = new HashMap<String,Object>(); | |
175 if( message != null ) | |
176 args.put("message",message); | |
177 if( nextUrl != null ) | |
178 args.put("nextUrl",nextUrl); | |
179 Template template = site.getTemplate( "login_path", | |
180 BasicNamespace.class, NabbleNamespace.class | |
181 ); | |
182 StringWriter sw = new StringWriter(); | |
183 template.run( new TemplatePrintWriter(sw), args, | |
184 new BasicNamespace(template), new NabbleNamespace(site) | |
185 ); | |
186 return sw.toString(); | |
187 } | |
188 | |
189 | |
190 public static String hideNull(Object obj) { | |
191 return obj==null ? "" : obj.toString(); | |
192 } | |
193 | |
194 public static String userUrl(Person user) { | |
195 String base = user.getSite().getBaseUrl(); | |
196 return base + "/template/NamlServlet.jtp?macro=user_nodes&user=" + user.getIdString(); | |
197 } | |
198 | |
199 public static String userLink(Person user) { | |
200 return "<a href=\"" + userUrl(user) + "\" rel=\"nofollow\" target=\"_top\">" | |
201 +user.getNameHtml()+"</a>"; | |
202 } | |
203 | |
204 public static String userLinkJs(HttpServletRequest request,User user) { | |
205 StringBuilder buf = new StringBuilder(); | |
206 buf.append("<script>document.write('<a href=\\\""); | |
207 buf.append(request.getContextPath()); | |
208 buf.append("' +'/user/UserNodes' + '.jtp?'+'user="); | |
209 buf.append(user.getId()); | |
210 buf.append("\\\" rel=\\\"nofollow\\\" "); | |
211 buf.append("' + Nabble.embeddedTarget('_top') + '"); | |
212 buf.append(">');</script>"); | |
213 buf.append(user.getNameHtml()); | |
214 buf.append("<script>document.write('</a>');</script>"); | |
215 return buf.toString(); | |
216 } | |
217 | |
218 | |
219 public static String subjectEncode(String s) { | |
220 return ViewUtils.subjectEncode(s); | |
221 } | |
222 | |
223 public static String link(Node node) { | |
224 if (node == null) | |
225 return ""; | |
226 return "<a href=\"" + url(node) + "\">" + node.getSubjectHtml() + "</a>"; | |
227 } | |
228 | |
229 public static String url(Node node) { | |
230 return node.getSite().getBaseUrl() + path(node); | |
231 } | |
232 | |
233 public static String path(Node node) { | |
234 switch( node.getKind() ) { | |
235 case POST: | |
236 return Permalink.path(null,node); | |
237 case APP: | |
238 Site site = node.getSite(); | |
239 Template template = site.getTemplate( "app_path", | |
240 BasicNamespace.class, NabbleNamespace.class, NodeNamespace.class | |
241 ); | |
242 StringWriter sw = new StringWriter(); | |
243 template.run( new TemplatePrintWriter(sw), Collections.<String,Object>emptyMap(), | |
244 new BasicNamespace(template), new NabbleNamespace(site), new NodeNamespace(node) | |
245 ); | |
246 return sw.toString(); | |
247 } | |
248 throw new RuntimeException("never"); | |
249 } | |
250 | |
251 public static String topicViewPath(Node node, long selectedId, TopicView topicView) { | |
252 Site site = node.getSite(); | |
253 Template template = site.getTemplate( "topic_path", | |
254 BasicNamespace.class, NabbleNamespace.class, NodeNamespace.class | |
255 ); | |
256 StringWriter sw = new StringWriter(); | |
257 Map<String,Object> args = new HashMap<String,Object>(); | |
258 args.put( "view", topicView == TopicView.CLASSIC? "classic" : topicView == TopicView.THREADED? "threaded" : "list"); | |
259 args.put( "selected_id", selectedId); | |
260 template.run( new TemplatePrintWriter(sw), args, | |
261 new BasicNamespace(template), new NabbleNamespace(site), new NodeNamespace(node) | |
262 ); | |
263 return sw.toString(); | |
264 } | |
265 | |
266 public static String link(User user) { | |
267 return "<a href=\"" + url(user) + "\">" + user.getNameHtml() + "</a>"; | |
268 } | |
269 | |
270 public static String url(User user) { | |
271 return user.getSite().getBaseUrl() + path(user); | |
272 } | |
273 | |
274 public static String path(User user) { | |
275 Site site = user.getSite(); | |
276 Template template = site.getTemplate( "path", | |
277 BasicNamespace.class, NabbleNamespace.class, UserNamespace.class | |
278 ); | |
279 StringWriter sw = new StringWriter(); | |
280 template.run( new TemplatePrintWriter(sw), Collections.<String,Object>emptyMap(), | |
281 new BasicNamespace(template), new NabbleNamespace(site), new UserNamespace(user) | |
282 ); | |
283 return sw.toString(); | |
284 } | |
285 | |
286 public static void javascriptRedirect(HttpServletRequest request,HttpServletResponse response,Node node) | |
287 throws IOException, ServletException | |
288 { | |
289 Shared.javascriptRedirect(request,response,url(node)); | |
290 } | |
291 | |
292 public static String getStartFragment(String text,int minSize,int maxSize) { | |
293 if( text.length() <= maxSize ) | |
294 return text; | |
295 int i = maxSize; | |
296 while( !Character.isWhitespace(text.charAt(i)) ) { | |
297 i--; | |
298 if( i <= minSize ) | |
299 return text.substring(0,maxSize); | |
300 } | |
301 while( Character.isWhitespace(text.charAt(i)) ) { | |
302 if( i < minSize ) | |
303 return text.substring(0,maxSize); | |
304 i--; | |
305 } | |
306 return text.substring(0,i+1); | |
307 } | |
308 | |
309 public static String truncate(String text,int len,String dotdotdot) { | |
310 return text.length() <= len ? text : text.substring(0,len) + dotdotdot; | |
311 } | |
312 | |
313 public static String breakUp(final String text) { | |
314 return HtmlUtils.breakUp(text,30,true); | |
315 } | |
316 | |
317 private static final String PUNCTUATION = "[\\!\"\\&\\'\\(\\)\\*\\+\\,\\.\\/\\:\\;\\<\\=\\>\\?\\@\\[\\]\\^\\_\\`\\{\\|\\}\\~]"; | |
318 | |
319 private static void metaKeywords(final String text,Set<String> words) { | |
320 StringTokenizer str = new StringTokenizer(text.toLowerCase().replaceAll(PUNCTUATION," ")); | |
321 while( str.hasMoreTokens() ) { | |
322 String s = str.nextToken(); | |
323 if( "re".equals(s) ) | |
324 continue; | |
325 words.add(s); | |
326 } | |
327 } | |
328 | |
329 private static final Set<String> invalidKeywords = new TreeSet<String>(); | |
330 static { | |
331 String[] words = { | |
332 "hi", "a", "an", "in", "i", "hello", "please", | |
333 "to", "on", "by", "t", "m", "but", "the", "of", | |
334 "it", "so", "at", "am", "dear", "me", "and", "are", | |
335 "wonder", "from", "be", "been", "is", | |
336 "was", "if", "this", "that", "there" | |
337 }; | |
338 for (String word : words) { | |
339 invalidKeywords.add(word); | |
340 } | |
341 } | |
342 | |
343 private static String metaKeywords(Set<String> words) { | |
344 StringBuilder buf = new StringBuilder(); | |
345 for( String s : words ) { | |
346 if (invalidKeywords.contains(s.toLowerCase())) | |
347 continue; | |
348 if (buf.length() > 0) buf.append(", "); | |
349 buf.append(s); | |
350 } | |
351 return buf.toString(); | |
352 } | |
353 | |
354 public static String metaKeywords(Node node) { | |
355 return metaKeywords(node, true); | |
356 } | |
357 | |
358 public static String metaKeywords(Node node, boolean includeMessage) { | |
359 Set<String> words = new LinkedHashSet<String>(); | |
360 for( Node n = node; n!=null; n = n.getParent() ) { | |
361 metaKeywords(n.getSubjectHtml(),words); | |
362 if (includeMessage) { | |
363 metaKeywords(getFragment(n.getMessage().getText(), 200), words); | |
364 includeMessage = false; | |
365 } | |
366 } | |
367 if( node.getKind() == Node.Kind.APP ) { | |
368 String type = node.getType(); | |
369 if (type.equals(Node.Type.GALLERY)) { | |
370 words.add("photo"); | |
371 words.add("pictures"); | |
372 words.add("gallery"); | |
373 words.add("images"); | |
374 } else if (type.equals(Node.Type.BLOG)) { | |
375 words.add("blog"); | |
376 } else if (type.equals(Node.Type.NEWS)) { | |
377 words.add("news"); | |
378 words.add("newspaper"); | |
379 words.add("magazine"); | |
380 } else { | |
381 words.add("forum"); | |
382 words.add("board"); | |
383 words.add("community"); | |
384 } | |
385 if (node.getAssociatedMailingList() != null) | |
386 words.add("mailing list archive"); | |
387 } | |
388 return metaKeywords(words); | |
389 } | |
390 | |
391 public static String metaDescription(Node node) { | |
392 StringBuilder buf = new StringBuilder(); | |
393 String name = node.getSubjectHtml(); | |
394 buf.append(name); | |
395 if( node.getKind() == Node.Kind.APP ) { | |
396 String viewName = viewName(node).toLowerCase(); | |
397 if (! (name.toLowerCase().indexOf(viewName) >= 0)) { | |
398 buf.append(' ').append(viewName); | |
399 } | |
400 if (node.getAssociatedMailingList()!=null) | |
401 buf.append(" and mailing list archive"); | |
402 buf.append("."); | |
403 } | |
404 appendSnippet(node.getMessage().getText(), buf); | |
405 return buf.toString(); | |
406 } | |
407 | |
408 private static void appendSnippet(String text, StringBuilder buf) { | |
409 if (buf.length() >= 200 || text.length() == 0) return; | |
410 String fragment = getFragment(text, 200 - buf.length()); | |
411 if (buf.charAt(buf.length() - 1) != '.') buf.append('.'); | |
412 buf.append(' '); | |
413 buf.append(ModelHome.hideAllEmails(HtmlUtils.htmlEncode(fragment).replaceAll("\\s+"," "))); | |
414 if (fragment.length() < text.length()) buf.append("..."); | |
415 } | |
416 | |
417 private static String getFragment(String text, int size) { | |
418 if (text.length() <= size) return text; | |
419 int end = text.lastIndexOf(' ', size); | |
420 if (end < 0) end = size; | |
421 return text.substring(0, end); | |
422 } | |
423 | |
424 | |
425 public static String formatDateOnly(String label, Date date) { | |
426 return "<script>document.write('" + label + "' + Nabble.formatDateOnly(new Date("+date.getTime()+")));</script>"; | |
427 } | |
428 | |
429 public static String formatDateOnly(Date date) { | |
430 return "<script>document.write(Nabble.formatDateOnly(new Date("+date.getTime()+")));</script>"; | |
431 } | |
432 | |
433 public static String formatDateLong(String label, Date date) { | |
434 return "<script>document.write('" + label + "' + Nabble.formatDateLong(new Date("+date.getTime()+")));</script>"; | |
435 } | |
436 | |
437 public static String formatDateLong(Date date) { | |
438 return "<script>document.write(Nabble.formatDateLong(new Date("+date.getTime()+")));</script>"; | |
439 } | |
440 | |
441 public static String formatDateShort(Date date) { | |
442 return "<script>document.write(Nabble.formatDateShort(new Date("+date.getTime()+")));</script>"; | |
443 } | |
444 | |
445 | |
446 private static final String jsWriteStart = "<script>document.write("; | |
447 private static final String jsWriteEnd = ");</script>"; | |
448 | |
449 public static String javascriptQuote(String s) { | |
450 StringBuilder buf = new StringBuilder(); | |
451 buf.append( "'" ); | |
452 int i = 0; | |
453 while(true) { | |
454 int i2 = s.indexOf(jsWriteStart,i); | |
455 if( i2 == -1 ) | |
456 break; | |
457 int i3 = s.indexOf(jsWriteEnd,i2); | |
458 if( i3 == -1 ) | |
459 throw new RuntimeException(); | |
460 buf.append( HtmlUtils.javascriptStringEncode(s.substring(i,i2)) ); | |
461 buf.append( "'+(" ); | |
462 buf.append( s.substring(i2+jsWriteStart.length(),i3) ); | |
463 buf.append( ")+'" ); | |
464 i = i3 + jsWriteEnd.length(); | |
465 } | |
466 buf.append( HtmlUtils.javascriptStringEncode(s.substring(i)) ); | |
467 buf.append( "'" ); | |
468 return buf.toString(); | |
469 } | |
470 | |
471 public static int getYear(Date date) { | |
472 return date.getYear() + 1900; | |
473 } | |
474 | |
475 public static int thisYear() { | |
476 return getYear(new Date()); | |
477 } | |
478 | |
479 public static String capitalize(String s) { | |
480 return s.substring(0,1).toUpperCase() + s.substring(1); | |
481 } | |
482 | |
483 public static void dontCache(HttpServletResponse response) { | |
484 response.setHeader("Cache-Control","no-cache, max-age=0"); | |
485 } | |
486 | |
487 | |
488 public static Map<String,FileItem> getFileItems(HttpServletRequest request) | |
489 throws FileUploadException | |
490 { | |
491 DiskFileUpload fu = new DiskFileUpload(); | |
492 Map<String,FileItem> map = new HashMap<String,FileItem>(); | |
493 @SuppressWarnings("unchecked") | |
494 List<FileItem> fileItems = fu.parseRequest(request); | |
495 for( FileItem fi : fileItems ) { | |
496 map.put(fi.getFieldName(),fi); | |
497 } | |
498 return map; | |
499 } | |
500 | |
501 public static String helpIndexUrl(HttpServletRequest request,HttpServletResponse response) | |
502 throws IOException | |
503 { | |
504 StringBuilder url = new StringBuilder(); | |
505 url.append( request.getContextPath() ); | |
506 url.append( "/help/Index.jtp" ); | |
507 return url.toString(); | |
508 } | |
509 | |
510 public static long getLong(HttpServletRequest request,String param) | |
511 throws ServletException | |
512 { | |
513 return parseLong( request, request.getParameter(param) ); | |
514 } | |
515 | |
516 public static long parseLong(HttpServletRequest request,String s) | |
517 throws ServletException | |
518 { | |
519 try { | |
520 return Long.parseLong(s); | |
521 } catch(NumberFormatException e) { | |
522 if (invalidReferer(request) || s.contains(" Result: ")) { | |
523 logger.warn("Bad URL", e); | |
524 throw new MinorServletException(e); | |
525 } else | |
526 throw e; | |
527 } | |
528 } | |
529 | |
530 public static int getInt(HttpServletRequest request,String param) | |
531 throws ServletException | |
532 { | |
533 return parseInt( request, request.getParameter(param) ); | |
534 } | |
535 | |
536 public static int parseInt(HttpServletRequest request,String s) | |
537 throws ServletException | |
538 { | |
539 try { | |
540 return Integer.parseInt(s); | |
541 } catch(NumberFormatException e) { | |
542 if (invalidReferer(request)) | |
543 throw new ServletException(e); | |
544 else | |
545 throw e; | |
546 } | |
547 } | |
548 | |
549 /* | |
550 The User-Agent should not be considered. If a browser doesn't send the referer, then we have no way | |
551 to know if the referer is valid or not. So we should assume it isn't to avoid having cases | |
552 where it really isn't valid go into error.log . If the referer really was valid, then the same problem | |
553 should show up again in a good browser. We shouldn't worry so much about taking care of broken browsers. | |
554 -fschmidt | |
555 */ | |
556 public static boolean invalidReferer(HttpServletRequest request) { | |
557 String referer = request.getHeader("referer"); | |
558 if( referer == null | |
559 || !referer.startsWith("http://" + request.getHeader("host")) | |
560 ) | |
561 return true; | |
562 if( request.getMethod().equals("GET") ) { | |
563 StringBuffer url = request.getRequestURL(); | |
564 String query = request.getQueryString(); | |
565 if( query != null ) | |
566 url.append( '?' ).append( query ); | |
567 if( url.toString().equals(referer) ) | |
568 return true; | |
569 } | |
570 return false; | |
571 } | |
572 | |
573 public static ServletException servletException(HttpServletRequest request,String msg) { | |
574 if( invalidReferer(request) ) | |
575 return new ServletException(msg); | |
576 throw new RuntimeException(msg); | |
577 } | |
578 | |
579 public static String getString(HttpServletRequest request, String param) | |
580 throws ServletException | |
581 { | |
582 String value = request.getParameter(param); | |
583 if (value == null && invalidReferer(request)) { | |
584 throw new ServletException("Parameter is null: " + param + " [referer is null]"); | |
585 } | |
586 return value; | |
587 } | |
588 | |
589 // should be removed | |
590 public static String getReadAuthorizationKey(Node node) { | |
591 if( node==null ) | |
592 return null; | |
593 node = Permissions.getPrivateNode(node); | |
594 return node!=null ? Long.toString(node.getId()) : null; | |
595 } | |
596 | |
597 // should be removed | |
598 public static boolean authorizeForRead(String key,HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException { | |
599 Node node = getSiteNotNull(request).getNode( Long.parseLong(key) ); | |
600 User user = getUser(request,response); | |
601 if( user==null ) { | |
602 login("You must login to view " + node.getSubject(), request, response); | |
603 return false; | |
604 } | |
605 | |
606 if( !Jtp.canBeViewedBy(node,user) ) { | |
607 response.sendRedirect(getUnauthorizedPath(node)); | |
608 return false; | |
609 } | |
610 return true; | |
611 } | |
612 | |
613 private static String getUnauthorizedPath(Node node) { | |
614 Template template = node.getSite().getTemplate( "unauthorized_path", | |
615 BasicNamespace.class, NabbleNamespace.class, NodeNamespace.class | |
616 ); | |
617 StringWriter sw = new StringWriter(); | |
618 template.run( new TemplatePrintWriter(sw), Collections.<String, Object>emptyMap(), | |
619 new BasicNamespace(template), new NabbleNamespace(node.getSite()), new NodeNamespace(Permissions.getPrivateNode(node)) | |
620 ); | |
621 return sw.toString(); | |
622 } | |
623 | |
624 private static final long startTime = System.currentTimeMillis()/1000*1000; | |
625 | |
626 public static boolean cacheMe(HttpServletRequest request,HttpServletResponse response) | |
627 throws ServletException, IOException | |
628 { | |
629 if( startTime <= request.getDateHeader("If-Modified-Since") ) { | |
630 response.sendError(HttpServletResponse.SC_NOT_MODIFIED); | |
631 return true; | |
632 } | |
633 response.setDateHeader("Last-Modified",startTime); | |
634 return false; | |
635 } | |
636 | |
637 | |
638 public static void addBreadCrumbEvents(Collection<String> events,Node node) { | |
639 if( node==null ) return; | |
640 for( Node f = node.getApp(); f != null; f = f.getParent() ) { | |
641 events.add( Cache.nodeChangeEvent(f) ); | |
642 } | |
643 } | |
644 | |
645 | |
646 public static List<Node> getPinnedChildren(Node parent) { | |
647 List<Node> pinned = new ArrayList<Node>(); | |
648 NodeIterator<? extends Node> iter = parent.getChildren(); | |
649 try { | |
650 while( iter.hasNext() ) { | |
651 Node node = iter.next(); | |
652 if( !node.isPinned() ) | |
653 break; | |
654 pinned.add(node); | |
655 } | |
656 } finally { | |
657 iter.close(); | |
658 } | |
659 return pinned; | |
660 } | |
661 | |
662 public static void addPinnedChild(Node parent, Node child) { | |
663 List<Node> pinned = getPinnedChildren(parent); | |
664 if (child.getKind() == Node.Kind.APP) { | |
665 // Insert after the last pinned forum (before pinned threads) | |
666 boolean added = false; | |
667 for (int i = 0; i < pinned.size(); i++) { | |
668 Node node = pinned.get(i); | |
669 if (node.getKind() == Node.Kind.POST) { | |
670 pinned.add(i, child); | |
671 added = true; | |
672 break; | |
673 } | |
674 } | |
675 if (!added) | |
676 pinned.add(child); | |
677 } | |
678 else { | |
679 pinned.add(child); | |
680 } | |
681 parent.pin(pinned.toArray(new Node[0])); | |
682 } | |
683 | |
684 public static void unpinChild(Node parent, Node child) { | |
685 List<Node> pinned = getPinnedChildren(parent); | |
686 pinned.remove(child); | |
687 parent.pin(pinned.toArray(new Node[0])); | |
688 } | |
689 | |
690 | |
691 public static String snippet(Node node,int len) { | |
692 String text = node.getMessage().getTextWithoutQuotes(); | |
693 return HtmlUtils.htmlEncode(ModelHome.hideAllEmails(NodeSearcher.getStartingFragment(text, len, "..."))); | |
694 } | |
695 | |
696 | |
697 public static String noCid(String url) { | |
698 return url.replaceAll("(;|&)cid=(\\d|\\-)+", ""); | |
699 } | |
700 | |
701 // should go away -fschmidt | |
702 public static String viewName(Node node) { | |
703 Node forum = node.getApp(); | |
704 if (forum == null) | |
705 return "Forum"; | |
706 String type = forum.getType(); | |
707 if (type.equals(Node.Type.GALLERY)) | |
708 return "Gallery"; | |
709 else if (type.equals(Node.Type.NEWS)) | |
710 return "Newspaper"; | |
711 else if (type.equals(Node.Type.BLOG)) | |
712 return "Blog"; | |
713 else | |
714 return "Forum"; | |
715 } | |
716 | |
717 // should go away -fschmidt | |
718 public static String childName(Node node, boolean plural) { | |
719 Node forum = node.getApp(); | |
720 if (forum == null) | |
721 return plural? "Sub-Forums" : "Sub-Forum"; | |
722 String type = forum.getType(); | |
723 if (type.equals(Node.Type.GALLERY) || | |
724 type.equals(Node.Type.NEWS) || | |
725 type.equals(Node.Type.BLOG) || | |
726 type.equals(Node.Type.BOARD)) | |
727 return plural? "Subcategories" : "Subcategory"; | |
728 else | |
729 return plural? "Sub-Forums" : "Sub-Forum"; | |
730 } | |
731 | |
732 public static String parentName(Node node) { | |
733 if (node.getParent() == null) | |
734 return "Current Parent"; | |
735 return "Parent " + viewName(node.getParent()); | |
736 } | |
737 | |
738 // what do you plan for this? -fschmidt | |
739 public static String getSmallLogo(String type) { | |
740 if (type.equals(Node.Type.GALLERY)) | |
741 return "<img src=\"/images/homepage/gallery_sm.png\" width=20 height=15 alt=\"Free photo gallery\">"; | |
742 else if (type.equals(Node.Type.BLOG)) | |
743 return "<img src=\"/images/homepage/blog_sm.png\" width=20 height=17 alt=\"Free blog\">"; | |
744 else if (type.equals(Node.Type.NEWS)) | |
745 return "<img src=\"/images/homepage/news_sm.png\" width=20 height=15 alt=\"Free newspaper\">"; | |
746 else | |
747 return ""; | |
748 } | |
749 | |
750 static String getCanonicalUrl(HttpServletRequest request) { | |
751 String current = ServletUtils.getCurrentURL(request); | |
752 if (current.startsWith(Lazy.homeContextUrl)) | |
753 return null; | |
754 String host = request.getHeader("host"); | |
755 return current.replace("http://"+host, Lazy.homeContextUrl); | |
756 } | |
757 | |
758 private static final Pattern NUMBER_PATTERN = Pattern.compile("^[0-9]+$"); | |
759 public static boolean isInteger(String s) { | |
760 return s != null && NUMBER_PATTERN.matcher(s).find(); | |
761 } | |
762 | |
763 /** | |
764 * Maximum number of rows an app page can have. | |
765 * This limit must exist because there are physical restrictions: | |
766 * - the $Js URL can't support too many nodes at the same time. | |
767 * - div tags have a limit of 32,768 pixels of height. | |
768 */ | |
769 public static int getMaxRowsPerPage(String type) { | |
770 int n = 100; | |
771 if (type.equals(Node.Type.BLOG)) | |
772 n = 20; | |
773 return n; | |
774 } | |
775 | |
776 /** Default number of rows an app page has. */ | |
777 public static int getDefaultRowsPerPage(String type) { | |
778 int n = 35; | |
779 if (type.equals(Node.Type.BLOG)) | |
780 n = 10; | |
781 else if (type.equals(Node.Type.GALLERY)) | |
782 n = 12; | |
783 else if (type.equals(Node.Type.GALLERY)) | |
784 n = 25; | |
785 return n; | |
786 } | |
787 | |
788 public static int getDefaultMixedLength() { return 6;} | |
789 public static int getMaxMixedLength() { return 20;} | |
790 | |
791 private static final String defaultHost = (String)Init.get("defaultHost"); | |
792 static { | |
793 if( defaultHost==null ) { | |
794 logger.error("no defaultHost"); | |
795 System.exit(-1); | |
796 } | |
797 } | |
798 | |
799 public static String getDefaultHost() { | |
800 return defaultHost; | |
801 } | |
802 | |
803 public static String getBaseUrl(HttpServletRequest request) { | |
804 String host = request.getHeader("host"); | |
805 String scheme = request.getHeader("X-Forwarded-Proto"); | |
806 if( scheme == null ) | |
807 scheme = request.getScheme(); | |
808 return scheme + "://" + host; | |
809 } | |
810 | |
811 // fix jetty bug | |
812 public static void sendRedirect(HttpServletRequest request,HttpServletResponse response,String url) | |
813 throws IOException | |
814 { | |
815 if( url.startsWith("/") ) | |
816 url = Jtp.getBaseUrl(request) + url; | |
817 response.sendRedirect(url); | |
818 } | |
819 | |
820 private static class Lazy { | |
821 static final Pattern ROOT_URL_PATTERN; | |
822 static final String defaultContextUrl; | |
823 static final String homeContextUrl; | |
824 static { | |
825 defaultContextUrl = "http://" + defaultHost; | |
826 homeContextUrl = Init.get("homeContextUrl",defaultContextUrl); | |
827 ROOT_URL_PATTERN = Pattern.compile( ".*\\.(\\d+)\\."+Pattern.quote(defaultHost) ); | |
828 } | |
829 } | |
830 | |
831 public static String homePage() { | |
832 return nabble.view.web.Index.url(); | |
833 } | |
834 | |
835 public static String supportUrl() { | |
836 return "http://support.nabble.com/"; | |
837 } | |
838 | |
839 public static String supportLink() { | |
840 String supportUrl = supportUrl(); | |
841 return supportUrl==null ? null : "<a href='"+supportUrl+"' target='_top'>Nabble Support</a>"; | |
842 } | |
843 | |
844 public static String defaultContextUrl() { | |
845 return Lazy.defaultContextUrl; | |
846 } | |
847 | |
848 public static String homeContextUrl() { | |
849 return Lazy.homeContextUrl; | |
850 } | |
851 | |
852 // doesn't return null | |
853 public static Site getSiteNotNull(HttpServletRequest request) | |
854 throws ServletException | |
855 { | |
856 Site site = getSite(request); | |
857 if( site == null ) | |
858 throw servletException(request,"site not found"); | |
859 return site; | |
860 } | |
861 | |
862 private static final Object noSite = new Object(); | |
863 | |
864 public static Site getSite(HttpServletRequest request) { | |
865 Object obj = request.getAttribute("site"); | |
866 if( obj == null ) { | |
867 Long siteId = getSiteIdFromDomain( ServletUtils.getHost(request) ); | |
868 if( siteId != null ) | |
869 obj = ModelHome.getSite(siteId); | |
870 if( obj == null ) | |
871 obj = noSite; | |
872 request.setAttribute("root",obj); | |
873 } | |
874 return obj==noSite ? null : (Site)obj; | |
875 } | |
876 | |
877 public static Site getSiteFromUrl(String url) { | |
878 String domain = extractDomain(url); | |
879 if( domain == null ) | |
880 return null; | |
881 Long siteId = getSiteIdFromDomain(domain); | |
882 if( siteId == null ) | |
883 return null; | |
884 return ModelHome.getSite(siteId); | |
885 } | |
886 | |
887 public static Long getSiteIdFromDomain(String domain) { | |
888 if( domain.equals(defaultHost) ) | |
889 return null; | |
890 Matcher m = Lazy.ROOT_URL_PATTERN.matcher(domain); | |
891 if( m.matches() ) { | |
892 return Long.parseLong(m.group(1)); | |
893 } else { | |
894 return ModelHome.getSiteIdFromDomain(domain); | |
895 } | |
896 } | |
897 | |
898 public static String getDefaultBaseUrl(Site site) { | |
899 return ViewUtils.getDefaultBaseUrl( site.getId(), site.getRootNode().getSubject(), defaultHost ); | |
900 } | |
901 | |
902 public static String extractDomain(String url) { | |
903 int posDoubleSlash = url.indexOf("//"); | |
904 int posDomainEnd = url.indexOf('/', posDoubleSlash+2); | |
905 // if the last slash was not found, we can assume the domain ends at the end of the string. | |
906 posDomainEnd = posDomainEnd == -1? url.length() : posDomainEnd; | |
907 return posDoubleSlash > 0 && posDoubleSlash < 8 && posDomainEnd > posDoubleSlash? url.substring(posDoubleSlash+2, posDomainEnd) : null; | |
908 } | |
909 | |
910 // permissions hacks | |
911 | |
912 private static boolean can(Person person,String macro,Node node) { | |
913 Template template = node.getSite().getTemplate( macro, | |
914 BasicNamespace.class, NabbleNamespace.class, UserNamespace.class | |
915 ); | |
916 StringWriter sw = new StringWriter(); | |
917 Map<String,Object> args = new HashMap<String,Object>(); | |
918 args.put( "node_attr", new NodeNamespace(node) ); | |
919 template.run( new TemplatePrintWriter(sw), args, | |
920 new BasicNamespace(template), new NabbleNamespace(node.getSite()), new UserNamespace(person) | |
921 ); | |
922 return sw.toString().equals("true"); | |
923 } | |
924 | |
925 public static boolean canBeEditedBy(Node node,Person person) { | |
926 return can(person,"can_edit",node); | |
927 } | |
928 | |
929 public static boolean canBeRemovedBy(Node node,Person person) { | |
930 return can(person,"can_move",node); | |
931 } | |
932 | |
933 public static boolean canBeDeletedBy(Node node,Person person) { | |
934 return can(person,"can_delete",node); | |
935 } | |
936 | |
937 public static boolean canBeViewedBy(Node node,Person person) { | |
938 return can(person,"can_view",node); | |
939 } | |
940 | |
941 public static boolean canMove(Node node,Person person) { | |
942 return can(person,"can_move",node); | |
943 } | |
944 | |
945 public static boolean canAssign(Node node,Person person) { | |
946 return can(person,"can_be_assigned_to",node); | |
947 } | |
948 | |
949 public static boolean canChangePostDateOf(Node node,Person person) { | |
950 return can(person,"can_change_post_date_of",node); | |
951 } | |
952 | |
953 public static boolean isSiteAdmin(Site site,Person person) { | |
954 return person instanceof User && site.getRootNode().getOwner().equals(person); // for now | |
955 } | |
956 | |
957 public static final String CACHED = "cached"; | |
958 | |
959 public static boolean isCached(HttpServletRequest request,HttpServletResponse response) { | |
960 return response.containsHeader("Etag") || request.getAttribute(CACHED) != null; | |
961 } | |
962 | |
963 public static String termsUrl(boolean back) { | |
964 return homeContextUrl() + "/Terms.jtp"; | |
965 } | |
966 } |