/** * OLAT - Online Learning and Training<br> * http://www.olat.org * <p> * Licensed under the Apache License, Version 2.0 (the "License"); <br> * you may not use this file except in compliance with the License.<br> * You may obtain a copy of the License at * <p> * http://www.apache.org/licenses/LICENSE-2.0 * <p> * Unless required by applicable law or agreed to in writing,<br> * software distributed under the License is distributed on an "AS IS" BASIS, <br> * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> * See the License for the specific language governing permissions and <br> * limitations under the License. * <p> * frentix GmbH, Switzerland, http://www.frentix.com * <p> */ package org.olat.user.propertyhandlers.ui; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Properties; import java.util.Set; import org.olat.core.configuration.AbstractSpringModule; import org.olat.core.gui.control.Event; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; import org.olat.core.util.coordinate.CoordinatorManager; import org.olat.core.util.event.GenericEventListener; import org.olat.user.UserManager; import org.olat.user.UserPropertiesConfig; import org.olat.user.propertyhandlers.UserPropertiesConfigImpl; import org.olat.user.propertyhandlers.UserPropertyHandler; import org.olat.user.propertyhandlers.UserPropertyUsageContext; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * * Description:<br> * Provides Functionality to modify the UserPropertyConfig Object (from the * UserManager). The UserPropertyConfig-Object is modified with our own config * which can be edited through an admin-gui * * <P> * Initial Date: 24.08.2011 <br> * * @author strentini */ @Service public class UsrPropCfgManager extends AbstractSpringModule implements GenericEventListener { private static final OLog log = Tracing.createLoggerFor(UsrPropCfgManager.class); /* * these properties (the handlers) cannot be deactivated. OLAT depends on them */ private static final String[] NON_DEACTIVABLE_PROPERTIES = { "firstName", "lastName", "email", "emchangeKey", "emailDisabled" }; /* * properties (the handlers) that must be mandatory in all contexts. */ private static final String[] MUST_BE_MANDATORY_PROPERTIES = {"firstName", "lastName","email"}; /* * contexts that are an exception for "MUST-BE-MANDATORIY-PROPERTIES" ;-) * explanation: there are some contexts where the propertyhandlers are used to render search-fields. in these contexts/Forms * none of the properties must be mandatory. * (I know, it's a hack...) * */ private static final String[] EXCEPTIONAL_CONTEXTS_FOR_MUSTBE_MANDATORY_PROPERTIES = { "org.olat.admin.user.UsermanagerUserSearchForm", "org.olat.admin.user.UserSearchForm", "org.olat.user.HomePageConfig" }; private static final String CONF_KEY_ACTUPROP = "activeuserpropertyhandlers"; private static final String CONF_KEY_DACTUPROP = "deactiveuserpropertyhandlers"; private static final String CONF_KEY_PROPGROUP = "_group"; private static final String CONF_KEY_CONTPREFIX = "_hndl_"; private static final String CONF_KEY_CONT_MANDATORY_PREFIX = "mandatory"; private static final String CONF_KEY_CONT_ADMINONLY_PREFIX = "adminonly"; private static final String CONF_KEY_CONT_USERREADONLY_PREFIX = "usrreadonly"; private static final String PROP_DELIMITER = ","; private UsrPropCfgObject cfgObject; private final List<UserPropertyHandler> allUserPropertyHandlersFromXML; private final UserManager userManager; @Autowired public UsrPropCfgManager(CoordinatorManager coordinatorManager, UserManager userManager) { super(coordinatorManager, "com.frentix.olat.admin.userproperties.UsrPropCfgManager", false); this.userManager = userManager; allUserPropertyHandlersFromXML = userManager.getUserPropertiesConfig().getAllUserPropertyHandlers(); } @Override public void init() { getUserPropertiesConfigObject(); // } @Override protected void initFromChangedProperties() { // } /** * returns the current UserPropertiesConfigObject (on first invocation, the * cfgObject is loaded from persistedProperties) * * @return */ public UsrPropCfgObject getUserPropertiesConfigObject() { if (cfgObject == null) { loadModifiedUserPropertiesConfig(createPropertiesFromPersistedProperties()); } return cfgObject; } /** * Resets the UserCongfig to the given Preset * * @param properties */ public void resetToPresetConfig(Properties properties) { loadModifiedUserPropertiesConfig(properties); savePersistedProperties(); } /** * loads our persisted Config and manipulates the Config of the user-manager * (which comes from xml-config) */ private void loadModifiedUserPropertiesConfig(Properties props) { log.info("loading modified UserPropertiesConfig"); UserPropertiesConfig usrMngConfig = userManager.getUserPropertiesConfig(); cfgObject = new UsrPropCfgObject(allUserPropertyHandlersFromXML, usrMngConfig.getUserPropertyUsageContexts()); // now manipulate this cfgObject according to our own config ( from the // persistedProperties) List<String> val_activeHandlers = Arrays.asList(props.getProperty(CONF_KEY_ACTUPROP, "").split(PROP_DELIMITER)); List<String> val_dactiveHandlers = Arrays.asList(props.getProperty(CONF_KEY_DACTUPROP, "").split(PROP_DELIMITER)); for (UserPropertyHandler handler : cfgObject.getPropertyHandlers()) { //modify the groupName of the handler String groupName = props.getProperty(handler.getName() + CONF_KEY_PROPGROUP,null); if(groupName != null){ handler.setGroup(groupName); } //either it is set as an active property, or the property can't be deactivated (email, firstname, lastname, etc.) if (val_activeHandlers.contains(handler.getName()) || !UsrPropCfgManager.canBeDeactivated(handler)) { cfgObject.setHandlerAsActive(handler, true); } else if (!val_dactiveHandlers.contains(handler.getName())) { // this is a new handler (not yet in our own config) // -->set it as active // (note: if you delete persistedProperties-conf-file, all handlers are // "new" and therefore should be active) log.info("UserPropertyHandler "+handler.getName()+" unknown in config, set Property as active."); cfgObject.setHandlerAsActive(handler, true); } } // handle contexts (these are the contexts from xml) for (Entry<String, UserPropertyUsageContext> ctxEntry : cfgObject.getUsageContexts().entrySet()) { UserPropertyUsageContext ctx = ctxEntry.getValue(); String contextName = ctxEntry.getKey(); List<UserPropertyHandler> ctx_allHandlers = new ArrayList<UserPropertyHandler>(); Set<UserPropertyHandler> ctx_mandHandlers = new HashSet<UserPropertyHandler>(); Set<UserPropertyHandler> ctx_adminonlyHandlers = new HashSet<UserPropertyHandler>(); Set<UserPropertyHandler> ctx_usrreadonlyHandlers = new HashSet<UserPropertyHandler>(); String handlerNameInConfig = props.getProperty(contextName, null); if (handlerNameInConfig == null) {// our config doesn't know this context, // leave it as is! log.info("UserPropertyUsageContext "+contextName+" unknown in config, leave Context untouched."); continue; } // this list from the persistedProperties has the correct order of handlers! List<String> val_handlers = Arrays.asList(props.getProperty(contextName + CONF_KEY_CONTPREFIX, "").split(PROP_DELIMITER)); List<String> val_mandatoryHandlers = Arrays.asList(props.getProperty(contextName + CONF_KEY_CONTPREFIX + CONF_KEY_CONT_MANDATORY_PREFIX, "") .split(PROP_DELIMITER)); List<String> val_adminonlyHandlers = Arrays.asList(props.getProperty(contextName + CONF_KEY_CONTPREFIX + CONF_KEY_CONT_ADMINONLY_PREFIX, "") .split(PROP_DELIMITER)); List<String> val_userreadonlyHandlers = Arrays.asList(props.getProperty( contextName + CONF_KEY_CONTPREFIX + CONF_KEY_CONT_USERREADONLY_PREFIX, "").split(PROP_DELIMITER)); for (UserPropertyHandler handler : cfgObject.getPropertyHandlers()) { String handlerName = handler.getName(); if (val_handlers.contains(handlerName) && cfgObject.isActiveHandler(handler)) { ctx_allHandlers.add(handler); //either it is set as mandatory in config or it is one of the property where mandatory must be enforced :) if (val_mandatoryHandlers.contains(handlerName) || !UsrPropCfgManager.canBeOptionalInContext(handler,contextName)) { ctx_mandHandlers.add(handler); } if (val_adminonlyHandlers.contains(handlerName)) { ctx_adminonlyHandlers.add(handler); } if (val_userreadonlyHandlers.contains(handlerName)) { ctx_usrreadonlyHandlers.add(handler); } } } ctx.setPropertyHandlers(restoreCorrectHandlerOrderWithinContext(ctx_allHandlers,val_handlers)); ctx.setMandatoryProperties(ctx_mandHandlers); ctx.setAdminViewOnlyProperties(ctx_adminonlyHandlers); ctx.setUserViewReadOnlyProperties(ctx_usrreadonlyHandlers); } // create new modified userPropertiesConfig for UserManager setUserManagerProperties(); } /** * This method restores the correct property-order within the list of handlers. * The order is correctly stored in persistedProperties (as list of names -> orderedNameList). * * * @param allHandlers the handlers to sort * @param orderedNameList the list of handlerNames in the correct order * @return Returns the list of PropertyHandlers but with the correct order */ private List<UserPropertyHandler> restoreCorrectHandlerOrderWithinContext(List<UserPropertyHandler> allHandlers, List<String> orderedNameList){ Map<String,UserPropertyHandler> nameToPropertyMap = new HashMap<String, UserPropertyHandler>(); for(UserPropertyHandler handler:allHandlers) { nameToPropertyMap.put(handler.getName(), handler); } // this list will be returned. contains all handlers from "allHandlers" in sorted order List<UserPropertyHandler> sortedHandlersList = new ArrayList<UserPropertyHandler>(allHandlers.size()); for(String name:orderedNameList) { UserPropertyHandler handler = nameToPropertyMap.remove(name); if(handler != null) { sortedHandlersList.add(handler); } } for (UserPropertyHandler handler : allHandlers) { if(nameToPropertyMap.containsKey(handler.getName())) { sortedHandlersList.add(handler); } } return sortedHandlersList; } /** * saves the current UserPropertiesConfig to persistedProperties and updates * the UserManager with the current config */ public void saveUserPropertiesConfig() { log.info("saving modified UserPropertiesConfig"); // save our config to persistedProperties savePersistedProperties(); // make a new userpropertiesconfig and set it in the userManager setUserManagerProperties(); } /** * updates the userManager with our current config */ private void setUserManagerProperties() { UserPropertiesConfigImpl upConfig = new UserPropertiesConfigImpl(); List<UserPropertyHandler> handlers = new ArrayList<UserPropertyHandler>(); for (UserPropertyHandler handler : cfgObject.getPropertyHandlers()) { if (cfgObject.isActiveHandler(handler)) handlers.add(handler); } upConfig.setUserPropertyHandlers(handlers); upConfig.setUserPropertyUsageContexts(cfgObject.getUsageContexts()); userManager.setUserPropertiesConfig(upConfig); } /** * saves our config to persistedProperties */ private void savePersistedProperties() { StringBuilder sbActiveHandlers = new StringBuilder(); StringBuilder sbDeactiveHandlers = new StringBuilder(); // the propertyhandlers for (UserPropertyHandler handler : cfgObject.getPropertyHandlers()) { // save the group of each handler setStringProperty(handler.getName() + CONF_KEY_PROPGROUP, handler.getGroup(), false); if (cfgObject.isActiveHandler(handler)) { sbActiveHandlers.append(handler.getName() + PROP_DELIMITER); } else { sbDeactiveHandlers.append(handler.getName() + PROP_DELIMITER); } } setStringProperty(CONF_KEY_ACTUPROP, sbActiveHandlers.toString(), false); setStringProperty(CONF_KEY_DACTUPROP, sbDeactiveHandlers.toString(), false); // the contexts for (Entry<String, UserPropertyUsageContext> ctxEntry : cfgObject.getUsageContexts().entrySet()) { StringBuilder sbHandlers = new StringBuilder(); StringBuilder sbMandatoryHandlers = new StringBuilder(); StringBuilder sbAdminonlyHandlers = new StringBuilder(); StringBuilder sbUsrreadonlyHandlers = new StringBuilder(); // now loop over the handlers in the current context UserPropertyUsageContext ctx = ctxEntry.getValue(); for (UserPropertyHandler handler : ctx.getPropertyHandlers()) { if (cfgObject.isActiveHandler(handler)) { sbHandlers.append(handler.getName() + PROP_DELIMITER); log.debug(ctxEntry.getKey() +" has active handler :"+handler.getName() ); } if (ctx.isMandatoryUserProperty(handler)) { sbMandatoryHandlers.append(handler.getName() + PROP_DELIMITER); } if (ctx.isForAdministrativeUserOnly(handler)) { sbAdminonlyHandlers.append(handler.getName() + PROP_DELIMITER); } if (ctx.isUserViewReadOnly(handler)) { sbUsrreadonlyHandlers.append(handler.getName() + PROP_DELIMITER); } } setStringProperty(ctxEntry.getKey(), "1", false); setStringProperty(ctxEntry.getKey() + CONF_KEY_CONTPREFIX, sbHandlers.toString(), false); setStringProperty(ctxEntry.getKey() + CONF_KEY_CONTPREFIX + CONF_KEY_CONT_MANDATORY_PREFIX, sbMandatoryHandlers.toString(), false); setStringProperty(ctxEntry.getKey() + CONF_KEY_CONTPREFIX + CONF_KEY_CONT_ADMINONLY_PREFIX, sbAdminonlyHandlers.toString(), false); setStringProperty(ctxEntry.getKey() + CONF_KEY_CONTPREFIX + CONF_KEY_CONT_USERREADONLY_PREFIX, sbUsrreadonlyHandlers.toString(), false); } // now persist savePropertiesAndFireChangedEvent(); } /** * small helper method that checks if given propertyHandler can be * deactivated. See UsrPropCfgtableController.NON_DEACTIVABLE_PROPERTIES * * @param propertyHandler * @return */ public static boolean canBeDeactivated(UserPropertyHandler propertyHandler) { String name = propertyHandler.getName(); for (String key : NON_DEACTIVABLE_PROPERTIES) { if (key.equals(name)) return false; } return true; } /** * small helper method that cecks if given propertyHandler can be optional (i.e. "non-mandatory") in any context. * UsrPropCfgtableController.MUST_BE_MANDATORY_PROPERTIES * * @param propertyHandler the propertyHandler to check * @param contextClassName the name of the context to check for. (there is a special special case, where "must-be-mandatory-properties" can be non-mandatory) * @return */ public static boolean canBeOptionalInContext(UserPropertyHandler propertyHandler, String contextClassName){ // check for the exception of the rule ^ if(contextClassName != null) { for(String exceptionalContext : EXCEPTIONAL_CONTEXTS_FOR_MUSTBE_MANDATORY_PROPERTIES){ if(exceptionalContext.equals(contextClassName))return true; } } String name = propertyHandler.getName(); for(String key : MUST_BE_MANDATORY_PROPERTIES){ if(key.equals(name))return false; } return true; } @Override public void event(Event event) { // nothing to do } }