/**********************************************************************************
* $URL: $
* $Id: $
**********************************************************************************
*
* Copyright (c) 2005, 2006, 2007, 2008, 2009 The Sakai Foundation
*
* Licensed under the Educational Community License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.opensource.org/licenses/ECL-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
**********************************************************************************/
package org.sakaiproject.lessonbuildertool.service;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.net.URLEncoder;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.Locale;
import java.util.regex.Pattern;
import java.net.URL;
import java.net.URLConnection;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.sakaiproject.authz.cover.AuthzGroupService;
import org.sakaiproject.authz.cover.SecurityService;
import org.sakaiproject.component.cover.ServerConfigurationService;
import org.sakaiproject.site.api.Site;
import org.sakaiproject.site.api.SitePage;
import org.sakaiproject.site.api.ToolConfiguration;
import org.sakaiproject.site.cover.SiteService;
import org.sakaiproject.tool.api.Placement;
import org.sakaiproject.tool.api.Session;
import org.sakaiproject.tool.cover.SessionManager;
import org.sakaiproject.tool.cover.ToolManager;
import org.sakaiproject.user.api.User;
import org.sakaiproject.user.cover.UserDirectoryService;
import org.sakaiproject.util.Validator;
import org.sakaiproject.util.Web;
import org.springframework.context.MessageSource;
import org.sakaiproject.util.FormattedText;
import org.sakaiproject.lessonbuildertool.tool.beans.SimplePageBean;
/**
* <p>
* Ajax server
* </p>
* .../lessonbuilder-tool/ajax?op=getmessage&code=simplepage.new-page&locale=fr
* look up text messages in messages.properties. "" if can't find it
* this lets us avoid passing the text of some larger messages as text on
* every HTML page. The server doesn't have enough context to know the right
* locale for itself, but the UI should know it
* .../lessonbuilder-tool/ajax?op=getmimetype&url=http://www.rutgers.edu
* check a URL to see what MIME type it is, "" if can't find it
* though the URL should actually be urlencoded
*
* @author Charles Hedrick, Rutgers University.
* @version $Revision: $
*/
@SuppressWarnings({ "serial", "deprecation" })
public class AjaxServer extends HttpServlet
{
private static final String UTF8 = "UTF-8";
private static MessageSource messageSource;
/** Our log (commons). */
private static Log log = LogFactory.getLog(AjaxServer.class);
public static final String FILTERHTML = "lessonbuilder.filterhtml";
private static String filterHtml = ServerConfigurationService.getString(FILTERHTML);
static Class levelClass = null;
static Object[] levels = null;
static Class ftClass = null;
static Method ftMethod = null;
static Object ftInstance = setupFtStuff();
static Object setupFtStuff () {
Object ret = null;
try {
levelClass = Class.forName("org.sakaiproject.util.api.FormattedText$Level");
levels = levelClass.getEnumConstants();
ftClass = Class.forName("org.sakaiproject.util.api.FormattedText");
ftMethod = ftClass.getMethod("processFormattedText",
new Class[] { String.class, StringBuilder.class, levelClass });
ret = org.sakaiproject.component.cover.ComponentManager.get("org.sakaiproject.util.api.FormattedText");
return ret;
} catch (Exception e) {
log.error("Formatted Text with levels not available: " + e);
return null;
}
}
/**
* Access the Servlet's information display.
*
* @return servlet information.
*/
public String getServletInfo()
{
return "Lessons Ajax Server";
}
/**
* Initialize the servlet.
*
* @param config
* The servlet config.
* @throws ServletException
*/
public void init(ServletConfig config) throws ServletException
{
super.init(config);
}
/**
* Shutdown the servlet.
*/
public void destroy()
{
log.info("destroy()");
super.destroy();
}
/**
* Respond to Get requests:
* display main content by redirecting to it and adding
* user= euid= site= role= serverurl= time= sign=
* for privileged users, add a bar at the top with a link to
* the setup screen
* ?Setup generates the setup screen
*
* @param req
* The servlet request.
* @param res
* The servlet response.
* @throws ServletException.
* @throws IOException.
*/
public String getMessage(String code, String localeName) {
Locale locale = null;
if (localeName != null && localeName.length() > 0) {
String langLoc[] = localeName.split("_");
if (langLoc.length >= 2) {
if ("en".equals(langLoc[0]) && "ZA".equals(langLoc[1])) {
locale = new Locale("en", "GB");
} else {
locale = new Locale(langLoc[0], langLoc[1]);
}
} else {
locale = new Locale(langLoc[0]);
}
}
if (locale == null)
locale = Locale.getDefault();
String value = "";
try {
value = messageSource.getMessage(code, new String[0], locale);
} catch (Exception e) {};
return value;
}
// in order to match the logic in ShowPageProducer
// * youtube has to return something other than html, in this case application/youtube,
// * image types have to return something with image, using the same test as SimplePageBean.isimage
// * html has to return an html type even based on extension, matching test in ShowPageProducer
public String getMimeType(String url) {
Session s = SessionManager.getCurrentSession();
if (s == null || s.getUserId() == null) {
// return "";
}
if (SimplePageBean.getYoutubeKeyFromUrl(url) != null)
return "application/youtube";
String mimeType = "";
URLConnection conn = null;
try {
conn = new URL(new URL(ServerConfigurationService.getServerUrl()),url).openConnection();
conn.setConnectTimeout(10000);
conn.setReadTimeout(10000);
conn.connect();
String t = conn.getContentType();
if (t != null && !t.equals("")) {
int i = t.indexOf(";");
if (i >= 0)
t = t.substring(0, i);
t = t.trim();
mimeType = t;
}
} catch (Exception e) {
} finally {
if (conn != null) {
try {
conn.getInputStream().close();
} catch (Exception e) {
// log.error("getTypeOfUrl unable to close " + e);
}
}
}
if (mimeType == null || mimeType.equals("")) {
String name = url;
// starts after last /
int i = name.lastIndexOf("/");
if (i >= 0)
name = name.substring(i+1);
String extension = null;
i = name.lastIndexOf(".");
if (i > 0)
extension = name.substring(i+1);
if (extension == null)
return "";
if (SimplePageBean.imageTypes.contains(extension)) {
return "image/unknown";
} if (extension.equals("html") || extension.equals("htm")) {
return "text/html";
} else if (extension.equals("xhtml") || extension.equals("xht")) {
return "application/xhtml+xml";
} else {
return "";
}
}
return mimeType;
}
// run antisamy or whatever on a string
// WARNING: keep in sync with submit in SimplePageBean.
public static String filterHtml(String contents) {
StringBuilder error = new StringBuilder();
final Integer FILTER_DEFAULT=0;
final Integer FILTER_HIGH=1;
final Integer FILTER_LOW=2;
final Integer FILTER_NONE=3;
// Sakai currently defaults to high. Unfortunately many
// embeds won't work with that. Might want to default this
// to low, or add another config parameter, but currently
// using same code as for text blocks.
String html = contents;
// figure out how to filter
Integer filter = FILTER_DEFAULT;
// simplepagebean checks filterHtml property of tool. We can't really do that.
String filterSpec = filterHtml;
System.out.println("filterspec " + filterSpec);
if (filterSpec == null) // should never be null. unspeciifed should give ""
filter = FILTER_DEFAULT;
// old specifications
else if (filterSpec.equalsIgnoreCase("true"))
filter = FILTER_HIGH; // old value of true produced the same result as missing
else if (filterSpec.equalsIgnoreCase("false"))
filter = FILTER_NONE;
// new ones
else if (filterSpec.equalsIgnoreCase("default"))
filter = FILTER_DEFAULT;
else if (filterSpec.equalsIgnoreCase("high"))
filter = FILTER_HIGH;
else if (filterSpec.equalsIgnoreCase("low"))
filter = FILTER_LOW;
else if (filterSpec.equalsIgnoreCase("none"))
filter = FILTER_NONE;
// unspecified
else
filter = FILTER_DEFAULT;
if (filter.equals(FILTER_NONE)) {
html = FormattedText.processHtmlDocument(contents, error);
} else if (filter.equals(FILTER_DEFAULT)) {
html = FormattedText.processFormattedText(contents, error);
} else if (ftInstance != null) {
try {
// now filter is set. Implement it. Depends upon whether we have the anti-samy code
Object level = null;
if (filter.equals(FILTER_HIGH))
level = levels[1];
else
level = levels[2];
html = (String)ftMethod.invoke(ftInstance, new Object[] { contents, error, level });
} catch (Exception e) {
// this should never happen. If it does, emulate what the anti-samy
// code does if antisamy is disabled. It always filters
html = FormattedText.processFormattedText(contents, error);
}
} else {
// don't have antisamy. For LOW, use old instructor behavior, since
// LOW is the default. For high, it makes sense to filter
if (filter.equals(FILTER_HIGH))
html = FormattedText.processFormattedText(contents, error);
else
html = FormattedText.processHtmlDocument(contents, error);
}
return html;
}
@SuppressWarnings("unchecked")
protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException
{
// get the Tool
Placement placement = ToolManager.getCurrentPlacement();
Properties config = null;
String placementId = "none";
if (placement != null) {
config = placement.getConfig();
placementId = placement.getId();
}
res.setContentType("text/html; charset=utf-8");
PrintWriter out = res.getWriter();
String op = req.getParameter("op");
if (op.equals("getmessage")) {
String code = req.getParameter("code");
String locale = req.getParameter("locale");
out.println(getMessage(code, locale));
} else if (op.equals("getmimetype")) {
String url = req.getParameter("url");
out.println(getMimeType(url));
} else if (op.equals("filterhtml")) {
String html = req.getParameter("html");
out.print(filterHtml(html));
}
}
public void setMessageSource(MessageSource s) {
messageSource = s;
}
}