/*
This file is part of OpenMyEWB.
OpenMyEWB is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OpenMyEWB is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OpenMyEWB. If not, see <http://www.gnu.org/licenses/>.
OpenMyEWB is Copyright 2005-2009 Nicolas Kruchten (nicolas@kruchten.com), Francis Kung, Engineers Without Borders Canada, Michael Trauttmansdorff, Jon Fishbein, David Kadish
*/
package ca.myewb.frame;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.criterion.CriteriaSpecification;
import org.hibernate.criterion.Restrictions;
import org.radeox.api.engine.RenderEngine;
import org.radeox.api.engine.context.InitialRenderContext;
import org.radeox.api.engine.context.RenderContext;
import org.radeox.engine.BaseRenderEngine;
import org.radeox.engine.context.BaseInitialRenderContext;
import org.radeox.engine.context.BaseRenderContext;
import ca.myewb.model.DailyStatsModel;
import ca.myewb.model.GroupModel;
public class Helpers
{
private static String localRoot;
private static String appPrefix;
private static String defaultURL;
private static String domain;
private static boolean devMode = false;
private static DecimalFormat decimalFormat = new DecimalFormat("$###,###.00");
private static DecimalFormat dashboardFormat = new DecimalFormat("###,###,###,##0.##");
private static SimpleDateFormat dateFormatter = new SimpleDateFormat("EEE, MMM d, yyyy");
private static SimpleDateFormat timeFormatter = new SimpleDateFormat("h:mm a");
private static SimpleDateFormat postDateFormatter = new SimpleDateFormat("MMM d, yyyy");
private static SimpleDateFormat postDateFormatterNoYear = new SimpleDateFormat("EEE, MMM d");
private static SimpleDateFormat postTimeFormatter = new SimpleDateFormat("h:mm a z");
private static SimpleDateFormat sqlFormatter = new SimpleDateFormat("yyyy-MM-dd");
private static SimpleDateFormat rfcFormatter = new SimpleDateFormat("EEE', 'dd' 'MMM' 'yyyy' 'HH:mm:ss' 'Z");
private static SimpleDateFormat iCalDateTimeFormatter = new SimpleDateFormat("yyyyMMdd'T'HHmmss");
private static ConcurrentHashMap<String, Integer> adminGroupIDs = new ConcurrentHashMap<String, Integer>(30);
private static String frontPageCache = null;
private static Calendar cacheCal = null;
private static String longName;
private static String enShortName;
private static String frShortName;
private static String systemEmail;
public Helpers()
{
//empty constructor, so we can stick one of these in the Velocity context
}
public static void setLocalRoot(String localRoot)
{
Helpers.localRoot = localRoot;
}
public static String getLocalRoot()
{
return localRoot;
}
public static String getUserFilesDir()
{
return localRoot + "/userfiles/";
}
public static String getUserPicturesDir()
{
return localRoot + "/userpictures/";
}
public static String formatDollarAmount(float amount)
{
return decimalFormat.format(amount);
}
public static boolean isNull(Object a)
{
return a == null;
}
public static String formatDashboardNumber(Number amount)
{
return dashboardFormat.format(amount);
}
public static String formatRFCDate(Date date) {
return rfcFormatter.format(date);
}
public static String formatDateAndTime(Date date)
{
return Helpers.formatDate(date) + " at " + timeFormatter.format(date);
}
public static String formatAbsDate(Date date)
{
return dateFormatter.format(date);
}
public static String formatSQLDate(Date date)
{
return sqlFormatter.format(date);
}
public static String formatDate(Date date)
{
if (date == null)
{
return "never";
}
GregorianCalendar today = new GregorianCalendar();
today.setTime(new Date());
today.set(Calendar.HOUR_OF_DAY, 0);
today.set(Calendar.MINUTE, 0);
today.set(Calendar.SECOND, 1);
GregorianCalendar yesterday = (GregorianCalendar)today.clone();
yesterday.add(Calendar.DAY_OF_YEAR, -1);
yesterday.set(Calendar.HOUR_OF_DAY, 0);
yesterday.set(Calendar.MINUTE, 0);
yesterday.set(Calendar.SECOND, 1);
GregorianCalendar tomorrow = (GregorianCalendar)today.clone();
tomorrow.add(Calendar.DAY_OF_YEAR, 1);
tomorrow.set(Calendar.HOUR_OF_DAY, 0);
tomorrow.set(Calendar.MINUTE, 0);
tomorrow.set(Calendar.SECOND, 1);
GregorianCalendar dayAfterTomorrow = (GregorianCalendar)today.clone();
dayAfterTomorrow.add(Calendar.DAY_OF_YEAR, 2);
dayAfterTomorrow.set(Calendar.HOUR_OF_DAY, 0);
dayAfterTomorrow.set(Calendar.MINUTE, 0);
dayAfterTomorrow.set(Calendar.SECOND, 1);
GregorianCalendar dayOfDate = new GregorianCalendar();
dayOfDate.setTime(date);
dayOfDate.set(Calendar.HOUR_OF_DAY, 0);
dayOfDate.set(Calendar.MINUTE, 0);
dayOfDate.set(Calendar.SECOND, 2);
if (dayOfDate.before(yesterday)
|| dayOfDate.after(dayAfterTomorrow))
{
return "on " + dateFormatter.format(date);
}
else if (dayOfDate.before(today))
{
return "yesterday";
}
else if (dayOfDate.before(tomorrow))
{
return "today";
}
else
{
return "tomorrow";
}
}
public static String formatPostDateTime(Date date)
{
return Helpers.formatPostDateTime(date, true);
}
public static String formatPostDateTime(Date date, boolean showTime)
{
if (date == null)
{
return "never";
}
long dMillis = ((new Date()).getTime() - date.getTime());
if (dMillis < 0)
{
return "in the future";
}
long dMinutes = dMillis/(1000*60);
if(showTime)
{
if(dMinutes < 30)
{
return "a few minutes ago";
}
else if(dMinutes < 60)
{
return "half an hour ago";
}
else if(dMinutes < 120)
{
return "an hour ago";
}
else if(dMinutes < 24*60)
{
return (dMinutes / 60) + " hours ago";
}
}
long dDays = dMillis / (1000*60*60*24);
if (dDays < 2)
{
if(showTime)
{
return "a day ago at " + postTimeFormatter.format(date);
}
else if (dDays < 1)
{
return "in the past day";
}
else
{
return "a day ago";
}
}
if (dDays < 7) // less than 1 week
{
if(showTime)
{
return dDays + " days ago at " + postTimeFormatter.format(date);
}
else
{
return dDays + " days ago";
}
}
else if (dDays < 14) // less than 2 weeks
{
return "a week ago, on " + postDateFormatterNoYear.format(date);
}
else if (dDays < 28) // less than 4 weeks
{
return (dDays/7) + " weeks ago, on " + postDateFormatterNoYear.format(date);
}
else if (dDays < 60) // less than 2 months
{
return "a month ago, on " + postDateFormatterNoYear.format(date);
}
else if (dDays < 360) // less than a year
{
return (dDays/30) + " months ago, on " + postDateFormatter.format(date);
}
else if (dDays < 730) // less than 2 years
{
return " a year ago, on " + postDateFormatter.format(date);
}
else
{
return (dDays/365) + " years ago, on " + postDateFormatter.format(date);
}
}
public static String formatICalDateTime(Date date){
return iCalDateTimeFormatter.format(date);
}
public static String sanitizeFileName(String name){
return name.replaceAll("/../", "/");
}
public static GroupModel getGroup(String name) throws HibernateException
{
//we cache the admin-group id's by name so that
//we can exploit the 2nd-level cache on later retrievals
//these id's are different in existing and fresh installs
//so they can't be hardcoded
Integer id = adminGroupIDs.get(name);
if(id != null)
{
return (GroupModel)HibernateUtil.currentSession().load(GroupModel.class, id);
}
else
{
//group id unknown, grab group from db and cache the id
GroupModel theGroup = (GroupModel)HibernateUtil.currentSession()
.createQuery("FROM GroupModel g where g.admin=true and g.shortname=?")
.setString(0, name).uniqueResult();
adminGroupIDs.put(theGroup.getShortname(), theGroup.getId());
return theGroup;
}
}
public static String getDefaultURL()
{
return defaultURL;
}
public static void setPrefixes(String appPrefix, String defaultURL, String domain)
{
Helpers.domain = domain;
Helpers.appPrefix = appPrefix;
Helpers.defaultURL = Helpers.appPrefix + defaultURL;
}
/**
* Return the uri, relative to the app base. Thus, if you have no appbase
* ie http://sytem.com/home/Home/whatever/etc, or if you have an appbase ie
* http://localhost/sytem/home/Home/whatever/etc, this will always return
* [home, Home, whatever, etc]
*/
public static String[] getURIComponents(String uri)
{
return uri.substring(appPrefix.length() + 1).split("/");
}
public static String getAppPrefix()
{
return appPrefix;
}
public static void setDevMode(boolean devMode)
{
Helpers.devMode = devMode;
}
public static boolean isDevMode()
{
return devMode;
}
public static String getDomain()
{
return domain;
}
public static DailyStatsModel currentDailyStats()
{
List l = HibernateUtil.currentSession()
.createQuery("from DailyStatsModel as ds where ds.day=?")
.setDate(0, new Date()).list();
if (l.size() == 0)
{
// If we had a log, we should record an error
// (this is safe in local testing, when the cron doesn't run,
// but we should never enter this block on the production site)
Calendar cal = Calendar.getInstance();
return DailyStatsModel.newDailyStats(cal.getTime());
}
return (DailyStatsModel)l.get(0);
}
public static String htmlSafe(String value)
{
value = value.replace("&", "&"); // v. important to keep this first
value = value.replace("<", "<");
value = value.replace(">", ">");
value = value.replace("\"", """);
return value;
}
public static String wikiFormat(String input) {
// Escape HTML characters first. Maybe this can be done via the
// Wiki formatter, but meh.. it's probably useful to have this
// as our own seperate method anyways.
input = htmlSafe(input.trim());
//collapse multiple newlines
input = input.replaceAll("\r", "");
input = input.replaceAll("(?m)\n\\s*\n", "\r");
input = input.replaceAll("([\n\r]) ", "$1 ");
input = input.replaceAll("([\n\r]) ", "$1 ");
input = input.replaceAll("([\n\r]) ", "$1 ");
input = input.replaceAll("([\n\r]) ", "$1 ");
//massage the input a little bit by re-spacing tags.
input = input.replaceAll("\\*\\*", " ** ");
input = input.replaceAll("\\^\\^", " ^^ ");
input = input.replaceAll("==", " == ");
// Set up Wiki formatter
InitialRenderContext initialContext = new BaseInitialRenderContext();
initialContext.set(RenderContext.OUTPUT_BUNDLE_NAME, "radeox_markup_custom");
initialContext.set(RenderContext.INPUT_BUNDLE_NAME, "radeox_markup_custom");
RenderEngine engineWithContext = new BaseRenderEngine(initialContext);
String renderedText = engineWithContext.render(input, new BaseRenderContext());
//instead of trying to auto-close orphaned tags, just nuke them
renderedText = renderedText.replaceAll("\\*\\*", "");
renderedText = renderedText.replaceAll("\\^\\^", "");
renderedText = renderedText.replaceAll("==", "");
//radeox munges the h in the HREF in URL replacements starting with 'http'
renderedText = renderedText.replaceAll("h", "h");
//big hack: radeox munges the w in the HREF in URL replacements starting with 'www'
//so we use that to slip in the required http://
renderedText = renderedText.replaceAll("href=\"w", "href=\"http://w");
//emails, booh-yah
Matcher matcher = Pattern.compile("\\b(([._\\-\\p{Alnum}])+@([._\\-\\p{Alnum}])+\\.([._\\-\\p{Alnum}])+)\\b")
.matcher(renderedText);
String emailFiltered = renderedText;
while(matcher.find())
{
String email = matcher.group(0);
String escapedEmail = email.replace("@", "#atsign#");
emailFiltered = emailFiltered.replaceFirst(email,
"<a href=\"mailto:" + escapedEmail + "\">" + escapedEmail + "</a>");
}
emailFiltered = emailFiltered.replaceAll("#atsign#", "@");
//various transformations above result in empty paragraphs, with or without newlines, strip'em
String cleaned = emailFiltered.replaceAll("<p class=\"postbody\"><br/>", "<p class=\"postbody\">");
cleaned = cleaned.replaceAll("<br/></p>", "</p>");
cleaned = cleaned.replaceAll("<p class=\"postbody\">\\s*</p>", "");
return cleaned;
}
public static String textFormat(String input) {
return input.replaceAll("\\^\\^", "").replaceAll("\\*\\*", "");
}
public static String iCalTextFormat(String input){
return Helpers.textFormat(input).replaceAll(",","\\\\,").replaceAll("\r","").replaceAll("\n", "\\\\n");
}
public static List<GroupModel> getNationalRepLists(boolean includeUni, boolean includePro){
Criteria crit = HibernateUtil.currentSession().createCriteria(GroupModel.class);
crit.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
ArrayList<Object> types = new ArrayList<Object>();
types.add('b');
if(includePro)
{
types.add('p');
}
if(includeUni)
{
types.add('s');
}
crit.add(Restrictions.in("nationalRepType", types));
return (new SafeHibList<GroupModel>(crit)).list();
}
public static float findElapsedTime(long stime)
{
long elapsed = System.currentTimeMillis() - stime;
return (elapsed / 1000f);
}
public static String md5(String key)
{
try
{
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(key.getBytes());
byte[] v = md.digest();
StringBuffer sb = new StringBuffer(v.length * 2);
for (int i = 0; i < v.length; i++)
{
int b = v[i] & 0xFF;
sb.append("0123456789abcdef".charAt(b >>> 4)).append("0123456789abcdef".charAt(b & 0xF));
}
return sb.toString();
}
catch(NoSuchAlgorithmException nsae)
{
return "";
}
}
public static String getLongName() {
return longName;
}
public static void setLongName(String longName) {
Helpers.longName = longName;
}
public static String getEnShortName() {
return enShortName;
}
public static void setEnShortName(String enShortName) {
Helpers.enShortName = enShortName;
}
public static String getFrShortName() {
return frShortName;
}
public static void setFrShortName(String frShortName) {
Helpers.frShortName = frShortName;
}
public static String getSystemEmail() {
return systemEmail;
}
public static void setSystemEmail(String systemEmail) {
Helpers.systemEmail = systemEmail;
}
}