/*******************************************************************************
* Signavio Core Components
* Copyright (C) 2012 Signavio GmbH
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
package com.signavio.editor.handler;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import com.signavio.platform.account.business.FsAccount;
import com.signavio.platform.annotations.HandlerConfiguration;
import com.signavio.platform.annotations.HandlerMethodActivation;
import com.signavio.platform.core.Platform;
import com.signavio.platform.exceptions.IORequestException;
import com.signavio.platform.exceptions.JSONRequestException;
import com.signavio.platform.exceptions.RequestException;
import com.signavio.platform.handler.BasisHandler;
import com.signavio.platform.security.business.FsAccessToken;
import com.signavio.platform.security.business.FsSecureBusinessObject;
import com.signavio.platform.security.business.FsSecurityManager;
import com.signavio.platform.security.business.exceptions.BusinessObjectDoesNotExistException;
import com.signavio.platform.security.business.util.UUID;
import com.signavio.warehouse.directory.business.FsDirectory;
import com.signavio.warehouse.model.business.FsModel;
import com.signavio.warehouse.revision.business.FsModelRevision;
import com.signavio.warehouse.revision.business.RepresentationType;
@HandlerConfiguration(uri = "/editor", rel="editor")
public class EditorHandler extends BasisHandler {
public final String EDITOR_URL_PREFIX;
private static final Pattern SUPPORTED_USER_AGENT_PATTERN = Pattern.compile(Platform.getInstance().getPlatformProperties().getSupportedBrowserEditorRegExp());
public EditorHandler(ServletContext servletContext) {
super(servletContext);
EDITOR_URL_PREFIX = Platform.getInstance().getPlatformProperties().getEditorUri() + "/";
}
/**
* Implementation of a GET request
* @param req
* @param res
* @param identifier
* @throws Exception
*/
@Override
@HandlerMethodActivation
public <T extends FsSecureBusinessObject> void doGet(HttpServletRequest req, HttpServletResponse res, FsAccessToken token, T sbo) {
//check for firefox
if(!isSupported(req)) {
//show firefox page
addJSPAttributes(req);
try {
req.getRequestDispatcher("/WEB-INF/jsp/browser.jsp").include(req, res);
} catch (ServletException e) {
throw new RequestException("servletException", e);
} catch (IOException e) {
throw new IORequestException(e);
}
} else {
//return editor
JSONObject jParams = (JSONObject) req.getAttribute("params");
try{
//get id of model or revision
String id = jParams.getString("id");
id = id.replace("/directory/", "");
String revision = null;
if(jParams.has("revision")) {
revision = jParams.getString("revision");
}
if(jParams.has("data")) {
//editor requested model data (json)
String json = getJSONString(id, revision, token, req);
res.setStatus(200);
res.setContentType("application/json");
try {
res.getWriter().print(json);
} catch (IOException e) {
throw new RequestException("platform.ioexception", e);
}
} else {
FsAccount account = token.getAccount();
try {
//try to get the sbo for the id
FsSecureBusinessObject tempsbo = FsSecurityManager.getInstance().loadObject(id, token);
if(tempsbo instanceof FsModel) {
//check, if sbo is a model
FsModel model = (FsModel) tempsbo;
//set location
res.setHeader("location", req.getRequestURL() + "?id=" + id);
//print xhtml site
sendEditorXHTML(res, model.getName(), account);
}
// else if (sbo instanceof ModelRevision) {
// //check, if sbo is a model revision
// ModelRevision rev = (ModelRevision) tempsbo;
// Model model = (Model) SecurityManager.getInstance().loadObject(rev.getModelId(), token);
//
// sendEditorXHTML(res, model.getName(), account);
// }
else {
throw new RequestException("editor.invalidIdentifier");
}
} catch (BusinessObjectDoesNotExistException e) {
//id is no existing model/revision, get info from session
//Map<String,String> tempModelInfo = (Map<String,String>) req.getSession().getAttribute(id);
addJSPAttributes(req);
// Properties translation = TranslationFactory.getTranslation(token);
req.setAttribute("title", "New Process");
req.setAttribute("language", account.getLanguageCode());
req.setAttribute("country", account.getCountryCode());
//set location
res.setHeader("location", req.getRequestURL() + "?id=" + id);
// try {
sendEditorXHTML(res, "New Process", account);
//req.getRequestDispatcher("/WEB-INF/jsp/editor.jsp").include(req, res);
// } catch (ServletException e1) {
// throw new RequestException("servletException", e1);
// } catch (IOException e1) {
// throw new IORequestException(e1);
// }
}
}
} catch (JSONException e) {
//no id supplied, get stencilset namespace and directory id
try {
String stencilset = jParams.getString("stencilset");
//TODO check validity of stencilset namespace
String dirId = jParams.getString("directory");
UUID uuid = UUID.getUUID();
Map<String, String> tempModelInfo = new HashMap<String, String>();
tempModelInfo.put("stencilset", stencilset);
tempModelInfo.put("directory", dirId);
tempModelInfo.put("id", uuid.toString());
if (jParams.has("extensions")){
tempModelInfo.put("extensions", jParams.getString("extensions"));
}
req.getSession().setAttribute(uuid.toString(), tempModelInfo);
// Uses the variable from the properties because of http and https problems!
// The servlet doesn't know if there is a secure https connection.
String url = Platform.getInstance().getPlatformProperties().getServerName() + req.getRequestURI();
res.sendRedirect(url+ "?id=" + uuid.toString());
} catch(JSONException e2) {
res.setStatus(405);
} catch (IOException e3) {
throw new RequestException("platform.ioexception", e3);
}
}
}
}
private void sendEditorXHTML(HttpServletResponse res, String title, FsAccount account) {
String languageCode = account.getAccountInfo().getLanguageCode();
String countryCode = account.getAccountInfo().getCountryCode();
res.setStatus(200);
res.setContentType("application/xhtml+xml");
try {
res.getWriter().print(getEditorXHTML(title, languageCode, countryCode));
} catch (IOException e) {
}
}
private String getEditorXHTML(String title, String languageCode, String countryCode) {
String libsUri = Platform.getInstance().getPlatformProperties().getLibsUri();
String explorerUri = Platform.getInstance().getPlatformProperties().getExplorerUri();
String languageFiles = "";
if(!languageCode.equals("en") && !countryCode.equals("us")) {
if (new File(this.getServerRootPath() + EDITOR_URL_PREFIX + "i18n/translation_"+languageCode+".js").exists()) {
// Add regular i18n file for language
languageFiles += "<script src=\"" + EDITOR_URL_PREFIX
+ "i18n/translation_"+languageCode+".js\" type=\"text/javascript\" />\n";
// Add signavio i18n file for language
languageFiles += "<script src=\"" + EDITOR_URL_PREFIX
+ "i18n/translation_signavio_"+languageCode+".js\" type=\"text/javascript\" />\n";
}
if (new File(this.getServerRootPath() + EDITOR_URL_PREFIX + "i18n/translation_" + languageCode+"_" + countryCode + ".js").exists()) {
// Add regular i18n file for language
languageFiles += "<script src=\"" + EDITOR_URL_PREFIX
+ "i18n/translation_" + languageCode+"_" + countryCode
+ ".js\" type=\"text/javascript\" />\n";
// Add signavio i18n file for language
languageFiles += "<script src=\"" + EDITOR_URL_PREFIX
+ "i18n/translation_signavio_" + languageCode+"_" + countryCode
+ ".js\" type=\"text/javascript\" />\n";
}
}
return "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">"
+ "<html xmlns=\"http://www.w3.org/1999/xhtml\"\n"
+ "xmlns:b3mn=\"http://b3mn.org/2007/b3mn\"\n"
+ "xmlns:ext=\"http://b3mn.org/2007/ext\"\n"
+ "xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"\n"
+ "xmlns:atom=\"http://b3mn.org/2007/atom+xhtml\">\n"
+ "<head profile=\"http://purl.org/NET/erdf/profile\">\n"
+ "<title>" + title + " - Signavio</title>\n"
+ "<!-- libraries -->\n"
+ "<script src=\"" + libsUri + "/prototype-1.5.1.js\" type=\"text/javascript\" />\n"
+ "<script src=\"" + libsUri + "/path_parser.js\" type=\"text/javascript\" />\n"
+ "<script src=\"" + libsUri + "/ext-2.0.2/adapter/ext/ext-base.js\" type=\"text/javascript\" />\n"
+ "<script src=\"" + libsUri + "/ext-2.0.2/ext-all.js\" type=\"text/javascript\" />\n"
+ "<script src=\"" + libsUri + "/ext-2.0.2/color-field.js\" type=\"text/javascript\" />\n"
+ "<style media=\"screen\" type=\"text/css\">\n"
+ "@import url(\"" + libsUri + "/ext-2.0.2/resources/css/ext-all.css\");\n"
+ "@import url(\""+ libsUri + "/ext-2.0.2/resources/css/xtheme-darkgray.css\");\n"
+ "</style>\n"
//+ headExtentions
+ "<link rel=\"Stylesheet\" media=\"screen\" href=\"" + EDITOR_URL_PREFIX + "css/theme_norm.css\" type=\"text/css\" />\n"
+ "<link rel=\"Stylesheet\" media=\"screen\" href=\"" + EDITOR_URL_PREFIX + "css/theme_norm_signavio.css\" type=\"text/css\" />\n"
+ "<link rel=\"Stylesheet\" media=\"screen\" href=\"" + explorerUri + "/src/css/xtheme-smoky.css\" type=\"text/css\" />\n"
+ "<link rel=\"Stylesheet\" media=\"screen\" href=\"" + explorerUri + "/src/css/custom-style.css\" type=\"text/css\" />\n"
+ "<!-- oryx editor -->\n"
// EN_US is default an base language
+ "<!-- language files -->\n"
+ "<script src=\"" + EDITOR_URL_PREFIX + "i18n/translation_en_us.js\" type=\"text/javascript\" />\n"
+ "<script src=\"" + EDITOR_URL_PREFIX + "i18n/translation_signavio_en_us.js\" type=\"text/javascript\" />\n"
+ languageFiles
+ "<script src=\"" + libsUri + "/utils.js\" type=\"text/javascript\" />\n"
+ "<script src=\"" + EDITOR_URL_PREFIX + "oryx.debug.js\" type=\"text/javascript\" />\n"
+ "<!-- erdf schemas -->\n"
+ "<link rel=\"schema.dc\" href=\"http://purl.org/dc/elements/1.1/\" />\n"
+ "<link rel=\"schema.dcTerms\" href=\"http://purl.org/dc/terms/\" />\n"
+ "<link rel=\"schema.b3mn\" href=\"http://b3mn.org\" />\n"
+ "<link rel=\"schema.oryx\" href=\"http://oryx-editor.org/\" />\n"
+ "<link rel=\"schema.raziel\" href=\"http://raziel.org/\" />\n"
+ "</head>\n"
+ "<body style=\"overflow:hidden;\">\n"
+ "</body>\n"
+ "</html>";
}
@SuppressWarnings("unchecked")
private String getJSONString(String id, String revision, FsAccessToken token, HttpServletRequest req) {
JSONObject result = new JSONObject();
try {
String idInContext = (String) this.getServletContext().getAttribute(id);
FsSecureBusinessObject sbo = null;
if(idInContext != null) {
sbo = FsSecurityManager.getInstance().loadObject(idInContext, token);
} else {
sbo = FsSecurityManager.getInstance().loadObject(id, token);
}
String modelData = null;
String directory = null;
String name = "";
String description = "";
FsModelRevision rev;
//id references a model (HEAD revision)
if(sbo instanceof FsModel) {
FsModel model = (FsModel) sbo;
name = model.getName();
description = model.getDescription();
if(revision != null) {
rev = model.getRevision(Integer.parseInt(revision));
} else {
rev = model.getHeadRevision();
}
FsDirectory dir = model.getParentDirectory();
directory = dir.getId();
//id referencens a model revision
}
// else if (sbo instanceof ModelRevision) {
//
// rev = (ModelRevision) sbo;
//
// Model model = (Model) SecurityManager.getInstance().loadObject(rev.getModelId(), token);
//
// name = model.getName();
//
// description = model.getDescription();
//
// Directory dir = model.getParentDirectory();
//
// directory = dir.getId();
//
// //id is neither a model, nor a model revision
// }
else {
throw new RequestException("editor.invalidIdException");
}
//get model data (json)
try {
modelData = new String(rev.getRepresentation(RepresentationType.JSON).getContent(), "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new RequestException("editor.unsupportedEncodingException");
}
try {
result.put("modelId", id);
result.put("parent", directory);
JSONObject modelJSON = new JSONObject(modelData);
result.put("model", modelJSON);
result.put("name", name);
result.put("description", description);
result.put("modelHandler", getModelHandlerURI(req) );
result.put("revision", rev.getRevisionNumber());
result.put("versioning", true);
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return result.toString();
} catch (BusinessObjectDoesNotExistException e) {
//new model. return a json stub with the info in the session
Map<String, String> params = (Map<String, String>) req.getSession().getAttribute(id);
try {
result.put("modelId", id);
result.put("parent", (String)params.get("directory"));
String stencilset = (String)params.get("stencilset");
String[] extensions;
if (params.containsKey("extensions")){
String str = params.get("extensions");
extensions = str.split(";");
} else {
extensions = new String[] {};
}
result.put("model", createJSONStub(stencilset, extensions));
result.put("name", "New Model");
result.put("description", "");
result.put("modelHandler", getModelHandlerURI(req) );
result.put("revision", 0);
result.put("new", true);
result.put("versioning", true);
} catch (JSONException e1) {
throw new JSONRequestException(e1);
}
return result.toString();
}
}
private JSONObject createJSONStub(String ssnamespace, String[] extensions) {
JSONObject modelData = new JSONObject();
try {
modelData.put("resourceId", "canvas");
modelData.put("id", "canvas");
JSONObject ssJSON = new JSONObject();
ssJSON.put("namespace", ssnamespace);
modelData.put("stencilset", ssJSON);
if(extensions != null && extensions.length > 0) {
JSONArray ext = new JSONArray();
for(int i = 0; i < extensions.length; i++) {
ext.put(extensions[i]);
}
modelData.put("extensions", ext);
modelData.put("ssextensions", ext);
}
return modelData;
} catch (JSONException e) {
throw new RequestException("editor.creatingJSONStubFailed");
}
}
private String getModelHandlerURI(HttpServletRequest req) {
return req.getRequestURI().replace(this.getHandlerURI(), "") + "/model";
}
private boolean isSupported(HttpServletRequest req) {
String userAgent = req.getHeader("User-Agent");
Matcher m = SUPPORTED_USER_AGENT_PATTERN.matcher(userAgent);
return m.find();
}
}