/*******************************************************************************
* Imixs Workflow
* Copyright (C) 2001, 2011 Imixs Software Solutions GmbH,
* http://www.imixs.com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You can receive a copy of the GNU General Public
* License at http://www.gnu.org/licenses/gpl.html
*
* Project:
* http://www.imixs.org
* http://java.net/projects/imixs-workflow
*
* Contributors:
* Imixs Software Solutions GmbH - initial API and implementation
* Ralph Soika - Software Developer
*******************************************************************************/
package org.imixs.marty.profile;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.StringTokenizer;
import java.util.logging.Logger;
import javax.annotation.PostConstruct;
import javax.ejb.EJB;
import javax.enterprise.context.SessionScoped;
import javax.enterprise.event.Observes;
import javax.faces.context.FacesContext;
import javax.inject.Inject;
import javax.inject.Named;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.imixs.marty.ejb.ProfileService;
import org.imixs.marty.ejb.security.UserGroupService;
import org.imixs.marty.workflow.WorkflowController;
import org.imixs.marty.workflow.WorkflowEvent;
import org.imixs.workflow.ItemCollection;
import org.imixs.workflow.engine.PropertyService;
import org.imixs.workflow.engine.WorkflowService;
import org.imixs.workflow.exceptions.AccessDeniedException;
import org.imixs.workflow.exceptions.ModelException;
import org.imixs.workflow.exceptions.PluginException;
import org.imixs.workflow.exceptions.ProcessingErrorException;
import org.imixs.workflow.faces.fileupload.FileData;
import org.imixs.workflow.faces.fileupload.FileUploadController;
import org.imixs.workflow.faces.util.LoginController;
/**
* This backing beans handles the Profile entity for the current user and
* provides a application scoped access to all other profiles through the
* ProfileService EJB.
*
* A new user profile will be created automatically if no profile yet exists!
* The user is identified by its principal user name. This name is mapped to the
* attribute txtname.
*
* The UserController provides the user 'locale' and 'language' which is used in
* JSF Pages to display pages using the current user settings.
*
* With the methods mark() and unmark() workitems can be added into the users
* profile favorite list.
*
* @author rsoika
*
*/
@Named("userController")
@SessionScoped
public class UserController implements Serializable {
public final static int MAX_PRIMARY_ENTRIES = 5;
public final static int UPDATE_PROJECT_ACTIVITY_ID = 10;
public final static String DEFAULT_LOCALE = "de_DE";
public final static String COOKIE_LOCALE = "imixs.workflow.locale";
@EJB
protected ProfileService profileService;
@EJB
protected PropertyService propertyService;
@EJB
protected UserGroupService userGroupService;
@EJB
protected WorkflowService workflowService;
@Inject
protected LoginController loginController;
@Inject
protected WorkflowController workflowController;
@Inject
protected FileUploadController fileUploadController;
private static final long serialVersionUID = 1L;
private ItemCollection workitem = null;
private ItemCollection currentProfile = null;
private boolean profileLoaded = false;
private Locale locale;
private static Logger logger = Logger.getLogger(UserController.class.getName());
public UserController() {
super();
}
/**
* The init method is used to load a user profile or automatically create a
* new one if no profile for the user is available. A new Profile will be
* filled with default values.
*
* This method did not use the internal cache of the ProfileService to
* lookup the user profile, to make sure that the entity is uptodate when a
* user logs in.
*
* @throws ProcessingErrorException
* @throws AccessDeniedException
*/
@PostConstruct
public void init() throws AccessDeniedException, ProcessingErrorException {
// test user is logged-in and automatically create profile if no profile
// exists yet
if (this.loginController.isAuthenticated() && !profileLoaded) {
// try to load the profile for the current user
ItemCollection profile = profileService.lookupProfileById(loginController.getUserPrincipal());
if (profile == null) {
try {
profile = profileService.createProfile(loginController.getUserPrincipal(), getLocale().toString());
} catch (RuntimeException | PluginException | ModelException e) {
logger.severe("unable to create profile for userid '" + loginController.getUserPrincipal() + "': "
+ e.getMessage());
// logout user!!
logger.severe("logout current userid '" + loginController.getUserPrincipal() + "'...");
loginController.doLogout(null);
throw new ProcessingErrorException(UserController.class.getName(),
ProcessingErrorException.INVALID_WORKITEM, " unable to create profile!", e);
}
} else {
// check if profile.autoProcessOnLogin is defined
String sAutoProcessID = propertyService.getProperties().getProperty("profile.autoProcessOnLogin");
logger.fine("profile.autoProcessOnLogin=" + sAutoProcessID);
try {
if (sAutoProcessID != null) {
int iActiviyID = Integer.valueOf(sAutoProcessID);
profile.replaceItemValue("$ActivityID", iActiviyID);
logger.fine("[UserController] autoprocess profile with autoProcessOnLogin=" + iActiviyID);
try {
profile = workflowService.processWorkItem(profile);
} catch (PluginException e) {
logger.severe("[UserController] unable to process new profile entity!");
throw new ProcessingErrorException(UserController.class.getName(),
ProcessingErrorException.INVALID_WORKITEM, " unable to process new profile entity!",
e);
} catch (ModelException e) {
logger.severe("[UserController] unable to process new profile entity!");
throw new ProcessingErrorException(UserController.class.getName(),
ProcessingErrorException.INVALID_WORKITEM, " unable to process new profile entity!",
e);
}
}
} catch (NumberFormatException nfe) {
logger.warning(
"[UserController] unable to autoprocess profile with autoProcessOnLogin=" + sAutoProcessID);
}
}
this.setWorkitem(profile);
profileLoaded = true;
// Now reset current locale based on the profile information
updateLocaleFromProfile();
logger.info("profile '" + loginController.getUserPrincipal() + "' initialized.");
}
}
/**
* This method returns the current users userprofile entity. The method
* verifies if the profile was yet loaded. if not the method tries to
* initiate the profile - see method init();
*
* @return
* @throws ProcessingErrorException
* @throws AccessDeniedException
*/
public ItemCollection getWorkitem() throws AccessDeniedException, ProcessingErrorException {
// test if current users profile was loaded
if (!profileLoaded)
init();
if (workitem == null)
workitem = new ItemCollection();
return workitem;
}
public void setWorkitem(ItemCollection aworkitem) {
this.workitem = aworkitem;
}
/**
* This method returns the current user locale. If the user is not logged in
* the method try to get the locale out from the cookie. If no cockie is set
* the method defaults to "de_DE"
*
* @return - ISO Locale format
*/
public Locale getLocale() {
// if no locale is set try to get it from cookie or set default
if (locale == null) {
FacesContext facesContext = FacesContext.getCurrentInstance();
HttpServletRequest request = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext()
.getRequest();
String cookieName = null;
Cookie cookie[] = ((HttpServletRequest) facesContext.getExternalContext().getRequest()).getCookies();
if (cookie != null && cookie.length > 0) {
for (int i = 0; i < cookie.length; i++) {
cookieName = cookie[i].getName();
if (cookieName.equals(COOKIE_LOCALE)) {
String sLocale = cookie[i].getValue();
if (sLocale != null && !"".equals(sLocale)) {
// split locale
StringTokenizer stLocale = new StringTokenizer(sLocale, "_");
if (stLocale.countTokens() == 1) {
// only language variant
String sLang = stLocale.nextToken();
String sCount = sLang.toUpperCase();
locale = new Locale(sLang, sCount);
} else {
// language and country
String sLang = stLocale.nextToken();
String sCount = stLocale.nextToken();
locale = new Locale(sLang, sCount);
}
}
break;
}
}
}
// still no value found? - default to "en"
if (locale == null || "".equals(locale.getLanguage())) {
Locale ldefault = request.getLocale();
if (ldefault != null) {
locale = ldefault;
} else {
locale = new Locale(DEFAULT_LOCALE);
}
}
}
return locale;
}
public void setLocale(Locale alocale) {
if (alocale == null || "".equals(alocale))
locale = new Locale(DEFAULT_LOCALE);
else
this.locale = alocale;
// update cookie
HttpServletResponse response = (HttpServletResponse) FacesContext.getCurrentInstance().getExternalContext()
.getResponse();
HttpServletRequest request = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext()
.getRequest();
Cookie cookieLocale = new Cookie(COOKIE_LOCALE, locale.toString());
cookieLocale.setPath(request.getContextPath());
// 30 days
cookieLocale.setMaxAge(2592000);
response.addCookie(cookieLocale);
}
/**
* returns the user language
*
* @return
*/
public String getLanguage() {
return getLocale().getLanguage();
}
/**
* This method returns a cached cloned version of a user profile for a given
* useraccount. The profile is cached in the current user session
*
* @param aName
* @return
*/
public ItemCollection getProfile(String aAccount) {
if (currentProfile == null || !currentProfile.getItemValueString("txtname").equals(aAccount)) {
currentProfile = profileService.findProfileById(aAccount);
}
return currentProfile;
}
/**
* This method returns the username (displayname) for a useraccount. If no
* Username is set in the profile then we return the useraccount.
*
* @param aName
* @return
*/
public String getUserName(String aAccount) {
// use internal cache
currentProfile = getProfile(aAccount);
if (currentProfile == null) {
return null;
} else {
return currentProfile.getItemValueString("txtuserName");
}
}
/**
* This method returns the email for a useraccount
*
* @param aName
* @return
*/
public String getEmail(String aAccount) {
// use internal cache
currentProfile = getProfile(aAccount);
if (currentProfile == null) {
return null;
} else {
return currentProfile.getItemValueString("txtemail");
}
}
/**
* remmoves the current user icon
*
* @param event
* @throws AccessDeniedException
* @throws ProcessingErrorException
*/
public void removeUserIcon() {
String file = workflowController.getWorkitem().getItemValueString("txtusericon");
workflowController.getWorkitem().removeFile(file);
workflowController.getWorkitem().replaceItemValue("txtusericon", "");
}
/**
* WorkflowEvent listener listens to WORKITEM events to reset the current
* username workitem if processed. The method also updates the user Locale
*
* @param workflowEvent
*
**/
public void onWorkflowEvent(@Observes WorkflowEvent workflowEvent) {
if (workflowEvent == null || workflowEvent.getWorkitem() == null) {
return;
}
String sType = workflowEvent.getWorkitem().getItemValueString("type");
// skip if not a profile...
if (!sType.startsWith("profile"))
return;
if ("profile".equals(sType) && WorkflowEvent.WORKITEM_CHANGED == workflowEvent.getEventType()) {
fileUploadController.reset();
}
// Update usericon and initials
// test icon upload
if ("profile".equals(sType) && WorkflowEvent.WORKITEM_BEFORE_PROCESS == workflowEvent.getEventType()) {
if (fileUploadController != null) {
List<FileData> fileList = fileUploadController.getUploades();
if (fileList != null && fileList.size() > 0) {
while (fileList.size() > 1) {
fileList.remove(0);
}
// we take the first element
FileData file = fileList.get(0);
String filenametest = file.getName().toLowerCase();
if (filenametest.endsWith(".png") || filenametest.endsWith(".gif")
|| filenametest.endsWith(".jpg")) {
logger.info("UserController Icon Upload started: " + file.getName());
workflowEvent.getWorkitem().replaceItemValue("txtusericon", file.getName());
} else {
fileList.remove(0);
logger.info("UserController Icon Upload not supported for: " + file.getName());
}
}
fileUploadController.updateWorkitem(workflowEvent.getWorkitem());
}
}
// discared cached user profile and update locale
if ("profile".equals(sType) && WorkflowEvent.WORKITEM_AFTER_PROCESS == workflowEvent.getEventType()) {
// check if current user profile was processed....
String sName = workflowEvent.getWorkitem().getItemValueString("txtName");
if (sName.equals(this.getWorkitem().getItemValueString("txtName"))) {
logger.info("[UserController] reload current user profile");
setWorkitem(workflowEvent.getWorkitem());
// update locale
updateLocaleFromProfile();
}
}
}
/**
* Returns true if the uniqueid is stored in the profile favorites
*
* @param id
* @return
*/
public boolean isFavorite(String id) {
return getFavoriteIds().contains(id);
}
/**
* Returns a list with all uniqueids stored in the profile favorites
*
* @return
*/
@SuppressWarnings("unchecked")
public List<String> getFavoriteIds() {
if (getWorkitem() == null)
return new ArrayList<String>();
return getWorkitem().getItemValue("txtWorkitemRef");
}
public void addFavorite(String id) {
if (getWorkitem() == null)
return;
List<String> list = getFavoriteIds();
// we expect that the id is in the list-..
if (!list.contains(id)) {
logger.fine("[UserController] add WorkitemRef:" + id);
list.add(id);
workitem.replaceItemValue("txtWorkitemRef", list);
workitem = workflowService.getDocumentService().save(workitem);
}
}
public void removeFavorite(String id) {
if (getWorkitem() == null)
return;
List<String> list = getFavoriteIds();
// we expect that the id is in the list-..
if (list.contains(id)) {
logger.fine("[UserController] remove WorkitemRef:" + id);
list.remove(id);
workitem.replaceItemValue("txtWorkitemRef", list);
workitem = workflowService.getDocumentService().save(workitem);
}
}
/*
* HELPER METHODS
*/
/**
* This method updates user locale stored in the user profile entity to the
* faces context.
*
* @throws ProcessingErrorException
* @throws AccessDeniedException
*
*/
private void updateLocaleFromProfile() throws AccessDeniedException, ProcessingErrorException {
Locale profileLocale = null;
// Verify if Locale is available in profile
String sLocale = getWorkitem().getItemValueString("txtLocale");
if ("".equals(sLocale)) {
// get default value
profileLocale = getLocale();
getWorkitem().replaceItemValue("txtLocale", profileLocale.toString());
} else {
if (sLocale.indexOf('_') > -1) {
String language = sLocale.substring(0, sLocale.indexOf('_'));
String country = sLocale.substring(sLocale.indexOf('_') + 1);
profileLocale = new Locale(language, country);
} else {
profileLocale = new Locale(sLocale);
}
}
logger.fine("update user locale: " + profileLocale);
// reset locale to update cookie
setLocale(profileLocale);
// set locale for context
FacesContext.getCurrentInstance().getViewRoot().setLocale(profileLocale);
}
}