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 }