/**
* <a href="http://www.openolat.org">
* OpenOLAT - Online Learning and Training</a><br>
* <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 the
* <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
* <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>
* Initial code contributed and copyrighted by<br>
* frentix GmbH, http://www.frentix.com
* <p>
*/
package org.olat.admin.user.bulkChange;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.context.Context;
import org.apache.velocity.exception.MethodInvocationException;
import org.apache.velocity.exception.ParseErrorException;
import org.apache.velocity.exception.ResourceNotFoundException;
import org.apache.velocity.runtime.RuntimeConstants;
import org.olat.admin.user.SystemRolesAndRightsController;
import org.olat.basesecurity.BaseSecurity;
import org.olat.basesecurity.BaseSecurityManager;
import org.olat.basesecurity.Constants;
import org.olat.basesecurity.SecurityGroup;
import org.olat.core.CoreSpringFactory;
import org.olat.core.commons.persistence.DB;
import org.olat.core.commons.persistence.DBFactory;
import org.olat.core.gui.components.form.ValidationError;
import org.olat.core.gui.translator.Translator;
import org.olat.core.helpers.Settings;
import org.olat.core.id.Identity;
import org.olat.core.id.Preferences;
import org.olat.core.id.User;
import org.olat.core.id.UserConstants;
import org.olat.core.logging.OLog;
import org.olat.core.logging.Tracing;
import org.olat.core.manager.BasicManager;
import org.olat.core.util.StringHelper;
import org.olat.core.util.Util;
import org.olat.core.util.WebappHelper;
import org.olat.core.util.i18n.I18nManager;
import org.olat.core.util.mail.MailBundle;
import org.olat.core.util.mail.MailManager;
import org.olat.core.util.mail.MailPackage;
import org.olat.group.BusinessGroupService;
import org.olat.group.model.BusinessGroupMembershipChange;
import org.olat.login.auth.OLATAuthManager;
import org.olat.user.UserManager;
import org.olat.user.propertyhandlers.GenderPropertyHandler;
import org.olat.user.propertyhandlers.UserPropertyHandler;
/**
* Description:<br>
* this is a helper class which can be used in bulkChange-Steps and also the UsermanagerUserSearchController
*
* <P>
* Initial Date: 07.03.2008 <br>
*
* @author rhaag
*/
public class UserBulkChangeManager extends BasicManager {
private static VelocityEngine velocityEngine;
private static OLog log = Tracing.createLoggerFor(UserBulkChangeManager.class);
private static UserBulkChangeManager INSTANCE = new UserBulkChangeManager();
static final String PWD_IDENTIFYER = "password";
static final String LANG_IDENTIFYER = "language";
public UserBulkChangeManager() {
// init velocity engine
Properties p = null;
try {
velocityEngine = new VelocityEngine();
p = new Properties();
p.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS, "org.apache.velocity.runtime.log.SimpleLog4JLogSystem");
p.setProperty("runtime.log.logsystem.log4j.category", "syslog");
velocityEngine.init(p);
} catch (Exception e) {
throw new RuntimeException("config error " + p.toString());
}
}
public static UserBulkChangeManager getInstance() {
return INSTANCE;
}
public void changeSelectedIdentities(List<Identity> selIdentities, Map<String, String> attributeChangeMap,
Map<String, String> roleChangeMap, List<String> notUpdatedIdentities, boolean isAdministrativeUser, List<Long> ownGroups, List<Long> partGroups,
Translator trans, Identity addingIdentity) {
Translator transWithFallback = UserManager.getInstance().getPropertyHandlerTranslator(trans);
String usageIdentifyer = UserBulkChangeStep00.class.getCanonicalName();
notUpdatedIdentities.clear();
List<Identity> changedIdentities = new ArrayList<Identity>();
List<UserPropertyHandler> userPropertyHandlers = UserManager.getInstance().getUserPropertyHandlersFor(usageIdentifyer,
isAdministrativeUser);
String[] securityGroups = { Constants.GROUP_USERMANAGERS, Constants.GROUP_GROUPMANAGERS, Constants.GROUP_AUTHORS, Constants.GROUP_ADMIN };
UserManager um = UserManager.getInstance();
BaseSecurity secMgr = BaseSecurityManager.getInstance();
// loop over users to be edited:
for (Identity identity : selIdentities) {
DB db = DBFactory.getInstance();
//reload identity from cache, to prevent stale object
identity = (Identity) db.loadObject(identity);
User user = identity.getUser();
String errorDesc = "";
boolean updateError = false;
// change pwd
if (attributeChangeMap.containsKey(PWD_IDENTIFYER)) {
String newPwd = attributeChangeMap.get(PWD_IDENTIFYER);
if (StringHelper.containsNonWhitespace(newPwd)) {
if (!UserManager.getInstance().syntaxCheckOlatPassword(newPwd)) {
errorDesc = transWithFallback.translate("error.password");
updateError = true;
}
} else {
newPwd = null;
}
OLATAuthManager olatAuthenticationSpi = CoreSpringFactory.getImpl(OLATAuthManager.class);
olatAuthenticationSpi.changePasswordAsAdmin(identity, newPwd);
}
// set language
String userLanguage = user.getPreferences().getLanguage();
if (attributeChangeMap.containsKey(LANG_IDENTIFYER)) {
String inputLanguage = attributeChangeMap.get(LANG_IDENTIFYER);
if (!userLanguage.equals(inputLanguage)) {
Preferences preferences = user.getPreferences();
preferences.setLanguage(inputLanguage);
user.setPreferences(preferences);
}
}
Context vcContext = new VelocityContext();
// set all properties as context
setUserContext(identity, vcContext);
// loop for each property configured in
// src/serviceconfig/org/olat/_spring/olat_userconfig.xml -> Key:
// org.olat.admin.user.bulkChange.UserBulkChangeStep00
for (int k = 0; k < userPropertyHandlers.size(); k++) {
UserPropertyHandler propHandler = userPropertyHandlers.get(k);
String propertyName = propHandler.getName();
String userValue = identity.getUser().getProperty(propertyName, null);
String inputFieldValue = "";
if (attributeChangeMap.containsKey(propertyName)) {
inputFieldValue = attributeChangeMap.get(propertyName);
inputFieldValue = inputFieldValue.replace("$", "$!");
String evaluatedInputFieldValue = evaluateValueWithUserContext(inputFieldValue, vcContext);
// validate evaluated property-value
ValidationError validationError = new ValidationError();
// do validation checks with users current locale!
Locale locale = transWithFallback.getLocale();
if (!propHandler.isValidValue(identity.getUser(), evaluatedInputFieldValue, validationError, locale)) {
errorDesc = transWithFallback.translate(validationError.getErrorKey(), validationError.getArgs()) + " (" + evaluatedInputFieldValue + ")";
updateError = true;
break;
}
if (!evaluatedInputFieldValue.equals(userValue)) {
String stringValue = propHandler.getStringValue(evaluatedInputFieldValue, locale);
propHandler.setUserProperty(user, stringValue);
}
}
} // for (propertyHandlers)
// set roles for identity
// loop over securityGroups defined above
for (String securityGroup : securityGroups) {
SecurityGroup secGroup = secMgr.findSecurityGroupByName(securityGroup);
Boolean isInGroup = secMgr.isIdentityInSecurityGroup(identity, secGroup);
String thisRoleAction = "";
if (roleChangeMap.containsKey(securityGroup)) {
thisRoleAction = roleChangeMap.get(securityGroup);
// user not anymore in security group, remove him
if (isInGroup && thisRoleAction.equals("remove")) {
secMgr.removeIdentityFromSecurityGroup(identity, secGroup);
logAudit("User::" + addingIdentity.getName() + " removed system role::" + securityGroup + " from user::" + identity.getName(), null);
}
// user not yet in security group, add him
if (!isInGroup && thisRoleAction.equals("add")) {
secMgr.addIdentityToSecurityGroup(identity, secGroup);
logAudit("User::" + addingIdentity.getName() + " added system role::" + securityGroup + " to user::" + identity.getName(), null);
}
}
}
// set status
if (roleChangeMap.containsKey("Status")) {
Integer status = Integer.parseInt(roleChangeMap.get("Status"));
int oldStatus = identity.getStatus();
String oldStatusText = (oldStatus == Identity.STATUS_PERMANENT ? "permanent"
: (oldStatus == Identity.STATUS_ACTIV ? "active"
: (oldStatus == Identity.STATUS_LOGIN_DENIED ? "login_denied"
: (oldStatus == Identity.STATUS_DELETED ? "deleted"
: "unknown"))));
String newStatusText = (status == Identity.STATUS_PERMANENT ? "permanent"
: (status == Identity.STATUS_ACTIV ? "active"
: (status == Identity.STATUS_LOGIN_DENIED ? "login_denied"
: (status == Identity.STATUS_DELETED ? "deleted"
: "unknown"))));
if(oldStatus != status && status == Identity.STATUS_LOGIN_DENIED && Boolean.parseBoolean(roleChangeMap.get("sendLoginDeniedEmail"))) {
sendLoginDeniedEmail(identity);
}
identity = secMgr.saveIdentityStatus(identity, status);
logAudit("User::" + addingIdentity.getName() + " changed accout status for user::" + identity.getName() + " from::" + oldStatusText + " to::" + newStatusText, null);
}
// persist changes:
if (updateError) {
String errorOutput = identity.getName() + ": " + errorDesc;
log.debug("error during bulkChange of users, following user could not be updated: " + errorOutput);
notUpdatedIdentities.add(errorOutput);
} else {
um.updateUserFromIdentity(identity);
changedIdentities.add(identity);
logAudit("User::" + addingIdentity.getName() + " successfully changed account data for user::" + identity.getName() + " in bulk change", null);
}
// commit changes for this user
db.intermediateCommit();
} // for identities
// FXOLAT-101: add identity to new groups:
if (ownGroups.size() != 0 || partGroups.size() != 0) {
List<BusinessGroupMembershipChange> changes = new ArrayList<BusinessGroupMembershipChange>();
for(Identity selIdentity:selIdentities) {
if(ownGroups != null && !ownGroups.isEmpty()) {
for(Long tutorGroupKey:ownGroups) {
BusinessGroupMembershipChange change = new BusinessGroupMembershipChange(selIdentity, tutorGroupKey);
change.setTutor(Boolean.TRUE);
changes.add(change);
}
}
if(partGroups != null && !partGroups.isEmpty()) {
for(Long partGroupKey:partGroups) {
BusinessGroupMembershipChange change = new BusinessGroupMembershipChange(selIdentity, partGroupKey);
change.setParticipant(Boolean.TRUE);
changes.add(change);
}
}
}
BusinessGroupService bgs = CoreSpringFactory.getImpl(BusinessGroupService.class);
MailPackage mailing = new MailPackage();
bgs.updateMemberships(addingIdentity, changes, mailing);
DBFactory.getInstance().commit();
}
}
public void sendLoginDeniedEmail(Identity identity) {
String lang = identity.getUser().getPreferences().getLanguage();
Locale locale = I18nManager.getInstance().getLocaleOrDefault(lang);
Translator translator = Util.createPackageTranslator(SystemRolesAndRightsController.class, locale);
String gender = "";
UserPropertyHandler handler = UserManager.getInstance().getUserPropertiesConfig().getPropertyHandler(UserConstants.GENDER);
if(handler instanceof GenderPropertyHandler) {
String internalGender = ((GenderPropertyHandler)handler).getInternalValue(identity.getUser());
if(StringHelper.containsNonWhitespace(internalGender)) {
Translator userPropTrans = UserManager.getInstance().getUserPropertiesConfig().getTranslator(translator);
gender = userPropTrans.translate("form.name.gender.salutation." + internalGender);
}
}
String[] args = new String[] {
identity.getName(),//0: changed users username
identity.getUser().getProperty(UserConstants.EMAIL, null),// 1: changed users email address
UserManager.getInstance().getUserDisplayName(identity.getUser()),// 2: Name (first and last name) of user who changed the password
WebappHelper.getMailConfig("mailSupport"), //3: configured support email address
identity.getUser().getProperty(UserConstants.LASTNAME, null), //4 last name
getServerURI(), //5 url system
gender //6 Mr. Mrs.
};
MailBundle bundle = new MailBundle();
bundle.setToId(identity);
bundle.setContent(translator.translate("mailtemplate.login.denied.subject", args),
translator.translate("mailtemplate.login.denied.body", args));
CoreSpringFactory.getImpl(MailManager.class).sendExternMessage(bundle, null, false);
}
private String getServerURI() {
String uri = Settings.getSecureServerContextPathURI();
if(StringHelper.containsNonWhitespace(uri)) {
return uri;
}
return Settings.getInsecureServerContextPathURI();
}
public String evaluateValueWithUserContext(String valToEval, Context vcContext) {
StringWriter evaluatedUserValue = new StringWriter();
// evaluate inputFieldValue to get a concatenated string
try {
velocityEngine.evaluate(vcContext, evaluatedUserValue, "vcUservalue", valToEval);
} catch (ParseErrorException e) {
log.error("parsing of values in BulkChange Field not possible!");
e.printStackTrace();
return "ERROR";
} catch (MethodInvocationException e) {
log.error("evaluating of values in BulkChange Field not possible!");
e.printStackTrace();
return "ERROR";
} catch (ResourceNotFoundException e) {
log.error("evaluating of values in BulkChange Field not possible!");
e.printStackTrace();
return "ERROR";
} catch (Exception e) {
log.error("evaluating of values in BulkChange Field not possible!");
e.printStackTrace();
return "ERROR";
}
return evaluatedUserValue.toString();
}
/**
*
* @param identity
* @param vcContext
* @param isAdministrativeUser
*/
public void setUserContext(Identity identity, Context vcContext) {
List<UserPropertyHandler> userPropertyHandlers2;
userPropertyHandlers2 = UserManager.getInstance().getAllUserPropertyHandlers();
for (UserPropertyHandler userPropertyHandler : userPropertyHandlers2) {
String propertyName = userPropertyHandler.getName();
String userValue = identity.getUser().getProperty(propertyName, null);
vcContext.put(propertyName, userValue);
}
}
public Context getDemoContext(Locale locale) {
Translator propertyTrans = Util.createPackageTranslator(UserPropertyHandler.class, locale);
return getDemoContext(propertyTrans);
}
public Context getDemoContext(Translator propertyTrans) {
Context vcContext = new VelocityContext();
List<UserPropertyHandler> userPropertyHandlers2;
userPropertyHandlers2 = UserManager.getInstance().getAllUserPropertyHandlers();
for (UserPropertyHandler userPropertyHandler : userPropertyHandlers2) {
String propertyName = userPropertyHandler.getName();
String userValue = propertyTrans.translate("import.example." + userPropertyHandler.getName());
vcContext.put(propertyName, userValue);
}
return vcContext;
}
}