001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. The ASF licenses this file to You
004: * under the Apache License, Version 2.0 (the "License"); you may not
005: * use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License. For additional information regarding
015: * copyright in this work, please see the NOTICE file in the top level
016: * directory of this distribution.
017: */
018:
019: package org.apache.roller.ui.rendering.model;
020:
021: import java.io.UnsupportedEncodingException;
022: import java.net.URLDecoder;
023: import java.net.URLEncoder;
024: import java.text.SimpleDateFormat;
025: import java.util.Date;
026: import java.util.Map;
027: import java.util.TimeZone;
028: import java.util.regex.Matcher;
029: import java.util.regex.Pattern;
030: import javax.servlet.http.HttpServletRequest;
031: import org.apache.commons.lang.StringEscapeUtils;
032: import org.apache.commons.lang.StringUtils;
033: import org.apache.commons.logging.Log;
034: import org.apache.commons.logging.LogFactory;
035: import org.apache.roller.RollerException;
036: import org.apache.roller.pojos.wrapper.WeblogEntryDataWrapper;
037: import org.apache.roller.pojos.wrapper.WebsiteDataWrapper;
038: import org.apache.roller.ui.core.RollerSession;
039: import org.apache.roller.ui.rendering.util.WeblogRequest;
040: import org.apache.roller.util.DateUtil;
041: import org.apache.roller.util.RegexUtil;
042: import org.apache.roller.util.Utilities;
043:
044: /**
045: * Model which provides access to a set of general utilities.
046: */
047: public class UtilitiesModel implements Model {
048:
049: private static Log log = LogFactory.getLog(UtilitiesModel.class);
050:
051: private static Pattern mLinkPattern = Pattern.compile(
052: "<a href=.*?>", Pattern.CASE_INSENSITIVE);
053: private static final Pattern OPENING_B_TAG_PATTERN = Pattern
054: .compile("<b>", Pattern.CASE_INSENSITIVE);
055: private static final Pattern CLOSING_B_TAG_PATTERN = Pattern
056: .compile("</b>", Pattern.CASE_INSENSITIVE);
057: private static final Pattern OPENING_I_TAG_PATTERN = Pattern
058: .compile("<i>", Pattern.CASE_INSENSITIVE);
059: private static final Pattern CLOSING_I_TAG_PATTERN = Pattern
060: .compile("</i>", Pattern.CASE_INSENSITIVE);
061: private static final Pattern OPENING_BLOCKQUOTE_TAG_PATTERN = Pattern
062: .compile("<blockquote>", Pattern.CASE_INSENSITIVE);
063: private static final Pattern CLOSING_BLOCKQUOTE_TAG_PATTERN = Pattern
064: .compile("</blockquote>", Pattern.CASE_INSENSITIVE);
065: private static final Pattern BR_TAG_PATTERN = Pattern.compile(
066: "<br */*>", Pattern.CASE_INSENSITIVE);
067: private static final Pattern OPENING_P_TAG_PATTERN = Pattern
068: .compile("<p>", Pattern.CASE_INSENSITIVE);
069: private static final Pattern CLOSING_P_TAG_PATTERN = Pattern
070: .compile("</p>", Pattern.CASE_INSENSITIVE);
071: private static final Pattern OPENING_PRE_TAG_PATTERN = Pattern
072: .compile("<pre>", Pattern.CASE_INSENSITIVE);
073: private static final Pattern CLOSING_PRE_TAG_PATTERN = Pattern
074: .compile("</pre>", Pattern.CASE_INSENSITIVE);
075: private static final Pattern OPENING_UL_TAG_PATTERN = Pattern
076: .compile("<ul>", Pattern.CASE_INSENSITIVE);
077: private static final Pattern CLOSING_UL_TAG_PATTERN = Pattern
078: .compile("</ul>", Pattern.CASE_INSENSITIVE);
079: private static final Pattern OPENING_OL_TAG_PATTERN = Pattern
080: .compile("<ol>", Pattern.CASE_INSENSITIVE);
081: private static final Pattern CLOSING_OL_TAG_PATTERN = Pattern
082: .compile("</ol>", Pattern.CASE_INSENSITIVE);
083: private static final Pattern OPENING_LI_TAG_PATTERN = Pattern
084: .compile("<li>", Pattern.CASE_INSENSITIVE);
085: private static final Pattern CLOSING_LI_TAG_PATTERN = Pattern
086: .compile("</li>", Pattern.CASE_INSENSITIVE);
087: private static final Pattern CLOSING_A_TAG_PATTERN = Pattern
088: .compile("</a>", Pattern.CASE_INSENSITIVE);
089: private static final Pattern OPENING_A_TAG_PATTERN = Pattern
090: .compile("<a href=.*?>", Pattern.CASE_INSENSITIVE);
091: private static final Pattern QUOTE_PATTERN = Pattern.compile(
092: """, Pattern.CASE_INSENSITIVE);
093:
094: private HttpServletRequest request = null;
095: private TimeZone tz = null;
096:
097: /** Template context name to be used for model */
098: public String getModelName() {
099: return "utils";
100: }
101:
102: /** Init page model based on request */
103: public void init(Map initData) throws RollerException {
104:
105: // extract request object
106: this .request = (HttpServletRequest) initData.get("request");
107:
108: // extract timezone if available
109: WeblogRequest weblogRequest = (WeblogRequest) initData
110: .get("weblogRequest");
111: if (weblogRequest != null && weblogRequest.getWeblog() != null) {
112: tz = weblogRequest.getWeblog().getTimeZoneInstance();
113: }
114: }
115:
116: //---------------------------------------------------- Authentication utils
117:
118: public boolean isUserAuthorizedToAuthor(WebsiteDataWrapper weblog) {
119: try {
120: RollerSession rses = RollerSession
121: .getRollerSession(request);
122: if (rses != null && rses.getAuthenticatedUser() != null) {
123: return rses.isUserAuthorizedToAuthor(weblog.getPojo());
124: }
125: } catch (Exception e) {
126: log.warn("ERROR: checking user authorization", e);
127: }
128: return false;
129: }
130:
131: public boolean isUserAuthorizedToAdmin(WebsiteDataWrapper weblog) {
132: try {
133: RollerSession rses = RollerSession
134: .getRollerSession(request);
135: if (rses != null && rses.getAuthenticatedUser() != null) {
136: return rses.isUserAuthorizedToAdmin(weblog.getPojo());
137: }
138: } catch (Exception e) {
139: log.warn("ERROR: checking user authorization", e);
140: }
141: return false;
142: }
143:
144: public boolean isUserAuthenticated() {
145: return (request.getUserPrincipal() != null);
146: }
147:
148: //-------------------------------------------------------------- Date utils
149: /**
150: * Return date for current time.
151: */
152: public static Date getNow() {
153: return new Date();
154: }
155:
156: /**
157: * Format date using SimpleDateFormat format string.
158: */
159: public String formatDate(Date d, String fmt) {
160: if (d == null || fmt == null)
161: return fmt;
162:
163: SimpleDateFormat format = new SimpleDateFormat(fmt);
164: if (tz != null) {
165: format.setTimeZone(tz);
166: }
167: return format.format(d);
168: }
169:
170: /**
171: * Format date using SimpleDateFormat format string.
172: */
173: public static String formatDate(Date d, String fmt,
174: TimeZone tzOverride) {
175: if (d == null || fmt == null)
176: return fmt;
177:
178: SimpleDateFormat format = new SimpleDateFormat(fmt);
179: format.setTimeZone(tzOverride);
180: return format.format(d);
181: }
182:
183: /**
184: * Format date in ISO-8601 format.
185: */
186: public static String formatIso8601Date(Date d) {
187: return DateUtil.formatIso8601(d);
188: }
189:
190: /**
191: * Format date in ISO-8601 format.
192: */
193: public static String formatIso8601Day(Date d) {
194: return DateUtil.formatIso8601Day(d);
195: }
196:
197: /**
198: * Return a date in RFC-822 format.
199: */
200: public static String formatRfc822Date(Date date) {
201: return DateUtil.formatRfc822(date);
202: }
203:
204: /**
205: * Return a date in RFC-822 format.
206: */
207: public static String format8charsDate(Date date) {
208: return DateUtil.format8chars(date);
209: }
210:
211: //------------------------------------------------------------ String utils
212:
213: public static boolean isEmpty(String str) {
214: if (str == null)
215: return true;
216: return "".equals(str.trim());
217: }
218:
219: public static boolean isNotEmpty(String str) {
220: return !isEmpty(str);
221: }
222:
223: public static String[] split(String str1, String str2) {
224: return StringUtils.split(str1, str2);
225: }
226:
227: public static boolean equals(String str1, String str2) {
228: return StringUtils.equals(str1, str2);
229: }
230:
231: public static boolean isAlphanumeric(String str) {
232: return StringUtils.isAlphanumeric(str);
233: }
234:
235: public static String[] stripAll(String[] strs) {
236: return StringUtils.stripAll(strs);
237: }
238:
239: public static String left(String str, int length) {
240: return StringUtils.left(str, length);
241: }
242:
243: public static String escapeHTML(String str) {
244: return StringEscapeUtils.escapeHtml(str);
245: }
246:
247: public static String unescapeHTML(String str) {
248: return StringEscapeUtils.unescapeHtml(str);
249: }
250:
251: public static String escapeXML(String str) {
252: return StringEscapeUtils.escapeXml(str);
253: }
254:
255: public static String unescapeXML(String str) {
256: return StringEscapeUtils.unescapeXml(str);
257: }
258:
259: public static String replace(String src, String target, String rWith) {
260: return StringUtils.replace(src, target, rWith);
261: }
262:
263: public static String replace(String src, String target,
264: String rWith, int maxCount) {
265: return StringUtils.replace(src, target, rWith, maxCount);
266: }
267:
268: private static String replace(String string, Pattern pattern,
269: String replacement) {
270: Matcher m = pattern.matcher(string);
271: return m.replaceAll(replacement);
272: }
273:
274: /**
275: * Remove occurences of html, defined as any text
276: * between the characters "<" and ">". Replace
277: * any HTML tags with a space.
278: */
279: public static String removeHTML(String str) {
280: return removeHTML(str, true);
281: }
282:
283: /**
284: * Remove occurences of html, defined as any text
285: * between the characters "<" and ">".
286: * Optionally replace HTML tags with a space.
287: */
288: public static String removeHTML(String str, boolean addSpace) {
289: return Utilities.removeHTML(str, addSpace);
290: }
291:
292: /**
293: * Autoformat.
294: */
295: public static String autoformat(String s) {
296: String ret = StringUtils.replace(s, "\n", "<br />");
297: return ret;
298: }
299:
300: /**
301: * Strips HTML and truncates.
302: */
303: public static String truncate(String str, int lower, int upper,
304: String appendToEnd) {
305: // strip markup from the string
306: String str2 = removeHTML(str, false);
307:
308: // quickly adjust the upper if it is set lower than 'lower'
309: if (upper < lower) {
310: upper = lower;
311: }
312:
313: // now determine if the string fits within the upper limit
314: // if it does, go straight to return, do not pass 'go' and collect $200
315: if (str2.length() > upper) {
316: // the magic location int
317: int loc;
318:
319: // first we determine where the next space appears after lower
320: loc = str2.lastIndexOf(' ', upper);
321:
322: // now we'll see if the location is greater than the lower limit
323: if (loc >= lower) {
324: // yes it was, so we'll cut it off here
325: str2 = str2.substring(0, loc);
326: } else {
327: // no it wasnt, so we'll cut it off at the upper limit
328: str2 = str2.substring(0, upper);
329: loc = upper;
330: }
331:
332: // the string was truncated, so we append the appendToEnd String
333: str2 = str2 + appendToEnd;
334: }
335:
336: return str2;
337: }
338:
339: public static String truncateNicely(String str, int lower,
340: int upper, String appendToEnd) {
341: return Utilities.truncateNicely(str, lower, upper, appendToEnd);
342: }
343:
344: public static String truncateText(String str, int lower, int upper,
345: String appendToEnd) {
346: // strip markup from the string
347: String str2 = removeHTML(str, false);
348: boolean diff = (str2.length() < str.length());
349:
350: // quickly adjust the upper if it is set lower than 'lower'
351: if (upper < lower) {
352: upper = lower;
353: }
354:
355: // now determine if the string fits within the upper limit
356: // if it does, go straight to return, do not pass 'go' and collect $200
357: if (str2.length() > upper) {
358: // the magic location int
359: int loc;
360:
361: // first we determine where the next space appears after lower
362: loc = str2.lastIndexOf(' ', upper);
363:
364: // now we'll see if the location is greater than the lower limit
365: if (loc >= lower) {
366: // yes it was, so we'll cut it off here
367: str2 = str2.substring(0, loc);
368: } else {
369: // no it wasnt, so we'll cut it off at the upper limit
370: str2 = str2.substring(0, upper);
371: loc = upper;
372: }
373: // the string was truncated, so we append the appendToEnd String
374: str = str2 + appendToEnd;
375: }
376: return str;
377: }
378:
379: public static String hexEncode(String str) {
380: if (StringUtils.isEmpty(str))
381: return str;
382:
383: return RegexUtil.encode(str);
384: }
385:
386: public static String encodeEmail(String str) {
387: return str != null ? RegexUtil.encodeEmail(str) : null;
388: }
389:
390: /**
391: * URL encoding.
392: * @param s a string to be URL-encoded
393: * @return URL encoding of s using character encoding UTF-8; null if s is null.
394: */
395: public static final String encode(String s) {
396: try {
397: if (s != null)
398: return URLEncoder.encode(s, "UTF-8");
399: else
400: return s;
401: } catch (UnsupportedEncodingException e) {
402: // Java Spec requires UTF-8 be in all Java environments, so this should not happen
403: return s;
404: }
405: }
406:
407: /**
408: * URL decoding.
409: * @param s a URL-encoded string to be URL-decoded
410: * @return URL decoded value of s using character encoding UTF-8; null if s is null.
411: */
412: public static final String decode(String s) {
413: try {
414: if (s != null)
415: return URLDecoder.decode(s, "UTF-8");
416: else
417: return s;
418: } catch (UnsupportedEncodingException e) {
419: // Java Spec requires UTF-8 be in all Java environments, so this should not happen
420: return s;
421: }
422: }
423:
424: /**
425: * Code (stolen from Pebble) to add rel="nofollow" string to all links in HTML.
426: */
427: public static String addNofollow(String html) {
428: if (html == null || html.length() == 0) {
429: return html;
430: }
431: Matcher m = mLinkPattern.matcher(html);
432: StringBuffer buf = new StringBuffer();
433: while (m.find()) {
434: int start = m.start();
435: int end = m.end();
436: String link = html.substring(start, end);
437: buf.append(html.substring(0, start));
438: if (link.indexOf("rel=\"nofollow\"") == -1) {
439: buf.append(link.substring(0, link.length() - 1)
440: + " rel=\"nofollow\">");
441: } else {
442: buf.append(link);
443: }
444: html = html.substring(end, html.length());
445: m = mLinkPattern.matcher(html);
446: }
447: buf.append(html);
448: return buf.toString();
449: }
450:
451: /**
452: * Transforms the given String into a subset of HTML displayable on a web
453: * page. The subset includes <b>, <i>, <p>, <br>,
454: * <pre> and <a href> (and their corresponding end tags).
455: *
456: * @param s the String to transform
457: * @return the transformed String
458: */
459: public static String transformToHTMLSubset(String s) {
460:
461: if (s == null) {
462: return null;
463: }
464:
465: s = replace(s, OPENING_B_TAG_PATTERN, "<b>");
466: s = replace(s, CLOSING_B_TAG_PATTERN, "</b>");
467: s = replace(s, OPENING_I_TAG_PATTERN, "<i>");
468: s = replace(s, CLOSING_I_TAG_PATTERN, "</i>");
469: s = replace(s, OPENING_BLOCKQUOTE_TAG_PATTERN, "<blockquote>");
470: s = replace(s, CLOSING_BLOCKQUOTE_TAG_PATTERN, "</blockquote>");
471: s = replace(s, BR_TAG_PATTERN, "<br />");
472: s = replace(s, OPENING_P_TAG_PATTERN, "<p>");
473: s = replace(s, CLOSING_P_TAG_PATTERN, "</p>");
474: s = replace(s, OPENING_PRE_TAG_PATTERN, "<pre>");
475: s = replace(s, CLOSING_PRE_TAG_PATTERN, "</pre>");
476: s = replace(s, OPENING_UL_TAG_PATTERN, "<ul>");
477: s = replace(s, CLOSING_UL_TAG_PATTERN, "</ul>");
478: s = replace(s, OPENING_OL_TAG_PATTERN, "<ol>");
479: s = replace(s, CLOSING_OL_TAG_PATTERN, "</ol>");
480: s = replace(s, OPENING_LI_TAG_PATTERN, "<li>");
481: s = replace(s, CLOSING_LI_TAG_PATTERN, "</li>");
482: s = replace(s, QUOTE_PATTERN, "\"");
483:
484: // HTTP links
485: s = replace(s, CLOSING_A_TAG_PATTERN, "</a>");
486: Matcher m = OPENING_A_TAG_PATTERN.matcher(s);
487: while (m.find()) {
488: int start = m.start();
489: int end = m.end();
490: String link = s.substring(start, end);
491: link = "<" + link.substring(4, link.length() - 4) + ">";
492: s = s.substring(0, start) + link
493: + s.substring(end, s.length());
494: m = OPENING_A_TAG_PATTERN.matcher(s);
495: }
496:
497: // escaped angle brackets
498: s = s.replaceAll("&lt;", "<");
499: s = s.replaceAll("&gt;", ">");
500: s = s.replaceAll("&#", "&#");
501:
502: return s;
503: }
504:
505: /**
506: * Convert a byte array into a Base64 string (as used in mime formats)
507: */
508: public static String toBase64(byte[] aValue) {
509:
510: final String m_strBase64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
511:
512: int byte1;
513: int byte2;
514: int byte3;
515: int iByteLen = aValue.length;
516: StringBuffer tt = new StringBuffer();
517:
518: for (int i = 0; i < iByteLen; i += 3) {
519: boolean bByte2 = (i + 1) < iByteLen;
520: boolean bByte3 = (i + 2) < iByteLen;
521: byte1 = aValue[i] & 0xFF;
522: byte2 = (bByte2) ? (aValue[i + 1] & 0xFF) : 0;
523: byte3 = (bByte3) ? (aValue[i + 2] & 0xFF) : 0;
524:
525: tt.append(m_strBase64Chars.charAt(byte1 / 4));
526: tt.append(m_strBase64Chars.charAt((byte2 / 16)
527: + ((byte1 & 0x3) * 16)));
528: tt.append(((bByte2) ? m_strBase64Chars.charAt((byte3 / 64)
529: + ((byte2 & 0xF) * 4)) : '='));
530: tt.append(((bByte3) ? m_strBase64Chars.charAt(byte3 & 0x3F)
531: : '='));
532: }
533:
534: return tt.toString();
535: }
536:
537: }
|