package in.partake.view.util; import in.partake.base.DateTime; import in.partake.base.TimeUtil; import in.partake.base.Util; import in.partake.model.dto.Event; import in.partake.model.dto.EventTicket; import in.partake.model.dto.auxiliary.TicketApplicationStart; import in.partake.resource.Constants; import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import org.owasp.validator.html.AntiSamy; import org.owasp.validator.html.CleanResults; import org.owasp.validator.html.Policy; import org.owasp.validator.html.PolicyException; import org.owasp.validator.html.ScanException; /** * View に関する定型処理をまとめたもの。 * * @author shinyak */ public final class Helper { private static Policy antiSamyPolicy; static { try { InputStream is = new BufferedInputStream(Util.createInputSteram(Constants.ANTISAMY_POLICY_FILE_RELATIVE_LOCATION)); antiSamyPolicy = Policy.getInstance(is); } catch (IOException e) { e.printStackTrace(); } catch (PolicyException e) { e.printStackTrace(); } } /** * escapeHTML の短縮関数名 * @param s * @return */ public static String h(String s) { return escapeHTML(s); } /** * HTML で利用されるタグなどを escape する。 * @param s * @return */ public static String escapeHTML(String s) { if (s == null) { return ""; } StringBuilder builder = new StringBuilder(); for (int i = 0; i < s.length(); i += Character.charCount(s.codePointAt(i))) { switch (s.codePointAt(i)) { case '&': builder.append("&"); break; case '<': builder.append("<"); break; case '>': builder.append(">"); break; case '"': builder.append("""); break; case '\'': builder.append("'"); break; default: if (Character.isIdentifierIgnorable(s.codePointAt(i))) { // ignore. } else { for (int j = 0; j < Character.charCount(s.codePointAt(i)); ++j) { builder.append(s.charAt(i + j)); } } } } return builder.toString(); } /** * HTMLエスケープを施すが、"&gt;"と"&lt;"はそのままにする。 * これはTwitterが'>'と'<'だけエスケープするという問題に対応するためのものである。 * * @see http://code.google.com/p/twitter-api/issues/detail?id=845 * @see http://www.mail-archive.com/twitter-development-talk@googlegroups.com/msg09528.html * @see http://code.google.com/p/partakein/issues/detail?id=148 * @param s エスケープする文字列 * @return エスケープされた文字列 */ public static String escapeTwitterResponse(String s) { return escapeHTML(s).replaceAll("&gt;", ">").replaceAll("&lt;", "<"); } /** * テキストを整形する。 * @param dirtyText * @return */ public static String cleanupText(String dirtyText) { String s = dirtyText; if (s == null) { return ""; } StringBuilder builder = new StringBuilder(); for (int i = 0; i < s.length(); i += Character.charCount(s.codePointAt(i))) { switch (s.codePointAt(i)) { case '&': builder.append("&"); break; case '<': builder.append("<"); break; case '>': builder.append(">"); break; case '"': builder.append("""); break; case '\'': builder.append("'"); break; case '\n': builder.append("<br />"); break; default: if (Character.isIdentifierIgnorable(s.codePointAt(i))) { // ignore. } else { for (int j = 0; j < Character.charCount(s.codePointAt(i)); ++j) { builder.append(s.charAt(i + j)); } } } } return builder.toString(); } /** * HTML の script など、危険と思われる要素を取り除く * @param dirtyHTML * @return */ public static String cleanupHTML(String dirtyHTML) { // Hmm... if (antiSamyPolicy == null) { return ""; } try { AntiSamy antiSamy = new AntiSamy(); CleanResults cr = antiSamy.scan(dirtyHTML, antiSamyPolicy); return cr.getCleanHTML(); } catch (PolicyException e) { e.printStackTrace(); return ""; } catch (ScanException e) { e.printStackTrace(); return ""; } } private static final String[] DAYS = new String[] { "日", "月", "火", "水", "木", "金", "土" }; /** 日時を読みやすい形で表示します */ public static String shortReadableData(DateTime dt) { GregorianCalendar cal = new GregorianCalendar(); cal.setTime(dt.toDate()); int year = cal.get(Calendar.YEAR); int month = cal.get(Calendar.MONTH) + 1; int date = cal.get(Calendar.DAY_OF_MONTH); int hour = cal.get(Calendar.HOUR_OF_DAY); int min = cal.get(Calendar.MINUTE); return String.format("%04d/%d/%d %02d:%02d", year, month, date, hour, min); } /** 日時を読みやすい形で表示します */ public static String readableDate(Date d) { GregorianCalendar cal = new GregorianCalendar(); cal.setTime(d); cal.setTimeZone(TimeUtil.JST_TIMEZONE); int year = cal.get(Calendar.YEAR); int month = cal.get(Calendar.MONTH) + 1; int date = cal.get(Calendar.DAY_OF_MONTH); int day = cal.get(Calendar.DAY_OF_WEEK); int hour = cal.get(Calendar.HOUR_OF_DAY); int min = cal.get(Calendar.MINUTE); String dayStr; switch (day) { case Calendar.SUNDAY: dayStr = DAYS[0]; break; case Calendar.MONDAY: dayStr = DAYS[1]; break; case Calendar.TUESDAY: dayStr = DAYS[2]; break; case Calendar.WEDNESDAY: dayStr = DAYS[3]; break; case Calendar.THURSDAY: dayStr = DAYS[4]; break; case Calendar.FRIDAY: dayStr = DAYS[5]; break; case Calendar.SATURDAY: dayStr = DAYS[6]; break; default: dayStr = "-"; } return String.format("%04d年%d月%d日(%s) %02d:%02d", year, month, date, dayStr, hour, min); } public static String readableDate(DateTime d) { return readableDate(new Date(d.getTime())); } public static String readableApplicationDuration(EventTicket ticket, Event event) { if (TicketApplicationStart.ANYTIME.equals(ticket.getApplicationStart())) return readableDate(ticket.acceptsTill(event)) + "まで"; return Helper.readableDuration(ticket.acceptsFrom(event), ticket.acceptsTill(event)); } public static String readableDuration(DateTime beginDate, DateTime endDate) { Date begin = beginDate != null ? new Date(beginDate.getTime()) : null; Date end = endDate != null ? new Date(endDate.getTime()) : null; return readableDuration(begin, end); } @Deprecated public static String readableDuration(Date beginDate, Date endDate) { if (endDate == null) { return readableDate(beginDate); } if (areSameDay(beginDate, endDate)) { GregorianCalendar cal = new GregorianCalendar(); cal.setTime(endDate); cal.setTimeZone(TimeUtil.JST_TIMEZONE); return readableDate(beginDate) + String.format(" - %02d:%02d", cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE)); } else { return readableDate(beginDate) + " - " + readableDate(endDate); } } /** * @author skypencil (@eller86) */ private static boolean areSameDay(Date beginDate, Date endDate) { assert beginDate != null; assert endDate != null; Calendar begin = Calendar.getInstance(); begin.setTime(beginDate); Calendar end = Calendar.getInstance(); end.setTime(endDate); return begin.get(Calendar.YEAR) == end.get(Calendar.YEAR) && begin.get(Calendar.MONTH) == end.get(Calendar.MONTH) && begin.get(Calendar.DAY_OF_MONTH) == end.get(Calendar.DAY_OF_MONTH); } public static String readableCapacity(int enrolled, int capacity) { if (capacity == 0) { return String.format("%d 人 / -", enrolled); } else { return String.format("%d 人 / %d 人", enrolled, capacity); } } public static String readableReminder(Date date) { if (date == null) { return "未送付"; } return String.format("送付済 (%s)", readableDate(date)); } // cache css version to invalidate user cache // see http://code.google.com/p/partakein/issues/detail?id=45 private static String cachedCssVersion; public static String getCssVersion() { return cachedCssVersion == null ? "unknown" : cachedCssVersion; } public static void setCssVersion(String version) { cachedCssVersion = version; } // public static String javascript(String... relativePaths) { // // TODO: すべてつなげて minify したものを作成し、それを常によむようにすると高速化されるので、そうしたい。 // StringBuilder builder = new StringBuilder(); // // String contextPath = ServletActionContext.getRequest().getContextPath(); // for (String relativePath : relativePaths) { // String filePath = ServletActionContext.getServletContext().getRealPath(relativePath); // File file = new File(filePath); // if (!file.exists() || !file.isFile()) { // LOGGER.warn(String.format("specified file(%s) doesn't exist. check argument(%s) and stored files.", filePath, relativePath)); // continue; // } // long time = file.lastModified(); // // String absolutePath = String.format("%s%s", contextPath, relativePath); // builder.append(String.format("<script type=\"text/javascript\" src=\"%s?%d\"></script>\n", h(absolutePath), time)); // } // // return builder.toString(); // } }