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:
Derek Hulley
2005-12-08 07:13:07 +00:00
commit d051d1153c
920 changed files with 98871 additions and 0 deletions

View 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);
}
}
}

View 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;
}
}

View 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);
}
}
}

View 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;
}
}

View 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 + "$$";
}
}
}

View File

@@ -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();
}

View 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);
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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://&lt;server&gt;/alfresco/navigate/&lt;outcome&gt;[/&lt;workspace&gt;/&lt;store&gt;/&lt;nodeId&gt;]</code> or <br/>
* <code>http://&lt;server&gt;/alfresco/navigate/&lt;outcome&gt;[/webdav/&lt;path/to/node&gt;]</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}";
}

View File

@@ -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
}
}

View File

@@ -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() } );
}
}

View 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);
}
}
}