Package org.olat.admin.user.delete.service

Source Code of org.olat.admin.user.delete.service.UserDeletionManager

/**
* 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>
* Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br>
* University of Zurich, Switzerland.
* <p>
*/

package org.olat.admin.user.delete.service;

import java.io.File;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.olat.admin.user.delete.SelectionController;
import org.olat.basesecurity.Authentication;
import org.olat.basesecurity.IdentityImpl;
import org.olat.basesecurity.Manager;
import org.olat.basesecurity.ManagerFactory;
import org.olat.basesecurity.SecurityGroup;
import org.olat.bookmark.BookmarkManager;
import org.olat.catalog.CatalogManager;
import org.olat.commons.calendar.CalendarManagerFactory;
import org.olat.commons.lifecycle.LifeCycleManager;
import org.olat.core.commons.persistence.DBFactory;
import org.olat.core.commons.persistence.DBQuery;
import org.olat.core.gui.translator.Translator;
import org.olat.core.id.Identity;
import org.olat.core.id.User;
import org.olat.core.id.UserConstants;
import org.olat.core.logging.Tracing;
import org.olat.core.util.StringHelper;
import org.olat.core.util.Util;
import org.olat.core.util.coordinate.CoordinatorManager;
import org.olat.core.util.coordinate.SyncerExecutor;
import org.olat.core.util.i18n.I18nManager;
import org.olat.core.util.mail.MailTemplate;
import org.olat.core.util.mail.MailerResult;
import org.olat.core.util.mail.MailerWithTemplate;
import org.olat.core.util.notifications.NotificationsManager;
import org.olat.core.util.resource.OresHelper;
import org.olat.course.assessment.EfficiencyStatementManager;
import org.olat.group.BusinessGroupManagerImpl;
import org.olat.ims.qti.QTIResultManager;
import org.olat.modules.iq.IQManager;
import org.olat.note.NoteManager;
import org.olat.properties.Property;
import org.olat.properties.PropertyManager;
import org.olat.repository.delete.service.DeletionModule;
import org.olat.repository.delete.service.RepositoryDeletionManager;
import org.olat.user.DisplayPortraitManager;
import org.olat.user.HomePageConfigManagerImpl;
import org.olat.user.PersonalFolderManager;
import org.olat.user.UserDataDeletable;
import org.olat.user.UserManager;
import org.olat.user.propertyhandlers.UserPropertyHandler;


/**
* Manager for user-deletion.
*
* @author Christian Guretzki 
*/
public class UserDeletionManager {
 
  public static final String DELETED_USER_DELIMITER = "_bkp_";
  /** Default value for last-login duration in month. */
  private static final int DEFAULT_LAST_LOGIN_DURATION = 24;
  /** Default value for delete-email duration in days. */
  private static final int DEFAULT_DELETE_EMAIL_DURATION = 30;
  private static final String LAST_LOGIN_DURATION_PROPERTY_NAME = "LastLoginDuration";
  private static final String DELETE_EMAIL_DURATION_PROPERTY_NAME = "DeleteEmailDuration";
  private static final String PROPERTY_CATEGORY = "UserDeletion";
 
  private static final UserDeletionManager INSTANCE = new UserDeletionManager();
  public static final String SEND_DELETE_EMAIL_ACTION = "sendDeleteEmail";
  private static final String USER_ARCHIVE_DIR = "archive_deleted_users";
  private static final String USER_DELETED_ACTION = "userdeleted";
  private String emailResponseTo;
  private Identity adminIdentity;
  private static boolean keepUserLoginAfterDeletion;
  private static boolean keepUserEmailAfterDeletion;
 
  private Set<UserDataDeletable> userDataDeletableResources;

  // Flag used in user-delete to indicate that all deletable managers are initialized
  private boolean managersInitialized = false;
  private String archiveRootDir;

  private UserDeletionManager() {
    userDataDeletableResources = new HashSet<UserDataDeletable>();
  }

  /**
   * @return Singleton.
   */
  public static UserDeletionManager getInstance() { return INSTANCE; }

  /**
   * Send 'delete'- emails to a list of identities. The delete email is an announcement for the user-deletion.
   *
   * @param selectedIdentities
   * @return String with warning message (e.g. email-address not valid, could not send email).
   *         If there is no warning, the return String is empty ("").
   */
  public String sendUserDeleteEmailTo(List<Identity> selectedIdentities, MailTemplate template,
      boolean isTemplateChanged, String keyEmailSubject, String keyEmailBody, Identity sender, Translator pT ) {
    StringBuilder buf = new StringBuilder();
    if (template != null) {
      MailerWithTemplate mailer = MailerWithTemplate.getInstance();
      template.addToContext("responseTo", emailResponseTo);
      for (Iterator iter = selectedIdentities.iterator(); iter.hasNext();) {
        Identity identity = (Identity)iter.next();
        if (!isTemplateChanged) {
          // Email template has NOT changed => take translated version of subject and body text
          Translator identityTranslator = Util.createPackageTranslator(SelectionController.class, I18nManager.getInstance().getLocaleOrDefault(identity.getUser().getPreferences().getLanguage()));
          template.setSubjectTemplate(identityTranslator.translate(keyEmailSubject));
          template.setBodyTemplate(identityTranslator.translate(keyEmailBody));
        }
        template.putVariablesInMailContext(template.getContext(), identity);
        Tracing.logDebug(" Try to send Delete-email to identity=" + identity.getName() + " with email=" + identity.getUser().getProperty(UserConstants.EMAIL, null), this.getClass());
        List<Identity> ccIdentities = new ArrayList<Identity>();
        if(template.getCpfrom()) {
          ccIdentities.add(sender);
        } else {
          ccIdentities = null
        }
        MailerResult mailerResult = mailer.sendMailUsingTemplateContext(identity, ccIdentities, null, template, sender);
        if (mailerResult.getReturnCode() != MailerResult.OK) {
          buf.append(pT.translate("email.error.send.failed", new String[] {identity.getUser().getProperty(UserConstants.EMAIL, null), identity.getName()} )).append("\n");
        }
        Tracing.logAudit("User-Deletion: Delete-email send to identity=" + identity.getName() + " with email=" + identity.getUser().getProperty(UserConstants.EMAIL, null), this.getClass());
        markSendEmailEvent(identity);
      }
    } else {
      // no template => User decides to sending no delete-email, mark only in lifecycle table 'sendEmail'
      for (Iterator iter = selectedIdentities.iterator(); iter.hasNext();) {
        Identity identity = (Identity)iter.next();
        Tracing.logAudit("User-Deletion: Move in 'Email sent' section without sending email, identity=" + identity.getName(), this.getClass());
        markSendEmailEvent(identity);
      }
    }
    return buf.toString();
  }
 
  private void markSendEmailEvent(Identity identity) {
    identity = (Identity)DBFactory.getInstance().loadObject(identity);
    LifeCycleManager.createInstanceFor(identity).markTimestampFor(SEND_DELETE_EMAIL_ACTION);
    DBFactory.getInstance().updateObject(identity);
  }
 
  /**
   * Return list of identities which have last-login older than 'lastLoginDuration' parameter.
   * This user are ready to start with user-deletion process.
   * @param lastLoginDuration  last-login duration in month
   * @return List of Identity objects
   */
  public List getDeletableIdentities(int lastLoginDuration) {
    Calendar lastLoginLimit = Calendar.getInstance();
    lastLoginLimit.add(Calendar.MONTH, - lastLoginDuration);
    Tracing.logDebug("lastLoginLimit=" + lastLoginLimit, this.getClass());
    // 1. get all 'active' identities with lastlogin > x
    String queryStr ="from org.olat.core.id.Identity as ident where ident.status = '"
      + Identity.STATUS_ACTIV
      + "' and (ident.lastLogin = null or ident.lastLogin < :lastLogin)"
    DBQuery dbq = DBFactory.getInstance().createQuery(queryStr);
    dbq.setDate("lastLogin", lastLoginLimit.getTime());
    List identities = dbq.list();
    // 2. get all 'active' identities in deletion process
    queryStr = "select ident from org.olat.core.id.Identity as ident"
      + " , org.olat.commons.lifecycle.LifeCycleEntry as le"
      + " where ident.key = le.persistentRef "
      + " and le.persistentTypeName ='" + IdentityImpl.class.getName() + "'"
      + " and le.action ='" + SEND_DELETE_EMAIL_ACTION + "' ";
    dbq = DBFactory.getInstance().createQuery(queryStr);
    List identitiesInProcess = dbq.list();
    // 3. Remove all identities in deletion-process from all inactive-identities
    identities.removeAll(identitiesInProcess);
    return identities;    
  }

  /**
   * Return list of identities which are in user-deletion-process.
   * user-deletion-process means delete-announcement.email send, duration of waiting for response is not expired.
   * @param deleteEmailDuration  Duration of user-deletion-process in days
   * @return List of Identity objects
   */
  public List getIdentitiesInDeletionProcess(int deleteEmailDuration) {
    Calendar deleteEmailLimit = Calendar.getInstance();
    deleteEmailLimit.add(Calendar.DAY_OF_MONTH, - (deleteEmailDuration-1));
    Tracing.logDebug("deleteEmailLimit=" + deleteEmailLimit, this.getClass());
    String queryStr = "select ident from org.olat.core.id.Identity as ident"
      + " , org.olat.commons.lifecycle.LifeCycleEntry as le"
      + " where ident.key = le.persistentRef "
      + " and ident.status = '"  + Identity.STATUS_ACTIV + "'"
      + " and le.persistentTypeName ='" + IdentityImpl.class.getName() + "'"
      + " and le.action ='" + SEND_DELETE_EMAIL_ACTION + "' and le.lcTimestamp >= :deleteEmailDate ";
    DBQuery dbq = DBFactory.getInstance().createQuery(queryStr);
    dbq.setDate("deleteEmailDate", deleteEmailLimit.getTime());
    return dbq.list();
  }

  /**
   * Return list of identities which are ready-to-delete in user-deletion-process.
   * (delete-announcement.email send, duration of waiting for response is expired).
   * @param deleteEmailDuration  Duration of user-deletion-process in days
   * @return List of Identity objects
   */
  public List getIdentitiesReadyToDelete(int deleteEmailDuration) {
    Calendar deleteEmailLimit = Calendar.getInstance();
    deleteEmailLimit.add(Calendar.DAY_OF_MONTH, - (deleteEmailDuration - 1));
    Tracing.logDebug("deleteEmailLimit=" + deleteEmailLimit, this.getClass());
    String queryStr = "select ident from org.olat.core.id.Identity as ident"
      + " , org.olat.commons.lifecycle.LifeCycleEntry as le"
      + " where ident.key = le.persistentRef "
      + " and ident.status = '"  + Identity.STATUS_ACTIV + "'"
      + " and le.persistentTypeName ='" + IdentityImpl.class.getName() + "'"
      + " and le.action ='" + SEND_DELETE_EMAIL_ACTION + "' and le.lcTimestamp < :deleteEmailDate ";
    DBQuery dbq = DBFactory.getInstance().createQuery(queryStr);
    dbq.setDate("deleteEmailDate", deleteEmailLimit.getTime());
    return dbq.list();
  }
 
  /**
   *
   * @return true when user can be deleted (non deletion-process is still running)
   */
  public boolean isReadyToDelete() {
    return UserFileDeletionManager.isReadyToDelete();
  }
  /**
   * Delete all user-data in registered deleteable resources.
   * @param identity
   * @return true
   */
  public void deleteIdentity(Identity identity) {
    Tracing.logInfo("Start deleteIdentity for identity=" + identity, this.getClass());

    String newName = getBackupStringWithDate(identity.getName());

    // TODO: chg: Workaround: instances each manager which implements UaserDataDeletable interface
    // Each manager register themself as deletable
    // Should be better with new config concept
    if (!managersInitialized) {
      HomePageConfigManagerImpl.getInstance();
      DisplayPortraitManager.getInstance();
      NoteManager.getInstance();
      PropertyManager.getInstance();
      BookmarkManager.getInstance();
      NotificationsManager.getInstance();
      PersonalFolderManager.getInstance();
      IQManager.getInstance();
      QTIResultManager.getInstance();
      BusinessGroupManagerImpl.getInstance();
      RepositoryDeletionManager.getInstance();
      CatalogManager.getInstance();
      CalendarManagerFactory.getInstance();
      EfficiencyStatementManager.getInstance();
      UserFileDeletionManager.getInstance();
      managersInitialized = true;
    }
   
    Tracing.logInfo("Start EfficiencyStatementManager.archiveUserData for identity=" + identity, this.getClass());
    EfficiencyStatementManager.getInstance().archiveUserData(identity, getArchivFilePath(identity) );

    Tracing.logInfo("Start Deleting user=" + identity, this.getClass());
    for (Iterator<UserDataDeletable> iter = userDataDeletableResources.iterator(); iter.hasNext();) {
      UserDataDeletable element = iter.next();
      Tracing.logInfo("UserDataDeletable-Loop element=" + element, this.getClass());
      element.deleteUserData(identity, newName);
    }
    Tracing.logInfo("deleteUserProperties user=" + identity.getUser(), this.getClass());
    UserManager.getInstance().deleteUserProperties(identity.getUser());
    // Delete all authentications for certain identity
    List authentications = ManagerFactory.getManager().getAuthentications(identity);
    for (Iterator iter = authentications.iterator(); iter.hasNext();) {
      Authentication auth = (Authentication) iter.next();
      Tracing.logInfo("deleteAuthentication auth=" + auth, this.getClass());
      ManagerFactory.getManager().deleteAuthentication(auth);
      Tracing.logInfo("Delete auth=" + auth + "  of identity="  + identity, this.getClass());
    }
   
    //remove identity from its security groups
    Manager secMgr = ManagerFactory.getManager();
    List<SecurityGroup> securityGroups = ManagerFactory.getManager().getSecurityGroupsForIdentity(identity);
    for (SecurityGroup secGroup : securityGroups) {
      secMgr.removeIdentityFromSecurityGroup(identity, secGroup);
      Tracing.logInfo("Removing user=" + identity + " from security group="  + secGroup.toString(), this.getClass());
    }
   
    // can be used, if there is once the possibility to delete identities without db-constraints...
    //if neither email nor login should be kept, REALLY DELETE Identity
    /*if (!keepUserEmailAfterDeletion & !keepUserLoginAfterDeletion){
      identity = (Identity)DBFactory.getInstance().loadObject(identity);
      DBFactory.getInstance().deleteObject(identity.getUser());
      DBFactory.getInstance().deleteObject(identity);
    }
    else { */
      identity = (Identity)DBFactory.getInstance().loadObject(identity);
      //keep login-name only -> change email
      if (!keepUserEmailAfterDeletion){
        List<UserPropertyHandler> userPropertyHandlers = UserManager.getInstance().getUserPropertyHandlersFor("org.olat.admin.user.UsermanagerUserSearchForm", true);
        User persistedUser = identity.getUser();
        String actualProperty;
        for (UserPropertyHandler userPropertyHandler : userPropertyHandlers) {
          actualProperty = userPropertyHandler.getName();
          if (actualProperty.equals(UserConstants.EMAIL)){
            String oldEmail = userPropertyHandler.getUserProperty(persistedUser, null);
            String newEmail = "";
            if (StringHelper.containsNonWhitespace(oldEmail)){
              newEmail = getBackupStringWithDate(oldEmail);
            }
            Tracing.logInfo("Update user-property user=" + persistedUser , this.getClass());
            userPropertyHandler.setUserProperty(persistedUser, newEmail);
          }
        }
      }
     
      //keep email only -> change login-name
      if (!keepUserLoginAfterDeletion){
        identity.setName(newName);
      }
     
      //keep everything, change identity.status to deleted
      Tracing.logInfo("Change stater identity=" + identity , this.getClass());
      identity.setStatus(Identity.STATUS_DELETED);
      DBFactory.getInstance().updateObject(identity);
      LifeCycleManager.createInstanceFor(identity).deleteTimestampFor(SEND_DELETE_EMAIL_ACTION);
      LifeCycleManager.createInstanceFor(identity).markTimestampFor(USER_DELETED_ACTION, createLifeCycleLogDataFor(identity));
    // }
   
    // TODO: chg: ev. logAudit at another place
    Tracing.logAudit("User-Deletion: Delete all userdata for identity=" + identity, this.getClass());
  }
 
  public String getBackupStringWithDate(String original){
    DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmm");
    String dateStamp = dateFormat.format(new Date());
    return dateStamp + DELETED_USER_DELIMITER + original;
  }
 
  private String createLifeCycleLogDataFor(Identity identity) {
    StringBuilder buf = new StringBuilder();
    buf.append("<identity>");
    buf.append("<username>").append(identity.getName()).append("</username>");
    buf.append("<lastname>").append(identity.getName()).append("</lastname>");
    buf.append("<firstname>").append(identity.getName()).append("</firstname>");
    buf.append("<email>").append(identity.getName()).append("</email>");
    buf.append("</identity>");
    return buf.toString();
  }

  /**
   * Re-activate an identity, lastLogin = now, reset deleteemaildate = null.
   * @param identity
   */
  public void setIdentityAsActiv(final Identity anIdentity) {
    CoordinatorManager.getCoordinator().getSyncer().doInSync(OresHelper.createOLATResourceableInstance(anIdentity.getClass(), anIdentity.getKey()) ,
      new SyncerExecutor(){
        public void execute() {
           //o_clusterOK by:fj : must be fast
          Identity identity = (Identity)DBFactory.getInstance().loadObject(anIdentity, true);
          if (Tracing.isDebugEnabled(this.getClass())) Tracing.logDebug("setIdentityAsActiv beginSingleTransaction identity=" + identity, this.getClass());
          identity.setLastLogin(new Date());
          LifeCycleManager lifeCycleManagerForIdenitiy = LifeCycleManager.createInstanceFor(identity);
          if (lifeCycleManagerForIdenitiy.lookupLifeCycleEntry(SEND_DELETE_EMAIL_ACTION) != null) {
            Tracing.logAudit("User-Deletion: Remove from delete-list identity=" + identity, this.getClass());
            lifeCycleManagerForIdenitiy.deleteTimestampFor(SEND_DELETE_EMAIL_ACTION);
          }
          if (Tracing.isDebugEnabled(this.getClass())) Tracing.logDebug("setIdentityAsActiv updateObject identity=" + identity, this.getClass());
          DBFactory.getInstance().updateObject(identity);
          if (Tracing.isDebugEnabled(this.getClass())) Tracing.logDebug("setIdentityAsActiv committed identity=" + identity, this.getClass());
        }
    });
  }

  /**
   * @return  Return duration in days for waiting for reaction on delete-email.
   */
  public int getDeleteEmailDuration() {
    return getPropertyByName(DELETE_EMAIL_DURATION_PROPERTY_NAME, DEFAULT_DELETE_EMAIL_DURATION);
  }

  /**
   * @return  Return last-login duration in month for user on delete-selection list.
   */
  public int getLastLoginDuration() {
    return getPropertyByName(LAST_LOGIN_DURATION_PROPERTY_NAME, DEFAULT_LAST_LOGIN_DURATION);
  }

  private int getPropertyByName(String name, int defaultValue) {
    List properties = PropertyManager.getInstance().findProperties(null, null, null, PROPERTY_CATEGORY, name);
    if (properties.size() == 0) {
      return defaultValue;
    } else {
      return ((Property)properties.get(0)).getLongValue().intValue();
    }
  }

  public void setLastLoginDuration(int lastLoginDuration) {
    setProperty(LAST_LOGIN_DURATION_PROPERTY_NAME, lastLoginDuration);
  }

  public void setDeleteEmailDuration(int deleteEmailDuration) {
    setProperty(DELETE_EMAIL_DURATION_PROPERTY_NAME, deleteEmailDuration);
  }

  private void setProperty(String propertyName, int value) {
    List properties = PropertyManager.getInstance().findProperties(null, null, null, PROPERTY_CATEGORY, propertyName);
    Property property = null;
    if (properties.size() == 0) {
      property = PropertyManager.getInstance().createPropertyInstance(null, null, null, PROPERTY_CATEGORY, propertyName, null,  new Long(value), null, null);
    } else {
      property = (Property)properties.get(0);
      property.setLongValue( new Long(value) );
    }
    PropertyManager.getInstance().saveProperty(property);
  }

  public void init(DeletionModule module) {
    emailResponseTo = module.getEmailResponseTo();
    adminIdentity = module.getAdminUserIdentity();
    archiveRootDir = module.getArchiveRootPath();
  }

  /**
   * Return in olat_config.xml definied administrator identity.
   * @return
   */
  public Identity getAdminIdentity() {
    return adminIdentity;
  }

  public void registerDeletableUserData(UserDataDeletable deletableUserDataResource) {
    userDataDeletableResources.add(deletableUserDataResource);
  }

  private File getArchivFilePath(Identity identity) {
    String archiveFilePath = archiveRootDir + File.separator + USER_ARCHIVE_DIR + File.separator + DeletionModule.getArchiveDatePath()
         + File.separator + "del_identity_" + identity.getName();
    File archiveIdentityRootDir = new File(archiveFilePath);
    if (!archiveIdentityRootDir.exists()) {
      archiveIdentityRootDir.mkdirs();
    }
    return archiveIdentityRootDir;
  }

  /**
   * Setter method used by spring
   * @param keepUserLoginAfterDeletion The keepUserLoginAfterDeletion to set.
   */
  public void setKeepUserLoginAfterDeletion(boolean keepUserLoginAfterDeletion) {
    this.keepUserLoginAfterDeletion = keepUserLoginAfterDeletion;
  }

  /**
   * Setter method used by spring
   * @param keepUserEmailAfterDeletion The keepUserEmailAfterDeletion to set.
   */
  public void setKeepUserEmailAfterDeletion(boolean keepUserEmailAfterDeletion) {
    this.keepUserEmailAfterDeletion = keepUserEmailAfterDeletion;
  }

  public static boolean isKeepUserLoginAfterDeletion() {
    return keepUserLoginAfterDeletion;
  }

}
TOP

Related Classes of org.olat.admin.user.delete.service.UserDeletionManager

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.