mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-31 17:39:05 +00:00
Moving to root below branch label
git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@2005 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
159
source/java/org/alfresco/web/app/AlfrescoNavigationHandler.java
Normal file
159
source/java/org/alfresco/web/app/AlfrescoNavigationHandler.java
Normal file
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation; either version
|
||||
* 2.1 of the License, or (at your option) any later version.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.gnu.org/licenses/lgpl.txt
|
||||
*
|
||||
* 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.alfresco.web.app;
|
||||
|
||||
import javax.faces.application.NavigationHandler;
|
||||
import javax.faces.application.ViewHandler;
|
||||
import javax.faces.component.UIViewRoot;
|
||||
import javax.faces.context.FacesContext;
|
||||
|
||||
import org.alfresco.config.Config;
|
||||
import org.alfresco.config.ConfigService;
|
||||
import org.alfresco.web.bean.NavigationBean;
|
||||
import org.alfresco.web.bean.repository.Node;
|
||||
import org.alfresco.web.config.NavigationConfigElement;
|
||||
import org.alfresco.web.config.NavigationElementReader;
|
||||
import org.alfresco.web.config.NavigationResult;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.web.jsf.FacesContextUtils;
|
||||
|
||||
/**
|
||||
* @author gavinc
|
||||
*/
|
||||
public class AlfrescoNavigationHandler extends NavigationHandler
|
||||
{
|
||||
private final static Log logger = LogFactory.getLog(AlfrescoNavigationHandler.class);
|
||||
|
||||
// The original navigation handler
|
||||
private NavigationHandler origHandler;
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*
|
||||
* @param origHandler The original navigation handler
|
||||
*/
|
||||
public AlfrescoNavigationHandler(NavigationHandler origHandler)
|
||||
{
|
||||
super();
|
||||
this.origHandler = origHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.application.NavigationHandler#handleNavigation(javax.faces.context.FacesContext, java.lang.String, java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public void handleNavigation(FacesContext context, String fromAction, String outcome)
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("handleNavigation (fromAction=" + fromAction + ", outcome=" + outcome + ")");
|
||||
|
||||
boolean useOriginalNavHandler = true;
|
||||
String finalOutcome = outcome;
|
||||
String viewId = context.getViewRoot().getViewId();
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Current view id: " + viewId);
|
||||
|
||||
NavigationBean navBean = (NavigationBean)context.getExternalContext().
|
||||
getSessionMap().get("NavigationBean");
|
||||
|
||||
// only continue if we have some dispatching context
|
||||
if (navBean != null && navBean.getDispatchContextNode() != null)
|
||||
{
|
||||
Node node = navBean.getDispatchContextNode();
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Found node in dispatch context: " + node);
|
||||
|
||||
// see if there is any navigation config for the node type
|
||||
ConfigService configSvc = (ConfigService)FacesContextUtils.getRequiredWebApplicationContext(
|
||||
context).getBean(Application.BEAN_CONFIG_SERVICE);
|
||||
|
||||
Config nodeConfig = configSvc.getConfig(node);
|
||||
NavigationConfigElement navigationCfg = (NavigationConfigElement)nodeConfig.
|
||||
getConfigElement(NavigationElementReader.ELEMENT_NAVIGATION);
|
||||
|
||||
if (navigationCfg != null)
|
||||
{
|
||||
// see if there is config for the current view state
|
||||
NavigationResult navResult = navigationCfg.getOverride(viewId, outcome);
|
||||
|
||||
if (navResult != null)
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Found navigation config: " + navResult);
|
||||
|
||||
if (navResult.isOutcome())
|
||||
{
|
||||
finalOutcome = navResult.getResult();
|
||||
}
|
||||
else
|
||||
{
|
||||
String newViewId = navResult.getResult();
|
||||
|
||||
if (newViewId.equals(viewId) == false)
|
||||
{
|
||||
useOriginalNavHandler = false;
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Dispatching to new view id: " + newViewId);
|
||||
|
||||
ViewHandler viewHandler = context.getApplication().getViewHandler();
|
||||
UIViewRoot viewRoot = viewHandler.createView(context, newViewId);
|
||||
viewRoot.setViewId(newViewId);
|
||||
context.setViewRoot(viewRoot);
|
||||
context.renderResponse();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("New view id is the same as the current one so setting outcome to null");
|
||||
|
||||
finalOutcome = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("No override configuration found for current view or outcome");
|
||||
}
|
||||
}
|
||||
else if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("No navigation configuration found for node");
|
||||
}
|
||||
|
||||
// reset the dispatch context
|
||||
navBean.resetDispatchContext();
|
||||
}
|
||||
else if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("No dispatch context found");
|
||||
}
|
||||
|
||||
// do the appropriate navigation handling
|
||||
if (useOriginalNavHandler)
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Passing outcome '" + finalOutcome + "' to original navigation handler");
|
||||
|
||||
this.origHandler.handleNavigation(context, fromAction, finalOutcome);
|
||||
}
|
||||
}
|
||||
}
|
662
source/java/org/alfresco/web/app/Application.java
Normal file
662
source/java/org/alfresco/web/app/Application.java
Normal file
@@ -0,0 +1,662 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.web.app;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import javax.faces.context.FacesContext;
|
||||
import javax.portlet.PortletContext;
|
||||
import javax.portlet.PortletSession;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import org.alfresco.config.ConfigService;
|
||||
import org.alfresco.error.AlfrescoRuntimeException;
|
||||
import org.alfresco.repo.importer.ImporterBootstrap;
|
||||
import org.alfresco.service.cmr.repository.StoreRef;
|
||||
import org.alfresco.web.app.servlet.AuthenticationHelper;
|
||||
import org.alfresco.web.bean.ErrorBean;
|
||||
import org.alfresco.web.bean.repository.User;
|
||||
import org.alfresco.web.config.ServerConfigElement;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
import org.springframework.web.context.support.WebApplicationContextUtils;
|
||||
import org.springframework.web.jsf.FacesContextUtils;
|
||||
|
||||
/**
|
||||
* Utilities class
|
||||
*
|
||||
* @author gavinc
|
||||
*/
|
||||
public class Application
|
||||
{
|
||||
private static final String LOCALE = "locale";
|
||||
|
||||
public static final String BEAN_CONFIG_SERVICE = "configService";
|
||||
public static final String BEAN_DATA_DICTIONARY = "dataDictionary";
|
||||
public static final String BEAN_IMPORTER_BOOTSTRAP = "importerBootstrap";
|
||||
|
||||
public static final String MESSAGE_BUNDLE = "alfresco.messages.webclient";
|
||||
|
||||
private static boolean inPortalServer = true;
|
||||
private static StoreRef repoStoreRef;
|
||||
private static String rootPath;
|
||||
private static String companyRootId;
|
||||
private static String companyRootDescription;
|
||||
private static String glossaryFolderName;
|
||||
private static String spaceTemplatesFolderName;
|
||||
private static String contentTemplatesFolderName;
|
||||
|
||||
/**
|
||||
* Private constructor to prevent instantiation of this class
|
||||
*/
|
||||
private Application()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether this application is running inside a portal server
|
||||
*
|
||||
* @param inPortal true to indicate the application is running as a portlet
|
||||
*/
|
||||
public static void setInPortalServer(boolean inPortal)
|
||||
{
|
||||
inPortalServer = inPortal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the server is running in a portal
|
||||
*
|
||||
* @return true if we are running inside a portal server
|
||||
*/
|
||||
public static boolean inPortalServer()
|
||||
{
|
||||
return inPortalServer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles errors thrown from servlets
|
||||
*
|
||||
* @param servletContext The servlet context
|
||||
* @param request The HTTP request
|
||||
* @param response The HTTP response
|
||||
* @param error The exception
|
||||
* @param logger The logger
|
||||
*/
|
||||
public static void handleServletError(ServletContext servletContext, HttpServletRequest request,
|
||||
HttpServletResponse response, Throwable error, Log logger, String returnPage)
|
||||
throws IOException, ServletException
|
||||
{
|
||||
// get the error bean from the session and set the error that occurred.
|
||||
HttpSession session = request.getSession();
|
||||
ErrorBean errorBean = (ErrorBean)session.getAttribute(ErrorBean.ERROR_BEAN_NAME);
|
||||
if (errorBean == null)
|
||||
{
|
||||
errorBean = new ErrorBean();
|
||||
session.setAttribute(ErrorBean.ERROR_BEAN_NAME, errorBean);
|
||||
}
|
||||
errorBean.setLastError(error);
|
||||
errorBean.setReturnPage(returnPage);
|
||||
|
||||
// try and find the configured error page
|
||||
boolean errorShown = false;
|
||||
String errorPage = getErrorPage(servletContext);
|
||||
|
||||
if (errorPage != null)
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("An error has occurred, redirecting to error page: " + errorPage);
|
||||
|
||||
if (response.isCommitted() == false)
|
||||
{
|
||||
errorShown = true;
|
||||
response.sendRedirect(request.getContextPath() + errorPage);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Response is already committed, re-throwing error");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("No error page defined, re-throwing error");
|
||||
}
|
||||
|
||||
// if we could not show the error page for whatever reason, re-throw the error
|
||||
if (!errorShown)
|
||||
{
|
||||
if (error instanceof IOException)
|
||||
{
|
||||
throw (IOException)error;
|
||||
}
|
||||
else if (error instanceof ServletException)
|
||||
{
|
||||
throw (ServletException)error;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ServletException(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the configured error page for the application
|
||||
*
|
||||
* @param servletContext The servlet context
|
||||
* @return The configured error page or null if the configuration is missing
|
||||
*/
|
||||
public static String getErrorPage(ServletContext servletContext)
|
||||
{
|
||||
return getErrorPage(WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the configured error page for the application
|
||||
*
|
||||
* @param portletContext The portlet context
|
||||
* @return
|
||||
*/
|
||||
public static String getErrorPage(PortletContext portletContext)
|
||||
{
|
||||
return getErrorPage((WebApplicationContext)portletContext.getAttribute(
|
||||
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the configured login page for the application
|
||||
*
|
||||
* @param servletContext The servlet context
|
||||
* @return The configured login page or null if the configuration is missing
|
||||
*/
|
||||
public static String getLoginPage(ServletContext servletContext)
|
||||
{
|
||||
return getLoginPage(WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the configured login page for the application
|
||||
*
|
||||
* @param portletContext The portlet context
|
||||
* @return
|
||||
*/
|
||||
public static String getLoginPage(PortletContext portletContext)
|
||||
{
|
||||
return getLoginPage((WebApplicationContext)portletContext.getAttribute(
|
||||
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the User object representing the currently logged in user
|
||||
*/
|
||||
public static User getCurrentUser(HttpSession session)
|
||||
{
|
||||
return (User)session.getAttribute(AuthenticationHelper.AUTHENTICATION_USER);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the User object representing the currently logged in user
|
||||
*/
|
||||
public static User getCurrentUser(FacesContext context)
|
||||
{
|
||||
return (User)context.getExternalContext().getSessionMap().get(AuthenticationHelper.AUTHENTICATION_USER);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the repository store URL (retrieved from config service)
|
||||
*/
|
||||
public static StoreRef getRepositoryStoreRef(ServletContext context)
|
||||
{
|
||||
return getRepositoryStoreRef(WebApplicationContextUtils.getRequiredWebApplicationContext(context));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the repository store URL (retrieved from config service)
|
||||
*/
|
||||
public static StoreRef getRepositoryStoreRef(FacesContext context)
|
||||
{
|
||||
return getRepositoryStoreRef(FacesContextUtils.getRequiredWebApplicationContext(context));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns id of the company root
|
||||
*/
|
||||
public static String getCompanyRootId()
|
||||
{
|
||||
return companyRootId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the company root id. This is setup by the ContextListener.
|
||||
*
|
||||
* @param id The company root id
|
||||
*/
|
||||
public static void setCompanyRootId(String id)
|
||||
{
|
||||
companyRootId = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the root path for the application (retrieved from config service)
|
||||
*/
|
||||
public static String getRootPath(ServletContext context)
|
||||
{
|
||||
return getRootPath(WebApplicationContextUtils.getRequiredWebApplicationContext(context));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the root path for the application (retrieved from config service)
|
||||
*/
|
||||
public static String getRootPath(FacesContext context)
|
||||
{
|
||||
return getRootPath(FacesContextUtils.getRequiredWebApplicationContext(context));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the glossary folder name (retrieved from config service)
|
||||
*/
|
||||
public static String getGlossaryFolderName(ServletContext context)
|
||||
{
|
||||
return getGlossaryFolderName(WebApplicationContextUtils.getRequiredWebApplicationContext(context));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the glossary folder name (retrieved from config service)
|
||||
*/
|
||||
public static String getGlossaryFolderName(FacesContext context)
|
||||
{
|
||||
return getGlossaryFolderName(FacesContextUtils.getRequiredWebApplicationContext(context));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the Space templates folder name (retrieved from config service)
|
||||
*/
|
||||
public static String getSpaceTemplatesFolderName(ServletContext context)
|
||||
{
|
||||
return getSpaceTemplatesFolderName(WebApplicationContextUtils.getRequiredWebApplicationContext(context));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the Space templates folder name (retrieved from config service)
|
||||
*/
|
||||
public static String getSpaceTemplatesFolderName(FacesContext context)
|
||||
{
|
||||
return getSpaceTemplatesFolderName(FacesContextUtils.getRequiredWebApplicationContext(context));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the Content templates folder name (retrieved from config service)
|
||||
*/
|
||||
public static String getContentTemplatesFolderName(ServletContext context)
|
||||
{
|
||||
return getContentTemplatesFolderName(WebApplicationContextUtils.getRequiredWebApplicationContext(context));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the Content templates folder name (retrieved from config service)
|
||||
*/
|
||||
public static String getContentTemplatesFolderName(FacesContext context)
|
||||
{
|
||||
return getContentTemplatesFolderName(FacesContextUtils.getRequiredWebApplicationContext(context));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the language locale for the current user context
|
||||
*
|
||||
* @param context FacesContext for current user
|
||||
* @param code The ISO locale code to set
|
||||
*/
|
||||
public static void setLanguage(FacesContext context, String code)
|
||||
{
|
||||
Locale locale = parseLocale(code);
|
||||
|
||||
// set locale for JSF framework usage
|
||||
context.getViewRoot().setLocale(locale);
|
||||
|
||||
// set locale for our framework usage
|
||||
context.getExternalContext().getSessionMap().put(LOCALE, locale);
|
||||
|
||||
// clear the current message bundle - so it's reloaded with new locale
|
||||
context.getExternalContext().getSessionMap().remove(MESSAGE_BUNDLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the language locale for the current user session
|
||||
*
|
||||
* @param session HttpSession for current user
|
||||
* @param code The ISO locale code to set
|
||||
*/
|
||||
public static void setLanguage(HttpSession session, String code)
|
||||
{
|
||||
Locale locale = parseLocale(code);
|
||||
|
||||
session.putValue(LOCALE, locale);
|
||||
session.removeAttribute(MESSAGE_BUNDLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param code Locale code (java format with underscores) to parse
|
||||
* @return Locale object or default if unable to parse
|
||||
*/
|
||||
private static Locale parseLocale(String code)
|
||||
{
|
||||
Locale locale = Locale.getDefault();
|
||||
|
||||
StringTokenizer t = new StringTokenizer(code, "_");
|
||||
int tokens = t.countTokens();
|
||||
if (tokens == 1)
|
||||
{
|
||||
locale = new Locale(code);
|
||||
}
|
||||
else if (tokens == 2)
|
||||
{
|
||||
locale = new Locale(t.nextToken(), t.nextToken());
|
||||
}
|
||||
else if (tokens == 3)
|
||||
{
|
||||
locale = new Locale(t.nextToken(), t.nextToken(), t.nextToken());
|
||||
}
|
||||
|
||||
return locale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the language Locale for the current user context
|
||||
*
|
||||
* @param context FacesContext for the current user
|
||||
*
|
||||
* @return Current language Locale set or null if none set
|
||||
*/
|
||||
public static Locale getLanguage(FacesContext context)
|
||||
{
|
||||
return (Locale)context.getExternalContext().getSessionMap().get(LOCALE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the language Locale for the current user Session.
|
||||
*
|
||||
* @param session HttpSession for the current user
|
||||
*
|
||||
* @return Current language Locale set or null if none set
|
||||
*/
|
||||
public static Locale getLanguage(HttpSession session)
|
||||
{
|
||||
return (Locale)session.getAttribute(LOCALE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the language Locale for the current user PortletSession.
|
||||
*
|
||||
* @param session PortletSession for the current user
|
||||
*
|
||||
* @return Current language Locale set or null if none set
|
||||
*/
|
||||
public static Locale getLanguage(PortletSession session)
|
||||
{
|
||||
return (Locale)session.getAttribute(LOCALE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the specified I18N message string from the default message bundle for this user
|
||||
*
|
||||
* @param context FacesContext
|
||||
* @param msg Message ID
|
||||
*
|
||||
* @return String from message bundle or $$msg$$ if not found
|
||||
*/
|
||||
public static String getMessage(FacesContext context, String msg)
|
||||
{
|
||||
return getBundle(context).getString(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the specified I18N message string from the default message bundle for this user
|
||||
*
|
||||
* @param session HttpSession
|
||||
* @param msg Message ID
|
||||
*
|
||||
* @return String from message bundle or $$msg$$ if not found
|
||||
*/
|
||||
public static String getMessage(HttpSession session, String msg)
|
||||
{
|
||||
return getBundle(session).getString(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the specified the default message bundle for this user
|
||||
*
|
||||
* @param session HttpSession
|
||||
*
|
||||
* @return ResourceBundle for this user
|
||||
*/
|
||||
public static ResourceBundle getBundle(HttpSession session)
|
||||
{
|
||||
ResourceBundle bundle = (ResourceBundle)session.getAttribute(MESSAGE_BUNDLE);
|
||||
if (bundle == null)
|
||||
{
|
||||
// get Locale from language selected by each user on login
|
||||
Locale locale = (Locale)session.getAttribute(LOCALE);
|
||||
if (locale == null)
|
||||
{
|
||||
locale = Locale.getDefault();
|
||||
}
|
||||
bundle = ResourceBundle.getBundle(MESSAGE_BUNDLE, locale);
|
||||
if (bundle == null)
|
||||
{
|
||||
throw new AlfrescoRuntimeException("Unable to load Alfresco messages bundle: " + MESSAGE_BUNDLE);
|
||||
}
|
||||
|
||||
// apply our wrapper to catch MissingResourceException
|
||||
bundle = new ResourceBundleWrapper(bundle);
|
||||
|
||||
session.setAttribute(MESSAGE_BUNDLE, bundle);
|
||||
}
|
||||
|
||||
return bundle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the specified the default message bundle for this user
|
||||
*
|
||||
* @param context FacesContext
|
||||
*
|
||||
* @return ResourceBundle for this user
|
||||
*/
|
||||
public static ResourceBundle getBundle(FacesContext context)
|
||||
{
|
||||
// get the resource bundle for the current locale
|
||||
// we store the bundle in the users session
|
||||
// this makes it easy to add a locale per user support later
|
||||
Map session = context.getExternalContext().getSessionMap();
|
||||
ResourceBundle bundle = (ResourceBundle)session.get(MESSAGE_BUNDLE);
|
||||
if (bundle == null)
|
||||
{
|
||||
// get Locale from language selected by each user on login
|
||||
Locale locale = (Locale)session.get(LOCALE);
|
||||
if (locale == null)
|
||||
{
|
||||
locale = Locale.getDefault();
|
||||
}
|
||||
bundle = ResourceBundle.getBundle(MESSAGE_BUNDLE, locale);
|
||||
if (bundle == null)
|
||||
{
|
||||
throw new AlfrescoRuntimeException("Unable to load Alfresco messages bundle: " + MESSAGE_BUNDLE);
|
||||
}
|
||||
|
||||
// apply our wrapper to catch MissingResourceException
|
||||
bundle = new ResourceBundleWrapper(bundle);
|
||||
|
||||
session.put(MESSAGE_BUNDLE, bundle);
|
||||
}
|
||||
|
||||
return bundle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to get the ConfigService instance
|
||||
*
|
||||
* @param context FacesContext
|
||||
*
|
||||
* @return ConfigService
|
||||
*/
|
||||
public static ConfigService getConfigService(FacesContext context)
|
||||
{
|
||||
return (ConfigService)FacesContextUtils.getRequiredWebApplicationContext(context).getBean(
|
||||
Application.BEAN_CONFIG_SERVICE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the repository store URL (retrieved from config service)
|
||||
*
|
||||
* @param context The spring context
|
||||
* @return The repository store URL to use
|
||||
*/
|
||||
private static StoreRef getRepositoryStoreRef(WebApplicationContext context)
|
||||
{
|
||||
if (repoStoreRef == null)
|
||||
{
|
||||
ImporterBootstrap bootstrap = (ImporterBootstrap)context.getBean(BEAN_IMPORTER_BOOTSTRAP);
|
||||
repoStoreRef = bootstrap.getStoreRef();
|
||||
}
|
||||
|
||||
return repoStoreRef;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the root path for the application (retrieved from config service)
|
||||
*
|
||||
* @param context The spring context
|
||||
* @return The application root path
|
||||
*/
|
||||
private static String getRootPath(WebApplicationContext context)
|
||||
{
|
||||
if (rootPath == null)
|
||||
{
|
||||
ImporterBootstrap bootstrap = (ImporterBootstrap)context.getBean(BEAN_IMPORTER_BOOTSTRAP);
|
||||
Properties configuration = bootstrap.getConfiguration();
|
||||
rootPath = configuration.getProperty("spaces.company_home.childname");
|
||||
}
|
||||
|
||||
return rootPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the glossary folder name (retrieved from config service)
|
||||
*
|
||||
* @param context The spring context
|
||||
* @return The glossary folder name
|
||||
*/
|
||||
private static String getGlossaryFolderName(WebApplicationContext context)
|
||||
{
|
||||
if (glossaryFolderName == null)
|
||||
{
|
||||
ImporterBootstrap bootstrap = (ImporterBootstrap)context.getBean(BEAN_IMPORTER_BOOTSTRAP);
|
||||
Properties configuration = bootstrap.getConfiguration();
|
||||
glossaryFolderName = configuration.getProperty("spaces.dictionary.childname");
|
||||
}
|
||||
|
||||
return glossaryFolderName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Space Templates folder name (retrieved from config service)
|
||||
*
|
||||
* @param context The spring context
|
||||
* @return The templates folder name
|
||||
*/
|
||||
private static String getSpaceTemplatesFolderName(WebApplicationContext context)
|
||||
{
|
||||
if (spaceTemplatesFolderName == null)
|
||||
{
|
||||
ImporterBootstrap bootstrap = (ImporterBootstrap)context.getBean(BEAN_IMPORTER_BOOTSTRAP);
|
||||
Properties configuration = bootstrap.getConfiguration();
|
||||
spaceTemplatesFolderName = configuration.getProperty("spaces.templates.childname");
|
||||
}
|
||||
|
||||
return spaceTemplatesFolderName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Content Templates folder name (retrieved from config service)
|
||||
*
|
||||
* @param context The spring context
|
||||
* @return The templates folder name
|
||||
*/
|
||||
private static String getContentTemplatesFolderName(WebApplicationContext context)
|
||||
{
|
||||
if (contentTemplatesFolderName == null)
|
||||
{
|
||||
ImporterBootstrap bootstrap = (ImporterBootstrap)context.getBean(BEAN_IMPORTER_BOOTSTRAP);
|
||||
Properties configuration = bootstrap.getConfiguration();
|
||||
contentTemplatesFolderName = configuration.getProperty("spaces.templates.content.childname");
|
||||
}
|
||||
|
||||
return contentTemplatesFolderName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the configured error page for the application
|
||||
*
|
||||
* @param context The Spring contexr
|
||||
* @return The configured error page or null if the configuration is missing
|
||||
*/
|
||||
private static String getErrorPage(WebApplicationContext context)
|
||||
{
|
||||
String errorPage = null;
|
||||
|
||||
ConfigService svc = (ConfigService)context.getBean(BEAN_CONFIG_SERVICE);
|
||||
ServerConfigElement serverConfig = (ServerConfigElement)svc.getGlobalConfig().getConfigElement("server");
|
||||
|
||||
if (serverConfig != null)
|
||||
{
|
||||
errorPage = serverConfig.getErrorPage();
|
||||
}
|
||||
|
||||
return errorPage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the configured login page for the application
|
||||
*
|
||||
* @param context The Spring contexr
|
||||
* @return The configured login page or null if the configuration is missing
|
||||
*/
|
||||
private static String getLoginPage(WebApplicationContext context)
|
||||
{
|
||||
String loginPage = null;
|
||||
|
||||
ConfigService svc = (ConfigService)context.getBean(BEAN_CONFIG_SERVICE);
|
||||
ServerConfigElement serverConfig = (ServerConfigElement)svc.getGlobalConfig().getConfigElement("server");
|
||||
|
||||
if (serverConfig != null)
|
||||
{
|
||||
loginPage = serverConfig.getLoginPage();
|
||||
}
|
||||
|
||||
return loginPage;
|
||||
}
|
||||
}
|
263
source/java/org/alfresco/web/app/ContextListener.java
Normal file
263
source/java/org/alfresco/web/app/ContextListener.java
Normal file
@@ -0,0 +1,263 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.web.app;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletContextEvent;
|
||||
import javax.servlet.ServletContextListener;
|
||||
import javax.servlet.http.HttpSessionEvent;
|
||||
import javax.servlet.http.HttpSessionListener;
|
||||
import javax.transaction.UserTransaction;
|
||||
|
||||
import org.alfresco.config.ConfigElement;
|
||||
import org.alfresco.config.ConfigService;
|
||||
import org.alfresco.error.AlfrescoRuntimeException;
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.security.authentication.AuthenticationComponent;
|
||||
import org.alfresco.repo.security.authentication.MutableAuthenticationDao;
|
||||
import org.alfresco.service.ServiceRegistry;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.service.cmr.repository.StoreRef;
|
||||
import org.alfresco.service.cmr.search.SearchService;
|
||||
import org.alfresco.service.cmr.security.AuthenticationService;
|
||||
import org.alfresco.service.cmr.security.PermissionService;
|
||||
import org.alfresco.service.cmr.security.PersonService;
|
||||
import org.alfresco.service.namespace.NamespaceService;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.service.transaction.TransactionService;
|
||||
import org.alfresco.web.app.portlet.AlfrescoFacesPortlet;
|
||||
import org.alfresco.web.app.servlet.AuthenticationHelper;
|
||||
import org.alfresco.web.bean.repository.Repository;
|
||||
import org.alfresco.web.bean.repository.User;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.quartz.Scheduler;
|
||||
import org.quartz.SchedulerException;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
import org.springframework.web.context.support.WebApplicationContextUtils;
|
||||
|
||||
/**
|
||||
* ServletContextListener implementation that initialises the application.
|
||||
*
|
||||
* NOTE: This class must appear after the Spring context loader listener
|
||||
*
|
||||
* @author gavinc
|
||||
*/
|
||||
public class ContextListener implements ServletContextListener, HttpSessionListener
|
||||
{
|
||||
private static Log logger = LogFactory.getLog(ContextListener.class);
|
||||
|
||||
private static final String ADMIN = "admin";
|
||||
private static final String ADMIN_FIRSTNAME = "Repository";
|
||||
private static final String ADMIN_LASTNAME = "Administrator";
|
||||
|
||||
private ServletContext servletContext;
|
||||
|
||||
/**
|
||||
* @see javax.servlet.ServletContextListener#contextInitialized(javax.servlet.ServletContextEvent)
|
||||
*/
|
||||
public void contextInitialized(ServletContextEvent event)
|
||||
{
|
||||
// make sure that the spaces store in the repository exists
|
||||
this.servletContext = event.getServletContext();
|
||||
WebApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
|
||||
ServiceRegistry registry = (ServiceRegistry) ctx.getBean(ServiceRegistry.SERVICE_REGISTRY);
|
||||
TransactionService transactionService = registry.getTransactionService();
|
||||
NodeService nodeService = registry.getNodeService();
|
||||
SearchService searchService = registry.getSearchService();
|
||||
NamespaceService namespaceService = registry.getNamespaceService();
|
||||
AuthenticationComponent authenticationComponent = (AuthenticationComponent) ctx
|
||||
.getBean("authenticationComponent");
|
||||
|
||||
// repo bootstrap code for our client
|
||||
UserTransaction tx = null;
|
||||
NodeRef companySpaceNodeRef = null;
|
||||
try
|
||||
{
|
||||
tx = transactionService.getUserTransaction();
|
||||
tx.begin();
|
||||
authenticationComponent.setCurrentUser(authenticationComponent.getSystemUserName());
|
||||
|
||||
// get and setup the initial store ref from config
|
||||
StoreRef storeRef = Repository.getStoreRef(servletContext);
|
||||
|
||||
// check the repository exists, create if it doesn't
|
||||
if (nodeService.exists(storeRef) == false)
|
||||
{
|
||||
throw new AlfrescoRuntimeException("Store not created prior to application startup: " + storeRef);
|
||||
}
|
||||
|
||||
// get hold of the root node
|
||||
NodeRef rootNodeRef = nodeService.getRootNode(storeRef);
|
||||
|
||||
// see if the company home space is present
|
||||
String rootPath = Application.getRootPath(servletContext);
|
||||
if (rootPath == null)
|
||||
{
|
||||
throw new AlfrescoRuntimeException("Root path has not been configured");
|
||||
}
|
||||
|
||||
List<NodeRef> nodes = searchService.selectNodes(rootNodeRef, rootPath, null, namespaceService, false);
|
||||
if (nodes.size() == 0)
|
||||
{
|
||||
throw new AlfrescoRuntimeException("Root path not created prior to application startup: " + rootPath);
|
||||
}
|
||||
|
||||
// Extract company space id and store it in the Application object
|
||||
companySpaceNodeRef = nodes.get(0);
|
||||
Application.setCompanyRootId(companySpaceNodeRef.getId());
|
||||
|
||||
// check the admin user exists, create if it doesn't
|
||||
MutableAuthenticationDao dao = (MutableAuthenticationDao) ctx.getBean("alfDaoImpl");
|
||||
|
||||
// this is required to setup the ACEGI context before we can check
|
||||
// for the user
|
||||
if (!dao.userExists(ADMIN))
|
||||
{
|
||||
ConfigService configService = (ConfigService) ctx.getBean(Application.BEAN_CONFIG_SERVICE);
|
||||
// default to password of "admin" if we don't find config for it
|
||||
String password = ADMIN;
|
||||
ConfigElement adminConfig = configService.getGlobalConfig().getConfigElement("admin");
|
||||
if (adminConfig != null)
|
||||
{
|
||||
List<ConfigElement> children = adminConfig.getChildren();
|
||||
if (children.size() != 0)
|
||||
{
|
||||
// try to find the config element for the initial
|
||||
// password
|
||||
ConfigElement passElement = children.get(0);
|
||||
if (passElement.getName().equals("initial-password"))
|
||||
{
|
||||
password = passElement.getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// create the Authentication instance for the "admin" user
|
||||
AuthenticationService authService = (AuthenticationService) ctx.getBean("authenticationService");
|
||||
authService.createAuthentication(ADMIN, password.toCharArray());
|
||||
}
|
||||
|
||||
PersonService personService = (PersonService) ctx.getBean("personService");
|
||||
if (!personService.personExists(ADMIN))
|
||||
{
|
||||
// create the node to represent the Person instance for the
|
||||
// admin user
|
||||
Map<QName, Serializable> props = new HashMap<QName, Serializable>(7, 1.0f);
|
||||
props.put(ContentModel.PROP_USERNAME, ADMIN);
|
||||
props.put(ContentModel.PROP_FIRSTNAME, ADMIN_FIRSTNAME);
|
||||
props.put(ContentModel.PROP_LASTNAME, ADMIN_LASTNAME);
|
||||
props.put(ContentModel.PROP_HOMEFOLDER, companySpaceNodeRef);
|
||||
props.put(ContentModel.PROP_EMAIL, "");
|
||||
props.put(ContentModel.PROP_ORGID, "");
|
||||
|
||||
personService.createPerson(props);
|
||||
}
|
||||
// set the store's GUEST access if we are allowed to modify permissions
|
||||
if (!transactionService.isReadOnly())
|
||||
{
|
||||
PermissionService permissionService = (PermissionService) ctx.getBean("permissionService");
|
||||
permissionService.setPermission(rootNodeRef, permissionService.getAllAuthorities(), PermissionService.GUEST, true);
|
||||
}
|
||||
|
||||
// commit the transaction
|
||||
tx.commit();
|
||||
}
|
||||
catch (Throwable e)
|
||||
{
|
||||
// rollback the transaction
|
||||
try
|
||||
{
|
||||
if (tx != null)
|
||||
{
|
||||
tx.rollback();
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {}
|
||||
|
||||
logger.error("Failed to initialise ", e);
|
||||
throw new AlfrescoRuntimeException("Failed to initialise ", e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
authenticationComponent.clearCurrentSecurityContext();
|
||||
}
|
||||
catch (Exception ex) {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.servlet.ServletContextListener#contextDestroyed(javax.servlet.ServletContextEvent)
|
||||
*/
|
||||
public void contextDestroyed(ServletContextEvent event)
|
||||
{
|
||||
WebApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
|
||||
Scheduler quartz = (Scheduler) ctx.getBean("schedulerFactory");
|
||||
try
|
||||
{
|
||||
quartz.shutdown(true);
|
||||
}
|
||||
catch (SchedulerException e)
|
||||
{
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Session created listener
|
||||
*/
|
||||
public void sessionCreated(HttpSessionEvent event)
|
||||
{
|
||||
if (logger.isDebugEnabled()) logger.debug("HTTP session created: " + event.getSession().getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Session destroyed listener
|
||||
*/
|
||||
public void sessionDestroyed(HttpSessionEvent event)
|
||||
{
|
||||
if (logger.isDebugEnabled()) logger.debug("HTTP session destroyed: " + event.getSession().getId());
|
||||
|
||||
User user;
|
||||
if (Application.inPortalServer() == false)
|
||||
{
|
||||
user = (User)event.getSession().getAttribute(AuthenticationHelper.AUTHENTICATION_USER);
|
||||
}
|
||||
else
|
||||
{
|
||||
user = (User)event.getSession().getAttribute(AlfrescoFacesPortlet.MANAGED_BEAN_PREFIX + AuthenticationHelper.AUTHENTICATION_USER);
|
||||
}
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
// invalidate ticket
|
||||
WebApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
|
||||
AuthenticationService authService = (AuthenticationService) ctx.getBean("authenticationService");
|
||||
authService.invalidateTicket(user.getTicket());
|
||||
event.getSession().removeAttribute(AuthenticationHelper.AUTHENTICATION_USER);
|
||||
}
|
||||
}
|
||||
}
|
62
source/java/org/alfresco/web/app/DebugPhaseListener.java
Normal file
62
source/java/org/alfresco/web/app/DebugPhaseListener.java
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation; either version
|
||||
* 2.1 of the License, or (at your option) any later version.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.gnu.org/licenses/lgpl.txt
|
||||
*
|
||||
* 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.alfresco.web.app;
|
||||
|
||||
import javax.faces.event.PhaseEvent;
|
||||
import javax.faces.event.PhaseId;
|
||||
import javax.faces.event.PhaseListener;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* Debug phase listener that simply logs when each phase is entered and exited.
|
||||
*
|
||||
* @author gavinc
|
||||
*/
|
||||
public class DebugPhaseListener implements PhaseListener
|
||||
{
|
||||
private static final Log logger = LogFactory.getLog(DebugPhaseListener.class);
|
||||
|
||||
/**
|
||||
* @see javax.faces.event.PhaseListener#afterPhase(javax.faces.event.PhaseEvent)
|
||||
*/
|
||||
public void afterPhase(PhaseEvent event)
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("********** Exiting phase: " + event.getPhaseId().toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.event.PhaseListener#beforePhase(javax.faces.event.PhaseEvent)
|
||||
*/
|
||||
public void beforePhase(PhaseEvent event)
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("********** Entering phase: " + event.getPhaseId().toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.event.PhaseListener#getPhaseId()
|
||||
*/
|
||||
public PhaseId getPhaseId()
|
||||
{
|
||||
return PhaseId.ANY_PHASE;
|
||||
}
|
||||
|
||||
}
|
71
source/java/org/alfresco/web/app/ResourceBundleWrapper.java
Normal file
71
source/java/org/alfresco/web/app/ResourceBundleWrapper.java
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.web.app;
|
||||
|
||||
import java.util.Enumeration;
|
||||
import java.util.MissingResourceException;
|
||||
import java.util.ResourceBundle;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.apache.log4j.Priority;
|
||||
|
||||
/**
|
||||
* Wrapper around Alfresco Resource Bundle objects. Used to catch and handle missing
|
||||
* resource exception to help identify missing I18N strings in client apps.
|
||||
*
|
||||
* @author Kevin Roast
|
||||
*/
|
||||
public final class ResourceBundleWrapper extends ResourceBundle
|
||||
{
|
||||
private static Logger logger = Logger.getLogger(ResourceBundleWrapper.class);
|
||||
private ResourceBundle delegate;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param bundle The ResourceBundle to route calls too
|
||||
*/
|
||||
public ResourceBundleWrapper(ResourceBundle bundle)
|
||||
{
|
||||
this.delegate = bundle;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.util.ResourceBundle#getKeys()
|
||||
*/
|
||||
public Enumeration<String> getKeys()
|
||||
{
|
||||
return this.delegate.getKeys();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.util.ResourceBundle#handleGetObject(java.lang.String)
|
||||
*/
|
||||
protected Object handleGetObject(String key)
|
||||
{
|
||||
try
|
||||
{
|
||||
return this.delegate.getObject(key);
|
||||
}
|
||||
catch (MissingResourceException err)
|
||||
{
|
||||
if (logger.isEnabledFor(Priority.WARN))
|
||||
logger.warn("Failed to find I18N message string key: " + key);
|
||||
|
||||
return "$$" + key + "$$";
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.web.app.context;
|
||||
|
||||
/**
|
||||
* Interface used to allow Beans to register themselves as interested in UI context events.
|
||||
* <p>
|
||||
* Beans supporting this interface should be register against the UIContextService. Then Beans
|
||||
* which wish to indicate that the UI should refresh itself i.e. dump all cached data and settings,
|
||||
* call the UIContextService.notifyBeans() to inform all registered instances of the change.
|
||||
*
|
||||
* @author Kevin Roast
|
||||
*/
|
||||
public interface IContextListener
|
||||
{
|
||||
/**
|
||||
* Method called by UIContextService.notifyBeans() to inform all registered beans that
|
||||
* all UI Beans should refresh dump all cached data and settings.
|
||||
*/
|
||||
public void contextUpdated();
|
||||
}
|
106
source/java/org/alfresco/web/app/context/UIContextService.java
Normal file
106
source/java/org/alfresco/web/app/context/UIContextService.java
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.web.app.context;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.faces.context.FacesContext;
|
||||
|
||||
/**
|
||||
* Beans supporting the IContextListener interface are registered against this class. Then Beans
|
||||
* which wish to indicate that the UI should refresh itself i.e. dump all cached data and settings,
|
||||
* call the UIContextService.notifyBeans() to inform all registered instances of the change.
|
||||
*
|
||||
* @author Kevin Roast
|
||||
*/
|
||||
public final class UIContextService
|
||||
{
|
||||
/**
|
||||
* Private constructor
|
||||
*/
|
||||
private UIContextService()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Session local instance of the UIContextService
|
||||
*
|
||||
* @return UIContextService for this Thread
|
||||
*/
|
||||
public static UIContextService getInstance(FacesContext fc)
|
||||
{
|
||||
Map session = fc.getExternalContext().getSessionMap();
|
||||
UIContextService service = (UIContextService)session.get(CONTEXT_KEY);
|
||||
if (service == null)
|
||||
{
|
||||
service = new UIContextService();
|
||||
session.put(CONTEXT_KEY, service);
|
||||
}
|
||||
|
||||
return service;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a bean to be informed of context events
|
||||
*
|
||||
* @param bean Conforming to the IContextListener interface
|
||||
*/
|
||||
public void registerBean(IContextListener bean)
|
||||
{
|
||||
if (bean == null)
|
||||
{
|
||||
throw new IllegalArgumentException("Bean reference specified cannot be null!");
|
||||
}
|
||||
|
||||
this.registeredBeans.put(bean.getClass(), bean);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a bean reference from those notified of changes
|
||||
*
|
||||
* @param bean Conforming to the IContextListener interface
|
||||
*/
|
||||
public void unregisterBean(IContextListener bean)
|
||||
{
|
||||
if (bean == null)
|
||||
{
|
||||
throw new IllegalArgumentException("Bean reference specified cannot be null!");
|
||||
}
|
||||
|
||||
this.registeredBeans.remove(bean);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call to notify all register beans that the UI context has changed and they should
|
||||
* refresh themselves as appropriate.
|
||||
*/
|
||||
public void notifyBeans()
|
||||
{
|
||||
for (IContextListener listener: this.registeredBeans.values())
|
||||
{
|
||||
listener.contextUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** key for the UI context service in the session */
|
||||
private final static String CONTEXT_KEY = "__uiContextService";
|
||||
|
||||
/** Map of bean registered against the context service */
|
||||
private Map<Class, IContextListener> registeredBeans = new HashMap<Class, IContextListener>(7, 1.0f);
|
||||
}
|
@@ -0,0 +1,343 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.web.app.portlet;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import javax.faces.application.ViewHandler;
|
||||
import javax.faces.component.UIViewRoot;
|
||||
import javax.faces.context.FacesContext;
|
||||
import javax.portlet.ActionRequest;
|
||||
import javax.portlet.ActionResponse;
|
||||
import javax.portlet.PortletException;
|
||||
import javax.portlet.PortletRequestDispatcher;
|
||||
import javax.portlet.PortletSession;
|
||||
import javax.portlet.RenderRequest;
|
||||
import javax.portlet.RenderResponse;
|
||||
|
||||
import org.alfresco.i18n.I18NUtil;
|
||||
import org.alfresco.service.cmr.security.AuthenticationService;
|
||||
import org.alfresco.util.TempFileProvider;
|
||||
import org.alfresco.web.app.Application;
|
||||
import org.alfresco.web.app.servlet.AuthenticationHelper;
|
||||
import org.alfresco.web.bean.ErrorBean;
|
||||
import org.alfresco.web.bean.FileUploadBean;
|
||||
import org.alfresco.web.bean.repository.User;
|
||||
import org.apache.commons.fileupload.FileItem;
|
||||
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
|
||||
import org.apache.commons.fileupload.portlet.PortletFileUpload;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.myfaces.portlet.MyFacesGenericPortlet;
|
||||
import org.apache.myfaces.portlet.PortletUtil;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
|
||||
/**
|
||||
* Class to extend the MyFacesGenericPortlet to provide behaviour specific to Alfresco web client.
|
||||
* Handles upload of multi-part forms through a JSR-168 Portlet, generic error handling and session
|
||||
* login authentication.
|
||||
*
|
||||
* @author Gavin Cornwell, Kevin Roast
|
||||
*/
|
||||
public class AlfrescoFacesPortlet extends MyFacesGenericPortlet
|
||||
{
|
||||
public static final String INSTANCE_NAME = "AlfrescoClientInstance";
|
||||
public static final String WINDOW_NAME = "AlfrescoClientWindow";
|
||||
public static final String MANAGED_BEAN_PREFIX = "javax.portlet.p." + INSTANCE_NAME +
|
||||
"." + WINDOW_NAME + "?";
|
||||
|
||||
private static final String ERROR_PAGE_PARAM = "error-page";
|
||||
private static final String ERROR_OCCURRED = "error-occurred";
|
||||
|
||||
private static Log logger = LogFactory.getLog(AlfrescoFacesPortlet.class);
|
||||
|
||||
private String loginPage = null;
|
||||
private String errorPage = null;
|
||||
|
||||
|
||||
/**
|
||||
* Called by the portlet container to allow the portlet to process an action request.
|
||||
*/
|
||||
public void processAction(ActionRequest request, ActionResponse response)
|
||||
throws PortletException, IOException
|
||||
{
|
||||
boolean isMultipart = PortletFileUpload.isMultipartContent(request);
|
||||
|
||||
try
|
||||
{
|
||||
// NOTE: Due to filters not being called within portlets we can not make use
|
||||
// of the MyFaces file upload support, therefore we are using a pure
|
||||
// portlet request/action to handle file uploads until there is a
|
||||
// solution.
|
||||
|
||||
if (isMultipart)
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Handling multipart request...");
|
||||
|
||||
PortletSession session = request.getPortletSession();
|
||||
|
||||
// get the file from the request and put it in the session
|
||||
DiskFileItemFactory factory = new DiskFileItemFactory();
|
||||
PortletFileUpload upload = new PortletFileUpload(factory);
|
||||
List<FileItem> fileItems = upload.parseRequest(request);
|
||||
Iterator<FileItem> iter = fileItems.iterator();
|
||||
FileUploadBean bean = new FileUploadBean();
|
||||
while(iter.hasNext())
|
||||
{
|
||||
FileItem item = iter.next();
|
||||
String filename = item.getName();
|
||||
if(item.isFormField() == false)
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Processing uploaded file: " + filename);
|
||||
|
||||
// workaround a bug in IE where the full path is returned
|
||||
// IE is only available for Windows so only check for the Windows path separator
|
||||
int idx = filename.lastIndexOf('\\');
|
||||
|
||||
if (idx == -1)
|
||||
{
|
||||
// if there is no windows path separator check for *nix
|
||||
idx = filename.lastIndexOf('/');
|
||||
}
|
||||
|
||||
if (idx != -1)
|
||||
{
|
||||
filename = filename.substring(idx + File.separator.length());
|
||||
}
|
||||
|
||||
File tempFile = TempFileProvider.createTempFile("alfresco", ".upload");
|
||||
item.write(tempFile);
|
||||
bean.setFile(tempFile);
|
||||
bean.setFileName(filename);
|
||||
bean.setFilePath(tempFile.getAbsolutePath());
|
||||
session.setAttribute(FileUploadBean.FILE_UPLOAD_BEAN_NAME, bean,
|
||||
PortletSession.PORTLET_SCOPE);
|
||||
}
|
||||
}
|
||||
|
||||
// it doesn't matter what the value is we just need the VIEW_ID parameter
|
||||
// to tell the faces portlet bridge to treat the request as a JSF request,
|
||||
// this will send us back to the same page we came from, which is fine for
|
||||
// most scenarios.
|
||||
response.setRenderParameter(VIEW_ID, "a-jsf-page");
|
||||
}
|
||||
else
|
||||
{
|
||||
// do the normal JSF processing
|
||||
super.processAction(request, response);
|
||||
}
|
||||
}
|
||||
catch (Throwable e)
|
||||
{
|
||||
if (getErrorPage() != null)
|
||||
{
|
||||
handleError(request, response, e);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.warn("No error page configured, re-throwing exception");
|
||||
|
||||
if (e instanceof PortletException)
|
||||
{
|
||||
throw (PortletException)e;
|
||||
}
|
||||
else if (e instanceof IOException)
|
||||
{
|
||||
throw (IOException)e;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new PortletException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.apache.myfaces.portlet.MyFacesGenericPortlet#facesRender(javax.portlet.RenderRequest, javax.portlet.RenderResponse)
|
||||
*/
|
||||
protected void facesRender(RenderRequest request, RenderResponse response)
|
||||
throws PortletException, IOException
|
||||
{
|
||||
Application.setInPortalServer(true);
|
||||
|
||||
if (request.getParameter(ERROR_OCCURRED) != null)
|
||||
{
|
||||
String errorPage = Application.getErrorPage(getPortletContext());
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("An error has occurred, redirecting to error page: " + errorPage);
|
||||
|
||||
response.setContentType("text/html");
|
||||
PortletRequestDispatcher dispatcher = getPortletContext().getRequestDispatcher(errorPage);
|
||||
dispatcher.include(request, response);
|
||||
}
|
||||
else
|
||||
{
|
||||
// if we have no User object in the session then a timeout must have occured
|
||||
// use the viewId to check that we are not already on the login page
|
||||
String viewId = request.getParameter(VIEW_ID);
|
||||
User user = (User)request.getPortletSession().getAttribute(AuthenticationHelper.AUTHENTICATION_USER);
|
||||
if (user == null && (viewId == null || viewId.equals(getLoginPage()) == false))
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("No valid login, requesting login page. ViewId: " + viewId);
|
||||
|
||||
// login page redirect
|
||||
response.setContentType("text/html");
|
||||
request.getPortletSession().setAttribute(PortletUtil.PORTLET_REQUEST_FLAG, "true");
|
||||
nonFacesRequest(request, response);
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
if (user != null)
|
||||
{
|
||||
// setup the authentication context
|
||||
WebApplicationContext ctx = (WebApplicationContext)getPortletContext().getAttribute(
|
||||
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
|
||||
AuthenticationService auth = (AuthenticationService)ctx.getBean("authenticationService");
|
||||
auth.validate(user.getTicket());
|
||||
}
|
||||
|
||||
// Set the current locale
|
||||
I18NUtil.setLocale(Application.getLanguage(request.getPortletSession()));
|
||||
|
||||
// do the normal JSF processing
|
||||
super.facesRender(request, response);
|
||||
}
|
||||
catch (Throwable e)
|
||||
{
|
||||
if (getErrorPage() != null)
|
||||
{
|
||||
handleError(request, response, e);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.warn("No error page configured, re-throwing exception");
|
||||
|
||||
if (e instanceof PortletException)
|
||||
{
|
||||
throw (PortletException)e;
|
||||
}
|
||||
else if (e instanceof IOException)
|
||||
{
|
||||
throw (IOException)e;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new PortletException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles errors that occur during a process action request
|
||||
*/
|
||||
private void handleError(ActionRequest request, ActionResponse response, Throwable error)
|
||||
throws PortletException, IOException
|
||||
{
|
||||
// get the error bean from the session and set the error that occurred.
|
||||
PortletSession session = request.getPortletSession();
|
||||
ErrorBean errorBean = (ErrorBean)session.getAttribute(ErrorBean.ERROR_BEAN_NAME,
|
||||
PortletSession.PORTLET_SCOPE);
|
||||
if (errorBean == null)
|
||||
{
|
||||
errorBean = new ErrorBean();
|
||||
session.setAttribute(ErrorBean.ERROR_BEAN_NAME, errorBean, PortletSession.PORTLET_SCOPE);
|
||||
}
|
||||
errorBean.setLastError(error);
|
||||
|
||||
response.setRenderParameter(ERROR_OCCURRED, "true");
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles errors that occur during a render request
|
||||
*/
|
||||
private void handleError(RenderRequest request, RenderResponse response, Throwable error)
|
||||
throws PortletException, IOException
|
||||
{
|
||||
// get the error bean from the session and set the error that occurred.
|
||||
PortletSession session = request.getPortletSession();
|
||||
ErrorBean errorBean = (ErrorBean)session.getAttribute(ErrorBean.ERROR_BEAN_NAME,
|
||||
PortletSession.PORTLET_SCOPE);
|
||||
if (errorBean == null)
|
||||
{
|
||||
errorBean = new ErrorBean();
|
||||
session.setAttribute(ErrorBean.ERROR_BEAN_NAME, errorBean, PortletSession.PORTLET_SCOPE);
|
||||
}
|
||||
errorBean.setLastError(error);
|
||||
|
||||
// if the faces context is available set the current view to the browse page
|
||||
// so that the error page goes back to the application (rather than going back
|
||||
// to the same page which just throws the error again meaning we can never leave
|
||||
// the error page)
|
||||
FacesContext context = FacesContext.getCurrentInstance();
|
||||
if (context != null)
|
||||
{
|
||||
ViewHandler viewHandler = context.getApplication().getViewHandler();
|
||||
// TODO: configure the portlet error return page
|
||||
UIViewRoot view = viewHandler.createView(context, "/jsp/browse/browse.jsp");
|
||||
context.setViewRoot(view);
|
||||
}
|
||||
|
||||
// get the error page and include that instead
|
||||
String errorPage = getErrorPage();
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("An error has occurred, redirecting to error page: " + errorPage);
|
||||
|
||||
response.setContentType("text/html");
|
||||
PortletRequestDispatcher dispatcher = getPortletContext().getRequestDispatcher(errorPage);
|
||||
dispatcher.include(request, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Retrieves the configured login page
|
||||
*/
|
||||
private String getLoginPage()
|
||||
{
|
||||
if (this.loginPage == null)
|
||||
{
|
||||
this.loginPage = Application.getLoginPage(getPortletContext());
|
||||
}
|
||||
|
||||
return this.loginPage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Retrieves the configured error page
|
||||
*/
|
||||
private String getErrorPage()
|
||||
{
|
||||
if (this.errorPage == null)
|
||||
{
|
||||
this.errorPage = Application.getErrorPage(getPortletContext());
|
||||
}
|
||||
|
||||
return this.errorPage;
|
||||
}
|
||||
}
|
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.web.app.servlet;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.faces.webapp.FacesServlet;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.alfresco.web.app.Application;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* Wrapper around standard faces servlet to provide error handling
|
||||
*
|
||||
* @author gavinc
|
||||
*/
|
||||
public class AlfrescoFacesServlet extends FacesServlet
|
||||
{
|
||||
private static Log logger = LogFactory.getLog(AlfrescoFacesServlet.class);
|
||||
|
||||
/**
|
||||
* @see javax.servlet.Servlet#service(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
|
||||
*/
|
||||
public void service(ServletRequest request, ServletResponse response)
|
||||
throws IOException, ServletException
|
||||
{
|
||||
try
|
||||
{
|
||||
super.service(request, response);
|
||||
}
|
||||
catch (Throwable error)
|
||||
{
|
||||
String returnPage = ((HttpServletRequest)request).getRequestURI();
|
||||
|
||||
Application.handleServletError(getServletConfig().getServletContext(), (HttpServletRequest)request,
|
||||
(HttpServletResponse)response, error, logger, returnPage);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.web.app.servlet;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.alfresco.web.app.Application;
|
||||
import org.alfresco.web.bean.LoginBean;
|
||||
|
||||
/**
|
||||
* @author Kevin Roast
|
||||
*
|
||||
* Servlet filter responsible for redirecting to the login page for the Web Client if the user
|
||||
* does not have a valid ticket.
|
||||
* <p>
|
||||
* The current ticker is validated for each page request and the login page is shown if the
|
||||
* ticker has expired.
|
||||
* <p>
|
||||
* Note that this filter is only active when the system is running in a servlet container -
|
||||
* the AlfrescoFacesPortlet will be used for a JSR-168 Portal environment.
|
||||
*/
|
||||
public class AuthenticationFilter implements Filter
|
||||
{
|
||||
/**
|
||||
* @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
|
||||
*/
|
||||
public void init(FilterConfig config) throws ServletException
|
||||
{
|
||||
this.context = config.getServletContext();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)
|
||||
*/
|
||||
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
|
||||
throws IOException, ServletException
|
||||
{
|
||||
HttpServletRequest httpReq = (HttpServletRequest)req;
|
||||
|
||||
// allow the login page to proceed
|
||||
if (httpReq.getRequestURI().endsWith(getLoginPage()) == false)
|
||||
{
|
||||
if (AuthenticationHelper.authenticate(this.context, httpReq, (HttpServletResponse)res))
|
||||
{
|
||||
// continue filter chaining
|
||||
chain.doFilter(req, res);
|
||||
}
|
||||
else
|
||||
{
|
||||
// failed to authenticate - save redirect URL for after login process
|
||||
httpReq.getSession().setAttribute(LoginBean.LOGIN_REDIRECT_KEY, httpReq.getRequestURI());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// continue filter chaining
|
||||
chain.doFilter(req, res);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.servlet.Filter#destroy()
|
||||
*/
|
||||
public void destroy()
|
||||
{
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The login page url
|
||||
*/
|
||||
private String getLoginPage()
|
||||
{
|
||||
if (this.loginPage == null)
|
||||
{
|
||||
this.loginPage = Application.getLoginPage(this.context);
|
||||
}
|
||||
|
||||
return this.loginPage;
|
||||
}
|
||||
|
||||
|
||||
private String loginPage = null;
|
||||
|
||||
private ServletContext context;
|
||||
}
|
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.web.app.servlet;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.alfresco.i18n.I18NUtil;
|
||||
import org.alfresco.repo.security.authentication.AuthenticationException;
|
||||
import org.alfresco.service.cmr.security.AuthenticationService;
|
||||
import org.alfresco.web.app.Application;
|
||||
import org.alfresco.web.app.portlet.AlfrescoFacesPortlet;
|
||||
import org.alfresco.web.bean.repository.User;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
import org.springframework.web.context.support.WebApplicationContextUtils;
|
||||
|
||||
/**
|
||||
* @author Kevin Roast
|
||||
*/
|
||||
public final class AuthenticationHelper
|
||||
{
|
||||
public final static String AUTHENTICATION_USER = "_alfAuthTicket";
|
||||
|
||||
public static boolean authenticate(ServletContext context, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
|
||||
throws IOException
|
||||
{
|
||||
// examine the appropriate session for our User object
|
||||
User user;
|
||||
if (Application.inPortalServer() == false)
|
||||
{
|
||||
user = (User)httpRequest.getSession().getAttribute(AUTHENTICATION_USER);
|
||||
}
|
||||
else
|
||||
{
|
||||
user = (User)httpRequest.getSession().getAttribute(AlfrescoFacesPortlet.MANAGED_BEAN_PREFIX + AUTHENTICATION_USER);
|
||||
}
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
// no user/ticket - redirect to login page
|
||||
httpResponse.sendRedirect(httpRequest.getContextPath() + "/faces" + Application.getLoginPage(context));
|
||||
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// setup the authentication context
|
||||
WebApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(context);
|
||||
AuthenticationService auth = (AuthenticationService)ctx.getBean("authenticationService");
|
||||
auth.validate(user.getTicket());
|
||||
|
||||
// Set the current locale
|
||||
I18NUtil.setLocale(Application.getLanguage(httpRequest.getSession()));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean authenticate(ServletContext context, HttpServletRequest httpRequest, HttpServletResponse httpResponse, String ticket)
|
||||
throws IOException
|
||||
{
|
||||
// setup the authentication context
|
||||
WebApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(context);
|
||||
AuthenticationService auth = (AuthenticationService)ctx.getBean("authenticationService");
|
||||
try
|
||||
{
|
||||
auth.validate(ticket);
|
||||
}
|
||||
catch (AuthenticationException authErr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set the current locale
|
||||
I18NUtil.setLocale(Application.getLanguage(httpRequest.getSession()));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@@ -0,0 +1,297 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.web.app.servlet;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.SocketException;
|
||||
import java.net.URLDecoder;
|
||||
import java.net.URLEncoder;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.alfresco.error.AlfrescoRuntimeException;
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.content.filestore.FileContentReader;
|
||||
import org.alfresco.service.ServiceRegistry;
|
||||
import org.alfresco.service.cmr.repository.ContentReader;
|
||||
import org.alfresco.service.cmr.repository.ContentService;
|
||||
import org.alfresco.service.cmr.repository.MimetypeService;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.StoreRef;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.web.app.Application;
|
||||
import org.alfresco.web.bean.LoginBean;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
import org.springframework.web.context.support.WebApplicationContextUtils;
|
||||
|
||||
/**
|
||||
* Servlet responsible for streaming node content from the repo directly to the response stream.
|
||||
* The appropriate mimetype is calculated based on filename extension.
|
||||
* <p>
|
||||
* The URL to the servlet should be generated thus:
|
||||
* <pre>/alfresco/download/attach/workspace/SpacesStore/0000-0000-0000-0000/myfile.pdf</pre>
|
||||
* or
|
||||
* <pre>/alfresco/download/direct/workspace/SpacesStore/0000-0000-0000-0000/myfile.pdf</pre>
|
||||
* <p>
|
||||
* The store protocol, followed by the store ID, followed by the content Node Id
|
||||
* the last element is used for mimetype calculation and browser default filename.
|
||||
* <p>
|
||||
* The 'attach' or 'direct' element is used to indicate whether to display the stream directly
|
||||
* in the browser or download it as a file attachment.
|
||||
* <p>
|
||||
* By default, the download assumes that the content is on the
|
||||
* {@link org.alfresco.model.ContentModel#PROP_CONTENT content property}.<br>
|
||||
* To retrieve the content of a specific model property, use a 'property' arg, providing the workspace,
|
||||
* node ID AND the qualified name of the property.
|
||||
* <p>
|
||||
* The URL may be followed by a valid ticket argument for authentication: ?ticket=1234567890
|
||||
*
|
||||
* @author Kevin Roast
|
||||
*/
|
||||
public class DownloadContentServlet extends HttpServlet
|
||||
{
|
||||
private static final long serialVersionUID = -4558907921887235966L;
|
||||
|
||||
private static Log logger = LogFactory.getLog(DownloadContentServlet.class);
|
||||
|
||||
private static final String DOWNLOAD_URL = "/download/attach/{0}/{1}/{2}/{3}";
|
||||
private static final String BROWSER_URL = "/download/direct/{0}/{1}/{2}/{3}";
|
||||
|
||||
private static final String MIMETYPE_OCTET_STREAM = "application/octet-stream";
|
||||
|
||||
private static final String MSG_ERROR_CONTENT_MISSING = "error_content_missing";
|
||||
|
||||
private static final String ARG_PROPERTY = "property";
|
||||
private static final String ARG_ATTACH = "attach";
|
||||
private static final String ARG_TICKET = "ticket";
|
||||
|
||||
/**
|
||||
* @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
|
||||
*/
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse res)
|
||||
throws ServletException, IOException
|
||||
{
|
||||
ServletOutputStream out = res.getOutputStream();
|
||||
|
||||
try
|
||||
{
|
||||
// The URL contains multiple parts
|
||||
// /alfresco/download/attach/workspace/SpacesStore/0000-0000-0000-0000/myfile.pdf
|
||||
// the protocol, followed by the store, followed by the Id
|
||||
// the last part is only used for mimetype and browser use
|
||||
// may be followed by valid ticket for pre-authenticated usage: ?ticket=1234567890
|
||||
String uri = req.getRequestURI();
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Processing URL: " + uri + (req.getQueryString() != null ? ("?" + req.getQueryString()) : ""));
|
||||
|
||||
// see if a ticket has been supplied
|
||||
String ticket = req.getParameter(ARG_TICKET);
|
||||
if (ticket == null || ticket.length() == 0)
|
||||
{
|
||||
if (AuthenticationHelper.authenticate(getServletContext(), req, res) == false)
|
||||
{
|
||||
// authentication failed - no point returning the content as we haven't logged in yet
|
||||
// so end servlet execution and save the URL so the login page knows what to do later
|
||||
req.getSession().setAttribute(LoginBean.LOGIN_REDIRECT_KEY, uri);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AuthenticationHelper.authenticate(getServletContext(), req, res, ticket);
|
||||
}
|
||||
|
||||
// TODO: add compression here?
|
||||
// see http://servlets.com/jservlet2/examples/ch06/ViewResourceCompress.java for example
|
||||
// only really needed if we don't use the built in compression of the servlet container
|
||||
StringTokenizer t = new StringTokenizer(uri, "/");
|
||||
if (t.countTokens() < 7)
|
||||
{
|
||||
throw new IllegalArgumentException("Download URL did not contain all required args: " + uri);
|
||||
}
|
||||
|
||||
t.nextToken(); // skip web app name
|
||||
t.nextToken(); // skip servlet name
|
||||
|
||||
String attachToken = t.nextToken();
|
||||
boolean attachment = attachToken.equals(ARG_ATTACH);
|
||||
|
||||
StoreRef storeRef = new StoreRef(t.nextToken(), t.nextToken());
|
||||
String id = t.nextToken();
|
||||
String filename = t.nextToken();
|
||||
|
||||
// get property qualified name
|
||||
QName propertyQName = null;
|
||||
String property = req.getParameter(ARG_PROPERTY);
|
||||
if (property == null || property.length() == 0)
|
||||
{
|
||||
propertyQName = ContentModel.PROP_CONTENT;
|
||||
}
|
||||
else
|
||||
{
|
||||
propertyQName = QName.createQName(property);
|
||||
}
|
||||
|
||||
NodeRef nodeRef = new NodeRef(storeRef, id);
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("Found NodeRef: " + nodeRef.toString());
|
||||
logger.debug("Will use filename: " + filename);
|
||||
logger.debug("For property: " + propertyQName);
|
||||
logger.debug("With attachment mode: " + attachment);
|
||||
}
|
||||
|
||||
if (attachment == true)
|
||||
{
|
||||
// set header based on filename - will force a Save As from the browse if it doesn't recognise it
|
||||
// this is better than the default response of the browse trying to display the contents!
|
||||
// TODO: make this configurable - and check it does not prevent streaming of large files
|
||||
res.setHeader("Content-Disposition", "attachment;filename=\"" + URLDecoder.decode(filename, "UTF-8") + '"');
|
||||
}
|
||||
|
||||
// get the services we need to retrieve the content
|
||||
WebApplicationContext context = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
|
||||
ServiceRegistry serviceRegistry = (ServiceRegistry)context.getBean(ServiceRegistry.SERVICE_REGISTRY);
|
||||
ContentService contentService = serviceRegistry.getContentService();
|
||||
|
||||
// get the content reader
|
||||
ContentReader reader = contentService.getReader(nodeRef, propertyQName);
|
||||
// ensure that it is safe to use
|
||||
reader = FileContentReader.getSafeContentReader(
|
||||
reader,
|
||||
Application.getMessage(req.getSession(), MSG_ERROR_CONTENT_MISSING),
|
||||
nodeRef, reader);
|
||||
|
||||
String mimetype = reader.getMimetype();
|
||||
// fall back if unable to resolve mimetype property
|
||||
if (mimetype == null || mimetype.length() == 0)
|
||||
{
|
||||
MimetypeService mimetypeMap = serviceRegistry.getMimetypeService();
|
||||
mimetype = MIMETYPE_OCTET_STREAM;
|
||||
int extIndex = filename.lastIndexOf('.');
|
||||
if (extIndex != -1)
|
||||
{
|
||||
String ext = filename.substring(extIndex + 1);
|
||||
String mt = mimetypeMap.getMimetypesByExtension().get(ext);
|
||||
if (mt != null)
|
||||
{
|
||||
mimetype = mt;
|
||||
}
|
||||
}
|
||||
}
|
||||
res.setContentType(mimetype);
|
||||
|
||||
// get the content and stream directly to the response output stream
|
||||
// assuming the repo is capable of streaming in chunks, this should allow large files
|
||||
// to be streamed directly to the browser response stream.
|
||||
try
|
||||
{
|
||||
reader.getContent( res.getOutputStream() );
|
||||
}
|
||||
catch (SocketException e)
|
||||
{
|
||||
if (e.getMessage().contains("ClientAbortException"))
|
||||
{
|
||||
// the client cut the connection - our mission was accomplished apart from a little error message
|
||||
logger.error("Client aborted stream read:\n node: " + nodeRef + "\n content: " + reader);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Throwable err)
|
||||
{
|
||||
throw new AlfrescoRuntimeException("Error during download content servlet processing: " + err.getMessage(), err);
|
||||
}
|
||||
finally
|
||||
{
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to generate a URL to a content node for downloading content from the server.
|
||||
* The content is supplied as an HTTP1.1 attachment to the response. This generally means
|
||||
* a browser should prompt the user to save the content to specified location.
|
||||
*
|
||||
* @param ref NodeRef of the content node to generate URL for (cannot be null)
|
||||
* @param name File name to return in the URL (cannot be null)
|
||||
*
|
||||
* @return URL to download the content from the specified node
|
||||
*/
|
||||
public final static String generateDownloadURL(NodeRef ref, String name)
|
||||
{
|
||||
String url = null;
|
||||
|
||||
try
|
||||
{
|
||||
url = MessageFormat.format(DOWNLOAD_URL, new Object[] {
|
||||
ref.getStoreRef().getProtocol(),
|
||||
ref.getStoreRef().getIdentifier(),
|
||||
ref.getId(),
|
||||
URLEncoder.encode(name, "US-ASCII") } );
|
||||
}
|
||||
catch (UnsupportedEncodingException uee)
|
||||
{
|
||||
throw new AlfrescoRuntimeException("Failed to encode content URL for node: " + ref, uee);
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to generate a URL to a content node for downloading content from the server.
|
||||
* The content is supplied directly in the reponse. This generally means a browser will
|
||||
* attempt to open the content directly if possible, else it will prompt to save the file.
|
||||
*
|
||||
* @param ref NodeRef of the content node to generate URL for (cannot be null)
|
||||
* @param name File name to return in the URL (cannot be null)
|
||||
*
|
||||
* @return URL to download the content from the specified node
|
||||
*/
|
||||
public final static String generateBrowserURL(NodeRef ref, String name)
|
||||
{
|
||||
String url = null;
|
||||
|
||||
try
|
||||
{
|
||||
url = MessageFormat.format(BROWSER_URL, new Object[] {
|
||||
ref.getStoreRef().getProtocol(),
|
||||
ref.getStoreRef().getIdentifier(),
|
||||
ref.getId(),
|
||||
URLEncoder.encode(name, "US-ASCII") } );
|
||||
}
|
||||
catch (UnsupportedEncodingException uee)
|
||||
{
|
||||
throw new AlfrescoRuntimeException("Failed to encode content URL for node: " + ref, uee);
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
}
|
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.web.app.servlet;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.alfresco.web.bean.LoginBean;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* Servlet allowing external URL access to various global JSF views in the Web Client.
|
||||
* <p>
|
||||
* The servlet accepts a well formed URL that can easily be generated from a Content or Space NodeRef.
|
||||
* The URL also specifies the JSF "outcome" to be executed which provides the correct JSF View to be
|
||||
* displayed. The JSF "outcome" must equate to a global navigation rule or it will not be displayed.
|
||||
* Servlet URL is of the form:
|
||||
* <p>
|
||||
* <code>http://<server>/alfresco/navigate/<outcome>[/<workspace>/<store>/<nodeId>]</code> or <br/>
|
||||
* <code>http://<server>/alfresco/navigate/<outcome>[/webdav/<path/to/node>]</code>
|
||||
* </p>
|
||||
*
|
||||
* @author Kevin Roast
|
||||
*/
|
||||
public class ExternalAccessServlet extends HttpServlet
|
||||
{
|
||||
private static final long serialVersionUID = -4118907921337237802L;
|
||||
|
||||
private static Log logger = LogFactory.getLog(ExternalAccessServlet.class);
|
||||
|
||||
|
||||
/**
|
||||
* @see javax.servlet.http.HttpServlet#service(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
|
||||
*/
|
||||
protected void service(HttpServletRequest req, HttpServletResponse res)
|
||||
throws ServletException, IOException
|
||||
{
|
||||
boolean alreadyAuthenticated = AuthenticationHelper.authenticate(getServletContext(), req, res);
|
||||
|
||||
// The URL contains multiple parts
|
||||
// /alfresco/navigate/<outcome>
|
||||
String uri = req.getRequestURI();
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Processing URL: " + uri);
|
||||
|
||||
StringTokenizer t = new StringTokenizer(uri, "/");
|
||||
int count = t.countTokens();
|
||||
if (count < 3)
|
||||
{
|
||||
throw new IllegalArgumentException("Externally addressable URL did not contain all required args: " + uri);
|
||||
}
|
||||
t.nextToken(); // skip web app name
|
||||
t.nextToken(); // skip servlet name
|
||||
|
||||
String outcome = t.nextToken();
|
||||
|
||||
// get rest of the tokens
|
||||
String[] tokens = new String[count - 3];
|
||||
for (int i=0; i<count - 3; i++)
|
||||
{
|
||||
tokens[i] = t.nextToken();
|
||||
}
|
||||
|
||||
// set the session variable so the login bean knows which outcome to use
|
||||
req.getSession().setAttribute(LoginBean.LOGIN_OUTCOME_KEY, outcome);
|
||||
|
||||
// set the args if any
|
||||
req.getSession().setAttribute(LoginBean.LOGIN_OUTCOME_ARGS, tokens);
|
||||
|
||||
if (alreadyAuthenticated)
|
||||
{
|
||||
// clear the User object from the Session - this will force a relogin
|
||||
// we do this so the outcome from the login page can then be changed
|
||||
req.getSession().removeAttribute(AuthenticationHelper.AUTHENTICATION_USER);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Removing User session - will redirect via login page...");
|
||||
|
||||
// redirect to root URL will force the login page to appear via the Authentication Filter
|
||||
res.sendRedirect(req.getContextPath());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a URL to the External Access Servlet.
|
||||
* Allows access to JSF views (via an "outcome" ID) from external URLs.
|
||||
*
|
||||
* @param outcome
|
||||
* @param args
|
||||
*
|
||||
* @return URL
|
||||
*/
|
||||
public final static String generateExternalURL(String outcome, String args)
|
||||
{
|
||||
if (args == null)
|
||||
{
|
||||
return MessageFormat.format(EXTERNAL_URL, new Object[] {outcome} );
|
||||
}
|
||||
else
|
||||
{
|
||||
return MessageFormat.format(EXTERNAL_URL_ARGS, new Object[] {outcome, args} );
|
||||
}
|
||||
}
|
||||
|
||||
// example: http://<server>/alfresco/navigate/<outcome>[/<workspace>/<store>/<nodeId>]
|
||||
private static final String EXTERNAL_URL = "/navigate/{0}";
|
||||
private static final String EXTERNAL_URL_ARGS = "/navigate/{0}/{1}";
|
||||
}
|
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.web.app.servlet;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
|
||||
import org.alfresco.web.app.Application;
|
||||
|
||||
|
||||
/**
|
||||
* Filter that determines whether the application is running inside a portal
|
||||
* server or servlet engine. The fact that this filter gets called means
|
||||
* the application is running inside a servlet engine.
|
||||
*
|
||||
* @author gavinc
|
||||
*/
|
||||
public class ModeDetectionFilter implements Filter
|
||||
{
|
||||
/**
|
||||
* @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
|
||||
*/
|
||||
public void init(FilterConfig config) throws ServletException
|
||||
{
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)
|
||||
*/
|
||||
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
|
||||
throws IOException, ServletException
|
||||
{
|
||||
// as we get here means we are inside a servlet engine as portal servers
|
||||
// do not support the calling of filters yet
|
||||
Application.setInPortalServer(false);
|
||||
|
||||
// continue filter chaining
|
||||
chain.doFilter(req, res);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.servlet.Filter#destroy()
|
||||
*/
|
||||
public void destroy()
|
||||
{
|
||||
// nothing to do
|
||||
}
|
||||
}
|
@@ -0,0 +1,274 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.web.app.servlet;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.SocketException;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import javax.transaction.UserTransaction;
|
||||
|
||||
import org.alfresco.error.AlfrescoRuntimeException;
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.template.DateCompareMethod;
|
||||
import org.alfresco.repo.template.HasAspectMethod;
|
||||
import org.alfresco.repo.template.I18NMessageMethod;
|
||||
import org.alfresco.service.ServiceRegistry;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.service.cmr.repository.StoreRef;
|
||||
import org.alfresco.service.cmr.repository.TemplateException;
|
||||
import org.alfresco.service.cmr.repository.TemplateImageResolver;
|
||||
import org.alfresco.service.cmr.repository.TemplateNode;
|
||||
import org.alfresco.service.cmr.repository.TemplateService;
|
||||
import org.alfresco.web.app.Application;
|
||||
import org.alfresco.web.bean.LoginBean;
|
||||
import org.alfresco.web.bean.repository.Repository;
|
||||
import org.alfresco.web.bean.repository.User;
|
||||
import org.alfresco.web.ui.common.Utils;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
import org.springframework.web.context.support.WebApplicationContextUtils;
|
||||
|
||||
/**
|
||||
* Servlet responsible for streaming content from a template processed against a node directly
|
||||
* to the response stream.
|
||||
* <p>
|
||||
* The URL to the servlet should be generated thus:
|
||||
* <pre>/alfresco/template/workspace/SpacesStore/0000-0000-0000-0000
|
||||
* or
|
||||
* <pre>/alfresco/template/workspace/SpacesStore/0000-0000-0000-0000/workspace/SpacesStore/0000-0000-0000-0000
|
||||
* <p>
|
||||
* The store protocol, followed by the store ID, followed by the content Node Id used to
|
||||
* identify the node to execute the default template for. The second set of elements encode
|
||||
* the store and node Id of the template to used if a default is not set or not requested.
|
||||
* <p>
|
||||
* The URL may be followed by a valid 'ticket' argument for authentication: ?ticket=1234567890
|
||||
* <br>
|
||||
* And may be followed by a 'mimetype' argument specifying the mimetype to return the result as
|
||||
* on the stream. Otherwise it is assumed that HTML is the default response mimetype.
|
||||
*
|
||||
* @author Kevin Roast
|
||||
*/
|
||||
public class TemplateContentServlet extends HttpServlet
|
||||
{
|
||||
private static final String MIMETYPE_HTML = "text/html";
|
||||
|
||||
private static final long serialVersionUID = -4123407921997235977L;
|
||||
|
||||
private static Log logger = LogFactory.getLog(TemplateContentServlet.class);
|
||||
|
||||
private static final String DEFAULT_URL = "/template/{0}/{1}/{2}";
|
||||
private static final String TEMPALTE_URL = "/template/{0}/{1}/{2}/{3}/{4}/{5}";
|
||||
|
||||
private static final String MSG_ERROR_CONTENT_MISSING = "error_content_missing";
|
||||
|
||||
private static final String ARG_TICKET = "ticket";
|
||||
private static final String ARG_MIMETYPE = "mimetype";
|
||||
|
||||
/**
|
||||
* @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
|
||||
*/
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse res)
|
||||
throws ServletException, IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
String uri = req.getRequestURI();
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Processing URL: " + uri + (req.getQueryString() != null ? ("?" + req.getQueryString()) : ""));
|
||||
|
||||
// see if a ticket has been supplied
|
||||
String ticket = req.getParameter(ARG_TICKET);
|
||||
if (ticket == null || ticket.length() == 0)
|
||||
{
|
||||
if (AuthenticationHelper.authenticate(getServletContext(), req, res) == false)
|
||||
{
|
||||
// authentication failed - no point returning the content as we haven't logged in yet
|
||||
// so end servlet execution and save the URL so the login page knows what to do later
|
||||
req.getSession().setAttribute(LoginBean.LOGIN_REDIRECT_KEY, uri);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AuthenticationHelper.authenticate(getServletContext(), req, res, ticket);
|
||||
}
|
||||
|
||||
StringTokenizer t = new StringTokenizer(uri, "/");
|
||||
int tokenCount = t.countTokens();
|
||||
if (tokenCount < 5)
|
||||
{
|
||||
throw new IllegalArgumentException("Download URL did not contain all required args: " + uri);
|
||||
}
|
||||
|
||||
t.nextToken(); // skip web app name
|
||||
t.nextToken(); // skip servlet name
|
||||
|
||||
// get NodeRef to the content
|
||||
StoreRef storeRef = new StoreRef(t.nextToken(), t.nextToken());
|
||||
NodeRef nodeRef = new NodeRef(storeRef, t.nextToken());
|
||||
|
||||
// get NodeRef to the template if supplied
|
||||
NodeRef templateRef = null;
|
||||
if (tokenCount == 8)
|
||||
{
|
||||
storeRef = new StoreRef(t.nextToken(), t.nextToken());
|
||||
templateRef = new NodeRef(storeRef, t.nextToken());
|
||||
}
|
||||
|
||||
// get the services we need to retrieve the content
|
||||
WebApplicationContext context = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
|
||||
ServiceRegistry serviceRegistry = (ServiceRegistry)context.getBean(ServiceRegistry.SERVICE_REGISTRY);
|
||||
NodeService nodeService = serviceRegistry.getNodeService();
|
||||
TemplateService templateService = serviceRegistry.getTemplateService();
|
||||
|
||||
UserTransaction txn = null;
|
||||
try
|
||||
{
|
||||
txn = serviceRegistry.getTransactionService().getUserTransaction(true);
|
||||
txn.begin();
|
||||
|
||||
// if template not supplied, then use the default against the node
|
||||
if (templateRef == null)
|
||||
{
|
||||
if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_TEMPLATABLE))
|
||||
{
|
||||
templateRef = (NodeRef)nodeService.getProperty(nodeRef, ContentModel.PROP_TEMPLATE);
|
||||
}
|
||||
if (templateRef == null)
|
||||
{
|
||||
throw new TemplateException("Template reference not set against node or not supplied in URL.");
|
||||
}
|
||||
}
|
||||
|
||||
// create the model - put the supplied noderef in as space/document as appropriate
|
||||
Object model = getModel(serviceRegistry, req.getSession(), nodeRef);
|
||||
|
||||
// process the template against the node content directly to the response output stream
|
||||
// assuming the repo is capable of streaming in chunks, this should allow large files
|
||||
// to be streamed directly to the browser response stream.
|
||||
try
|
||||
{
|
||||
templateService.processTemplate(
|
||||
null,
|
||||
templateRef.toString(),
|
||||
model,
|
||||
res.getWriter());
|
||||
|
||||
// commit the transaction
|
||||
txn.commit();
|
||||
}
|
||||
catch (SocketException e)
|
||||
{
|
||||
if (e.getMessage().contains("ClientAbortException"))
|
||||
{
|
||||
// the client cut the connection - our mission was accomplished apart from a little error message
|
||||
logger.error("Client aborted stream read:\n node: " + nodeRef + "\n template: " + templateRef);
|
||||
try { if (txn != null) {txn.rollback();} } catch (Exception tex) {}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Throwable txnErr)
|
||||
{
|
||||
try { if (txn != null) {txn.rollback();} } catch (Exception tex) {}
|
||||
}
|
||||
}
|
||||
catch (Throwable err)
|
||||
{
|
||||
throw new AlfrescoRuntimeException("Error during download content servlet processing: " + err.getMessage(), err);
|
||||
}
|
||||
}
|
||||
|
||||
private Object getModel(ServiceRegistry services, HttpSession session, NodeRef nodeRef)
|
||||
{
|
||||
// create FreeMarker default model and merge
|
||||
Map root = new HashMap(11, 1.0f);
|
||||
|
||||
// supply the CompanyHome space as "companyhome"
|
||||
NodeRef companyRootRef = new NodeRef(Repository.getStoreRef(), Application.getCompanyRootId());
|
||||
TemplateNode companyRootNode = new TemplateNode(companyRootRef, services, imageResolver);
|
||||
root.put("companyhome", companyRootNode);
|
||||
|
||||
// supply the users Home Space as "userhome"
|
||||
User user = Application.getCurrentUser(session);
|
||||
NodeRef userRootRef = new NodeRef(Repository.getStoreRef(), user.getHomeSpaceId());
|
||||
TemplateNode userRootNode = new TemplateNode(userRootRef, services, imageResolver);
|
||||
root.put("userhome", userRootNode);
|
||||
|
||||
// put the current NodeRef in as "space" and "document"
|
||||
TemplateNode node = new TemplateNode(nodeRef, services, imageResolver);
|
||||
root.put("space", node);
|
||||
root.put("document", node);
|
||||
|
||||
// supply the current user Node as "person"
|
||||
root.put("person", new TemplateNode(user.getPerson(), services, imageResolver));
|
||||
|
||||
// current date/time is useful to have and isn't supplied by FreeMarker by default
|
||||
root.put("date", new Date());
|
||||
|
||||
// add custom method objects
|
||||
root.put("hasAspect", new HasAspectMethod());
|
||||
root.put("message", new I18NMessageMethod());
|
||||
root.put("dateCompare", new DateCompareMethod());
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
/** Template Image resolver helper */
|
||||
private TemplateImageResolver imageResolver = new TemplateImageResolver()
|
||||
{
|
||||
public String resolveImagePathForName(String filename, boolean small)
|
||||
{
|
||||
return Utils.getFileTypeImage(filename, small);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper to generate a URL to a content node for downloading content from the server.
|
||||
* The content is supplied as an HTTP1.1 attachment to the response. This generally means
|
||||
* a browser should prompt the user to save the content to specified location.
|
||||
*
|
||||
* @param ref NodeRef of the content node to generate URL for (cannot be null)
|
||||
* @param name File name to return in the URL (cannot be null)
|
||||
*
|
||||
* @return URL to download the content from the specified node
|
||||
*/
|
||||
public final static String generateURL(NodeRef ref, String name)
|
||||
{
|
||||
return MessageFormat.format(DEFAULT_URL, new Object[] {
|
||||
ref.getStoreRef().getProtocol(),
|
||||
ref.getStoreRef().getIdentifier(),
|
||||
ref.getId() } );
|
||||
}
|
||||
}
|
141
source/java/org/alfresco/web/app/servlet/UploadFileServlet.java
Normal file
141
source/java/org/alfresco/web/app/servlet/UploadFileServlet.java
Normal file
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.web.app.servlet;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import org.alfresco.error.AlfrescoRuntimeException;
|
||||
import org.alfresco.util.TempFileProvider;
|
||||
import org.alfresco.web.app.Application;
|
||||
import org.alfresco.web.bean.FileUploadBean;
|
||||
import org.apache.commons.fileupload.FileItem;
|
||||
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
|
||||
import org.apache.commons.fileupload.servlet.ServletFileUpload;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* Servlet that takes a file uploaded via a browser and represents it as an
|
||||
* UploadFileBean in the session
|
||||
*
|
||||
* @author gavinc
|
||||
*/
|
||||
public class UploadFileServlet extends HttpServlet
|
||||
{
|
||||
private static final long serialVersionUID = -5482538466491052873L;
|
||||
private static Log logger = LogFactory.getLog(UploadFileServlet.class);
|
||||
|
||||
/**
|
||||
* @see javax.servlet.http.HttpServlet#service(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
|
||||
*/
|
||||
protected void service(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException
|
||||
{
|
||||
String returnPage = null;
|
||||
boolean isMultipart = ServletFileUpload.isMultipartContent(request);
|
||||
|
||||
try
|
||||
{
|
||||
AuthenticationHelper.authenticate(getServletContext(), request, response);
|
||||
|
||||
if (isMultipart == false)
|
||||
{
|
||||
throw new AlfrescoRuntimeException("This servlet can only be used to handle file upload requests, make" +
|
||||
"sure you have set the enctype attribute on your form to multipart/form-data");
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Uploading servlet servicing...");
|
||||
|
||||
HttpSession session = request.getSession();
|
||||
ServletFileUpload upload = new ServletFileUpload(new DiskFileItemFactory());
|
||||
List<FileItem> fileItems = upload.parseRequest(request);
|
||||
|
||||
Iterator<FileItem> iter = fileItems.iterator();
|
||||
FileUploadBean bean = new FileUploadBean();
|
||||
while(iter.hasNext())
|
||||
{
|
||||
FileItem item = iter.next();
|
||||
if(item.isFormField())
|
||||
{
|
||||
if (item.getFieldName().equalsIgnoreCase("return-page"))
|
||||
{
|
||||
returnPage = item.getString();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
String filename = item.getName();
|
||||
if (filename != null && filename.length() != 0)
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Processing uploaded file: " + filename);
|
||||
|
||||
// workaround a bug in IE where the full path is returned
|
||||
// IE is only available for Windows so only check for the Windows path separator
|
||||
int idx = filename.lastIndexOf('\\');
|
||||
|
||||
if (idx == -1)
|
||||
{
|
||||
// if there is no windows path separator check for *nix
|
||||
idx = filename.lastIndexOf('/');
|
||||
}
|
||||
|
||||
if (idx != -1)
|
||||
{
|
||||
filename = filename.substring(idx + File.separator.length());
|
||||
}
|
||||
|
||||
File tempFile = TempFileProvider.createTempFile("alfresco", ".upload");
|
||||
item.write(tempFile);
|
||||
bean.setFile(tempFile);
|
||||
bean.setFileName(filename);
|
||||
bean.setFilePath(tempFile.getAbsolutePath());
|
||||
session.setAttribute(FileUploadBean.FILE_UPLOAD_BEAN_NAME, bean);
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Temp file: " + tempFile.getAbsolutePath() + " created from upload filename: " + filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (returnPage == null || returnPage.length() == 0)
|
||||
{
|
||||
throw new AlfrescoRuntimeException("return-page parameter has not been supplied");
|
||||
}
|
||||
|
||||
// finally redirect
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Upload servicing complete, redirecting to: " + returnPage);
|
||||
|
||||
response.sendRedirect(returnPage);
|
||||
}
|
||||
catch (Throwable error)
|
||||
{
|
||||
Application.handleServletError(getServletContext(), (HttpServletRequest)request,
|
||||
(HttpServletResponse)response, error, logger, returnPage);
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user