--- /dev/null
+/*******************************************************************************
+ * Copyright � Igor Barma, Alexandre Desoubeaux, Christian Martel, Eric Brun, Mathieu Amblard, Gwenael Gevet, Pierre Guillot, 2012
+ * Copyright Alexandre Desoubeaux, Christian Martel, Cedric Lecarpentier, Alexandre Lefevre, Marc Salvat 2014-2016
+ * Copyright Alexandre Desoubeaux, Christian Martel, Cedric Lecarpentier, Marc Salvat, Marc Suarez, Harifetra Ramamonjy 2017
+ *
+ * This file is part of the work and learning management system Pentila Nero.
+ *
+ * Pentila Nero is free software. You can redistribute it and/or modify since
+ * you respect the terms of either (at least one of the both license) :
+ * - under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ * - the CeCILL-C as published by CeCILL-C; either version 1 of the
+ * License, or any later version
+ * - the GNU Lesser General Public License as published by the
+ * Free Software Foundation, either version 3 of the license,
+ * or (at your option) any later version.
+ *
+ * There are special exceptions to the terms and conditions of the
+ * licenses as they are applied to this software. View the full text of
+ * the exception in file LICENSE-PROJECT.txt in the directory of this software
+ * distribution.
+ *
+ * Pentila Nero 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
+ * Licenses for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * and the CeCILL-C and the GNU Lesser General Public License along with
+ * Pentila Nero. If not, see :
+ * <http://www.gnu.org/licenses/> and
+ * <http://www.cecill.info/licences.fr.html>.
+ ******************************************************************************/
+package com.pentila.entSavoie.synchroLdap.impl;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+
+import com.liferay.contacts.util.OrganizationConstants;
+import com.liferay.contacts.util.OrganizationFilter;
+import com.liferay.portal.kernel.dao.orm.QueryUtil;
+import com.liferay.portal.kernel.log.Log;
+import com.liferay.portal.kernel.log.LogFactoryUtil;
+import com.liferay.portal.kernel.workflow.WorkflowConstants;
+import com.liferay.portal.model.Group;
+import com.liferay.portal.model.Organization;
+import com.liferay.portal.model.Role;
+import com.liferay.portal.model.User;
+import com.liferay.portal.model.UserGroup;
+import com.liferay.portal.model.UserGroupRole;
+import com.liferay.portal.security.ldap.LDAPConstants;
+import com.liferay.portal.service.GroupLocalServiceUtil;
+import com.liferay.portal.service.OrganizationLocalServiceUtil;
+import com.liferay.portal.service.RoleLocalServiceUtil;
+import com.liferay.portal.service.UserGroupLocalServiceUtil;
+import com.liferay.portal.service.UserGroupRoleLocalServiceUtil;
+import com.liferay.portal.service.UserLocalServiceUtil;
+import com.pentila.entSavoie.ENTRolesConstants;
+import com.pentila.entSavoie.cahierDeTexte.util.CDTColorUtil;
+import com.pentila.entSavoie.cdt.model.SchoolSubject;
+import com.pentila.entSavoie.cdt.model.Subject;
+import com.pentila.entSavoie.cdt.service.SchoolSubjectLocalServiceUtil;
+import com.pentila.entSavoie.cdt.service.SubjectLocalServiceUtil;
+import com.pentila.entSavoie.gestionVieScolaire.model.SynchroConfig;
+import com.pentila.entSavoie.gestionVieScolaire.service.SynchroConfigLocalServiceUtil;
+import com.pentila.entSavoie.synchroLdap.exceptions.UserSynchronizationException;
+import com.pentila.entSavoie.userProperties.model.UserRelationship;
+import com.pentila.entSavoie.userProperties.service.UserRelationshipLocalServiceUtil;
+import com.pentila.entSavoie.userRelationship.UserRelationshipConstants;
+import com.pentila.entSavoie.utils.ENTMainUtilsLocalServiceUtil;
+import com.pentila.entSavoie.utils.ENTOrganizationsUtil;
+
+/**
+ * Class used to manage memberships during the synchronization process
+ * @author Cedric Lecarpentier
+ *
+ */
+public class MembershipManager {
+
+ private static Log _log = LogFactoryUtil.getLog(MembershipManager.class);
+
+
+ private static long companyId;
+
+ // This map indicates, for a given student userId, if there was a membership modification or not => fasten its parent's synchronization
+ private static Map<Long, Boolean> childModificationsMap;
+
+ // The list of school org ids that do use AAF for membership management
+ private static List<Long> schoolsUsingAAFMode;
+
+ // The list of all ENT users (used to determine all users except stdents, parents and teachers)
+ private static List<Long> allENTUserIds;
+
+ // The root organization
+ private static Organization rootOrg;
+
+ /**
+ * Entry point for managing memberships during the synchronization process
+ * @param _companyId
+ * @throws NamingException
+ * @throws Exception
+ */
+ public static void manageMemberships(long _companyId) throws NamingException, Exception {
+
+ _log.info("Start manageMemberships");
+ companyId = _companyId;
+
+ initMembership(companyId);
+
+ // Get all users in DB
+ List<User> allENTUsers = UserLocalServiceUtil.getUsers(QueryUtil.ALL_POS, QueryUtil.ALL_POS);
+ allENTUserIds = new ArrayList<Long>();
+ for (User user : allENTUsers) {
+ allENTUserIds.add(user.getUserId());
+ }
+
+ manageAllStudentsMembership();
+ manageAllParentsMemberships();
+ manageAllTeachersMemberships();
+ manageAllOtherProfilesMemberships();
+
+ }
+
+ /**
+ * Manage memberships for all students
+ */
+ private static void manageAllStudentsMembership() {
+
+ try {
+ Role studentRole = RoleLocalServiceUtil.getRole(companyId, ENTRolesConstants.NATIONAL_1);
+ List<User> students = UserLocalServiceUtil.getRoleUsers(studentRole.getRoleId());
+ _log.info("Manage student memberships : fetched "+students.size()+" students ...");
+
+ // Loop over students
+ long studentAll0 = System.currentTimeMillis();
+ int studentIdx = 0;
+ for (User student : students) {
+ if (SynchronizationManager.getUserAttributesMap().containsKey(student.getScreenName())) {
+
+ // Skip inactive students
+ if (student.getStatus() == WorkflowConstants.STATUS_INACTIVE) {
+ continue;
+ }
+
+ long studentTime0 = System.currentTimeMillis();
+ Attributes studentAttributes = SynchronizationManager.getUserAttributesMap().get(student.getScreenName());
+ manageStudentMembership(student, studentAttributes);
+ studentIdx++;
+ long studentTime1 = System.currentTimeMillis();
+ _log.info("Student membership took "+(studentTime1 - studentTime0)+" ms : "+studentIdx + "/" + students.size());
+ _log.info(" ");
+ }
+ allENTUserIds.remove(student.getUserId());
+ }
+ long studentAll1 = System.currentTimeMillis();
+ int studentMembershipTime = (int)((studentAll1 - studentAll0)/60000);
+ SynchronizationReport.setStudentMembershipTime(studentMembershipTime);
+ _log.info("Whole student membership took "+studentMembershipTime+" minutes");
+ } catch (Exception e) {
+ _log.error("Error when managing student memberships ", e);
+ }
+ }
+
+ /**
+ * Manage membership for a given student
+ * @param student
+ * @param userAttributes
+ */
+ private static void manageStudentMembership(User student, Attributes userAttributes) {
+
+ try {
+ // Get existing orgs in which the student is member
+ List<Organization> existingOrgsList = OrganizationLocalServiceUtil.getUserOrganizations(student.getUserId());
+ _log.info("Manage membership for student "+student.getFullName()+" : "+existingOrgsList.size()+" existing classes and groups");
+
+ // This is the list of organization ids that need to be kept attached to the current user
+ List<Long> organizationIdsToKeep = new ArrayList<Long>();
+
+ Organization rattachSchool = SynchronizationManager.getRattachSchool(student);
+ if (rattachSchool == null) {
+ _log.error("No rattach school found for user "+student.getFullName());
+ return;
+ }
+ organizationIdsToKeep.add(rattachSchool.getOrganizationId());
+
+ // Skip user if its rattach school does not use 'AAF' datasource (but 'EDT' or 'UDT')
+ if (!isSchoolImportTypeAAF(rattachSchool.getOrganizationId())) {
+ _log.info("This school does not use AAF for membership synchronization => skipping user ...");
+ removeStudentOtherOrgs(student, rattachSchool, existingOrgsList);
+ return;
+ }
+
+ // Keep root org
+ organizationIdsToKeep.add(rootOrg.getOrganizationId());
+
+ // Keep manually added organizations
+ List<Long> manuallyAddedOrgIds = getManuallyAddedOrganizations(student);
+ organizationIdsToKeep.addAll(manuallyAddedOrgIds);
+
+ // Maps used for further new membership addition
+ List<Organization> newStudentOrgs = new ArrayList<Organization>();
+ Map<Long, UserGroup> newStudentUserGroupsMap = new HashMap<Long, UserGroup>();
+
+ // Get 'ENTEleveClasses' and 'ENTEleveGroupes' attributes
+ List<String> userClassesAndGroups = SynchronizationUtils.getUserClassesAndGroups(student, userAttributes);
+ _log.info("Manage membership for student "+student.getFullName()+" : "+userClassesAndGroups.size()+" new classes and groups");
+
+ // Add student membership to these groups and classes
+ for (String className : userClassesAndGroups) {
+
+ //_log.info("Manage membership for student "+student.getFullName()+" : new class/group is "+className);
+
+ UserGroup userGroup = UserGroupLocalServiceUtil.getUserGroup(companyId, className);
+ Map<String, Organization> map = SynchronizationUtils.checkAndGetOrganizationCreation(userGroup);
+ if (map.keySet().size() > 0) {
+ Organization studentOrg = map.get("classe");
+ newStudentOrgs.add(studentOrg);
+ newStudentUserGroupsMap.put(studentOrg.getOrganizationId(), userGroup);
+ }
+ }
+
+ List<Organization> newStudentOrgsCopy = new ArrayList<Organization>(newStudentOrgs);
+
+ // Will be used to determine if there are any changes in the student's membership
+ // and then if there will be changes for the parent
+ int nbRemovedOrg = 0;
+ int nbSkippedNewOrg = 0;
+
+ // Loop over the existing child organizations
+ for (Organization studentOrg : existingOrgsList) {
+
+ //_log.info("Testing membership for student "+student.getFullName()+" : testing for removal class/group "+studentOrg.getName());
+
+ // Skip archived organizations
+ if (!studentOrg.getPreferences().getValue("migrate", "none").equals("none")) {
+ //_log.info(" Skipping archived org "+studentOrg.getName());
+ continue;
+ }
+
+ // Skip organizations that we want to keep
+ boolean isToKeep = false;
+ for (Long orgIdToKeep : organizationIdsToKeep) {
+ if (studentOrg.getOrganizationId() == orgIdToKeep) {
+ isToKeep = true;
+ }
+ }
+ if (isToKeep) {
+ //_log.info(" Skipping org to keep : "+studentOrg.getName());
+ continue;
+ }
+
+ // Skip if organization was just added
+ isToKeep = false;
+ for (Organization newStudentOrg : newStudentOrgs) {
+ if (studentOrg.getOrganizationId() == newStudentOrg.getOrganizationId()) {
+ isToKeep = true;
+ newStudentOrgsCopy.remove(newStudentOrg);
+ }
+ }
+ if (isToKeep) {
+ //_log.info(" Skipping new school org "+studentOrg.getName());
+ nbSkippedNewOrg++;
+ continue;
+ }
+
+ _log.info("Remove student "+student.getUserId()+" from organization "+studentOrg.getName());
+
+ UserLocalServiceUtil.unsetOrganizationUsers(studentOrg.getOrganizationId(), new long[]{student.getUserId()});
+ nbRemovedOrg++;
+ SynchronizationReport.notifyStudentMembershipRemoval(student, studentOrg);
+ }
+
+ // Store if there was modifications (for parents)
+ if (nbRemovedOrg == 0 && nbSkippedNewOrg == userClassesAndGroups.size()) {
+ _log.info("There was no modification for child "+student.getFullName());
+ childModificationsMap.put(student.getUserId(), false);
+ } else {
+ _log.info("There was at least 1 modification for child "+student.getFullName()+" : "+nbRemovedOrg+" removed org, "+nbSkippedNewOrg+" skipped new orgs for "+userClassesAndGroups.size()+" new orgs.");
+ childModificationsMap.put(student.getUserId(), true);
+ }
+
+ // Add remaining organizations (except manually added)
+ for (Organization orgToAdd : newStudentOrgsCopy) {
+ if (manuallyAddedOrgIds.contains(orgToAdd.getOrganizationId())) {
+ _log.info("Do not add org "+orgToAdd.getName()+" to user "+student.getFullName()+" because was manually added");
+ continue;
+ }
+ UserLocalServiceUtil.addOrganizationUsers(orgToAdd.getOrganizationId(), new long[]{student.getUserId()});
+// UserGroup userGroup = newStudentUserGroupsMap.get(orgToAdd.getOrganizationId());
+// try {
+// UserLocalServiceUtil.addUserGroupUsers(userGroup.getUserGroupId(), new long[] { student.getUserId() });
+// } catch (Exception e) {
+// _log.error("Could not add student "+student.getFullName()+" into userGroup "+userGroup.getName());
+// }
+ _log.info("Add new organization "+orgToAdd.getName()+" to user "+student.getFullName());
+ SynchronizationReport.notifyStudentMembershipCreation(student, orgToAdd);
+ }
+ } catch (Exception e) {
+ _log.error("Error when managing memberships for student "+student.getFullName(), e);
+ e.printStackTrace();
+ }
+
+ }
+
+
+ /**
+ * Removes membership for all other organizations that are not rattach school or its child orgs
+ * @param rattachSchool
+ * @param studentOrgs
+ */
+ private static void removeStudentOtherOrgs(User student, Organization rattachSchool, List<Organization> studentOrgs) {
+
+ List<Long> manuallyAddedOrgIds = getManuallyAddedOrganizations(student);
+ for (Organization existingOrg : studentOrgs) {
+ try {
+ if (existingOrg.getOrganizationId() != rootOrg.getOrganizationId()
+ && existingOrg.getOrganizationId() != rattachSchool.getOrganizationId()
+ && existingOrg.getParentOrganizationId() != rattachSchool.getOrganizationId()) {
+
+ if (manuallyAddedOrgIds.contains(existingOrg.getOrganizationId())) {
+ //_log.info("+++++ KEEP manually added org "+existingOrg.getName()+" for student "+student.getFullName() + " whereas its rattach school is "+rattachSchool.getName());
+ } else if (OrganizationFilter.isArchived(existingOrg)) {
+ //_log.info("+++++ KEEP archived org "+existingOrg.getName()+" for student "+student.getFullName() + " whereas its rattach school is "+rattachSchool.getName());
+ } else {
+ _log.info("+++++ REMOVE Org "+existingOrg.getName()+" (id "+existingOrg.getOrganizationId()+") for student "+student.getFullName() + " whereas its rattach school is "+rattachSchool.getName());
+ childModificationsMap.put(student.getUserId(), true);
+ try {
+ UserLocalServiceUtil.unsetOrganizationUsers(existingOrg.getOrganizationId(), new long[]{student.getUserId()});
+ } catch (Exception e) {
+ _log.error("Error when removing student other membership", e);
+ }
+ }
+ }
+ } catch (Exception e) {
+ _log.error("Error when removing obsolete organization " + existingOrg.getName()+" for student "+student.getFullName(), e);
+ }
+ }
+ }
+
+
+ /**
+ * Manage memberships for all parents
+ */
+ private static void manageAllParentsMemberships() {
+
+ try {
+ Role parentRole = RoleLocalServiceUtil.getRole(companyId, ENTRolesConstants.NATIONAL_2);
+ List<User> parents = UserLocalServiceUtil.getRoleUsers(parentRole.getRoleId());
+ _log.info("Manage parent memberships : fetched "+parents.size()+" parents ...");
+
+ // Loop over parents
+ long parentAll0 = System.currentTimeMillis();
+ int parentIdx = 0;
+ for (User parent : parents) {
+
+ // Skip inactive users
+ if (parent.getStatus() == WorkflowConstants.STATUS_INACTIVE) {
+ continue;
+ }
+
+ long parentTime0 = System.currentTimeMillis();
+ manageParentMembership(parent);
+ allENTUserIds.remove(parent.getUserId());
+ parentIdx++;
+ long parentTime1 = System.currentTimeMillis();
+ _log.info("Parent membership took "+(parentTime1 - parentTime0)+" ms : "+parentIdx + "/" + parents.size());
+ }
+ long parentAll1 = System.currentTimeMillis();
+ int parentMembershipTime = (int)((parentAll1 - parentAll0)/60000);
+ SynchronizationReport.setParentMembershipTime(parentMembershipTime);
+ _log.info("Whole parent membership took "+parentMembershipTime+" minutes");
+ } catch (Exception e) {
+ _log.error("Error when managing memberships for all parents ", e);
+ }
+ }
+
+
+ /**
+ * Manage parent memberships:
+ * - loop over children orgs
+ * - add parent into parent-related organizations
+ * - get 'obsolete' organizations and remove them
+ * @param user
+ */
+ private static void manageParentMembership(User parent) {
+
+ _log.debug("Manage membership for parent "+parent.getUserId());
+
+ // This is the list of organization ids that need to be kept attached to the current user
+ List<Long> organizationIdsToKeep = new ArrayList<Long>();
+
+ // Keep root org
+ organizationIdsToKeep.add(rootOrg.getOrganizationId());
+
+ // Keep manually added organizations
+ List<Long> manuallyAddedOrgIds = getManuallyAddedOrganizations(parent);
+ organizationIdsToKeep.addAll(manuallyAddedOrgIds);
+
+
+ try {
+
+ List<UserRelationship> relations = UserRelationshipLocalServiceUtil.getUserRelationshipByToUserIdRelationType(parent.getUserId(), UserRelationshipConstants.PARENT_RELATION);
+ if (relations != null) {
+
+ // If no update was performed on children => skip parent
+ boolean modifDoneOnChildren = false;
+ for (UserRelationship relation : relations) {
+ if (childModificationsMap.containsKey(relation.getFromUserId()) && childModificationsMap.get(relation.getFromUserId())) {
+ modifDoneOnChildren = true;
+ }
+ }
+ if (!modifDoneOnChildren && !ENTMainUtilsLocalServiceUtil.isForceParentMembership(companyId)) {
+ _log.info("No need to continue with this parent because no update was done for its children : "+parent.getFullName());
+ return;
+ }
+
+ // Loop over child relationships to ADD all relationships
+ for (UserRelationship relation : relations) {
+ User child = UserLocalServiceUtil.getUser(relation.getFromUserId());
+ _log.info("Parent "+parent.getFullName()+"(id "+parent.getUserId()+") has child "+child.getFullName()+" (id "+child.getUserId()+")");
+
+ Organization rattachSchool = SynchronizationManager.getRattachSchool(child);
+ Organization parentRattachSchool = ENTOrganizationsUtil.getOrCreateOrganization(companyId, rattachSchool.getName() + OrganizationConstants.ORG_SUFFIX_PARENTS, rattachSchool.getOrganizationId());
+
+ organizationIdsToKeep.add(rattachSchool.getOrganizationId());
+ organizationIdsToKeep.add(parentRattachSchool.getOrganizationId());
+
+ // Save child-parent link for reporting
+ SynchronizationReport.addChildParentLink(child, parent);
+
+ // Add parent into its child organizations
+ List<Organization> childOrgList = OrganizationLocalServiceUtil.getUserOrganizations(relation.getFromUserId());
+ for (Organization childOrg : childOrgList) {
+
+ // Skip archived organizations
+ if (!childOrg.getPreferences().getValue("migrate", "none").equals("none")) {
+ continue;
+ }
+
+ // Skip root org
+ if (childOrg.getOrganizationId() == rootOrg.getOrganizationId()) {
+ continue;
+ }
+
+ Organization parentOrg = ENTOrganizationsUtil.getOrCreateOrganization(companyId, childOrg.getName() + OrganizationConstants.ORG_SUFFIX_PARENTS, childOrg.getOrganizationId());
+ SynchronizationUtils.addOrRemoveUserFromOrg(parent, childOrg, true, OrganizationConstants.ORG_SUFFIX_PARENTS);
+ organizationIdsToKeep.add(parentOrg.getOrganizationId());
+ _log.info("Added parent "+parent.getFullName()+" to organization "+childOrg.getName()+OrganizationConstants.ORG_SUFFIX_PARENTS+" (id "+parentOrg.getOrganizationId()+")");
+
+ }
+ }
+ }
+
+
+ // Now loop over existing organizations in order to REMOVE obsolete organizations
+
+ List<Organization> parentOrgs = OrganizationLocalServiceUtil.getUserOrganizations(parent.getUserId());
+ for (Organization parentOrg : parentOrgs) {
+
+ //_log.info(" Testing for removal organization "+parentOrg.getName());
+
+ // Skip archived organizations
+ if (!parentOrg.getPreferences().getValue("migrate", "none").equals("none")) {
+ continue;
+ }
+
+ // Skip organizations to keep
+ if (doKeepOrg(parentOrg, organizationIdsToKeep)) {
+ continue;
+ }
+
+ _log.info("Remove parent "+parent.getUserId()+" from organization "+parentOrg.getName());
+ UserLocalServiceUtil.unsetOrganizationUsers(parentOrg.getOrganizationId(), new long[]{parent.getUserId()});
+ }
+
+ } catch (Exception e) {
+ _log.error("Error while managing memberships for parent "+parent.getFullName(), e);
+ }
+
+ }
+
+
+ /**
+ * Manage memberships for all teachers
+ */
+ private static void manageAllTeachersMemberships() {
+
+ try {
+ Role teacherRole = RoleLocalServiceUtil.getRole(companyId, ENTRolesConstants.NATIONAL_3);
+ List<User> teachers = UserLocalServiceUtil.getRoleUsers(teacherRole.getRoleId());
+ _log.info("Manage teachers memberships : fetched "+teachers.size()+" teachers ...");
+
+ // Loop over teachers
+ long teacherAll0 = System.currentTimeMillis();
+ int teacherIdx = 0;
+ for (User teacher : teachers) {
+ if (SynchronizationManager.getUserAttributesMap().containsKey(teacher.getScreenName())) {
+
+ // Skip inactive teachers
+ if (teacher.getStatus() == WorkflowConstants.STATUS_INACTIVE) {
+ continue;
+ }
+
+ long teacherTime0 = System.currentTimeMillis();
+ Attributes teacherAttributes = SynchronizationManager.getUserAttributesMap().get(teacher.getScreenName());
+ manageTeacherMembership(teacher, teacherAttributes);
+ long teacherTime1 = System.currentTimeMillis();
+ teacherIdx++;
+ _log.info("Teacher membership took "+(teacherTime1 - teacherTime0)+" ms : " + teacherIdx+"/"+teachers.size());
+ }
+ allENTUserIds.remove(teacher.getUserId());
+ }
+ long teacherAll1 = System.currentTimeMillis();
+ int teacherMembershipTime = (int)((teacherAll1 - teacherAll0)/60000);
+ SynchronizationReport.setTeacherMembershipTime(teacherMembershipTime);
+ _log.info("Whole teacher membership took "+teacherMembershipTime+" minutes");
+ } catch (Exception e) {
+ _log.error("Error when managing memberships for all teachers ", e);
+ }
+ }
+
+
+ /**
+ * Manage memberships for a given teacher
+ * - get the required memberships, grouped by school
+ * - loop over all existing organizations, and remove the ones no longer needed
+ * - add the remaining organizations
+ * @param teacher
+ * @param teacherAttributes
+ */
+ private static void manageTeacherMembership(User teacher, Attributes teacherAttributes) {
+
+ try {
+ // Get existing orgs in which the teacher is member
+ List<Organization> existingOrgsList = OrganizationLocalServiceUtil.getUserOrganizations(teacher.getUserId());
+ _log.info("Manage membership for teacher "+teacher.getFullName()+" : "+existingOrgsList.size()+" existing classes and groups");
+
+ // This is the list of organization ids that need to be kept attached to the current user
+ List<Long> organizationIdsToKeep = new ArrayList<Long>();
+
+ // Map used for further new membership addition
+ List<Organization> newTeacherOrgs = new ArrayList<Organization>();
+
+ // Get the teacher groups and classes defined in the LDAP
+ // Key is the school orgId, value is the list of classes and groups in this school
+ Map<Organization, List<String>> teacherSchoolClassesMap = SynchronizationUtils.getTeacherSchoolClassesMap(teacher, teacherAttributes);
+
+ // List of org id for this user which as other type of import
+ List<Long> schoolIdIsNotAAFImport = new ArrayList<Long>();
+
+ for (Organization schoolOrg : teacherSchoolClassesMap.keySet()) {
+
+ _log.info(" Parsing school for teacher "+teacher.getFullName()+" : school is "+schoolOrg.getName());
+
+ // Skip this school if the school does not use 'AAF' datasource (but 'EDT' or 'UDT')
+ if (!isSchoolImportTypeAAF(schoolOrg.getOrganizationId())) {
+ schoolIdIsNotAAFImport.add(schoolOrg.getOrganizationId());
+ _log.info(" This school "+schoolOrg.getName()+" does not use AAF for membership synchronization => add teacher to school-level organizations only");
+ addUserToSchoolLevelOrgs(teacher, schoolOrg);
+ continue;
+ }
+
+ // Synchronize subjects
+ synchronizeSubjects(teacher, teacherAttributes, schoolOrg.getOrganizationId());
+
+ // Get the school-related orgs of the school org
+ List<Organization> schoolLevelOrgList = getSchoolLevelOrganizations(teacher, schoolOrg);
+ for (Organization orgaToKeep : schoolLevelOrgList) {
+ // Store for further addition
+ organizationIdsToKeep.add(orgaToKeep.getOrganizationId());
+ newTeacherOrgs.add(orgaToKeep);
+ }
+
+ if (teacherSchoolClassesMap.get(schoolOrg) == null) {
+ _log.info(" Manage membership for teacher "+teacher.getFullName()+" : has no class or group in school "+schoolOrg.getName());
+ continue;
+ } else {
+ _log.info(" Manage membership for teacher "+teacher.getFullName()+" : has "+teacherSchoolClassesMap.get(schoolOrg).size()+" classes/groups in school "+schoolOrg.getName());
+ }
+
+ // Loop over classes/groups of this organization
+ // This list may be empty => add teacher only into school-level orgs
+ for (String className : teacherSchoolClassesMap.get(schoolOrg)) {
+
+ UserGroup userGroup = UserGroupLocalServiceUtil.getUserGroup(companyId, className);
+ Map<String, Organization> map = SynchronizationUtils.checkAndGetOrganizationCreation(userGroup);
+ if (map.keySet().size() > 0) {
+
+ Organization teacherOrg = map.get("classe");
+ _log.info(" Manage membership for teacher "+teacher.getFullName()+" : new class/group is "+teacherOrg.getName());
+ // Store for further addition
+ newTeacherOrgs.add(teacherOrg);
+
+ // Get associated '- Parents' organization
+ if (isStudentClass(teacherOrg)) {
+ Organization newTeacherParentOrg = OrganizationLocalServiceUtil.getOrganization(companyId, teacherOrg.getName() + OrganizationConstants.ORG_SUFFIX_PARENTS);
+ _log.info(" Manage membership for teacher "+teacher.getFullName()+" : new class/group is "+newTeacherParentOrg.getName() + " (parent level)");
+ // Store for further addition
+ newTeacherOrgs.add(newTeacherParentOrg);
+ }
+ }
+ }
+ }
+
+ // Manage technical people (recorder as Nat_3 by default, but without classes nor groups)
+ if (teacherSchoolClassesMap.keySet().size() == 0) {
+ Organization rattachSchool = SynchronizationManager.getRattachSchool(teacher);
+ if (rattachSchool != null) {
+ List<Organization> schoolLevelOrgs = getSchoolLevelOrganizations(teacher, rattachSchool);
+ for (Organization schoolLevelOrg : schoolLevelOrgs) {
+ organizationIdsToKeep.add(schoolLevelOrg.getOrganizationId());
+ _log.info("This teacher has no class/group attached yet, adding org to the list of to-keep orgs: "+schoolLevelOrg.getName());
+ }
+ }
+ }
+
+
+ // Keep root organization
+ organizationIdsToKeep.add(rootOrg.getOrganizationId());
+
+ // Keep manually added organizations
+ List<Long> manuallyAddedOrgIds = getManuallyAddedOrganizations(teacher);
+ organizationIdsToKeep.addAll(manuallyAddedOrgIds);
+
+ // Synchronize main teacher role if needed and add this org list to the list of org ids to keep
+ List<Long> ppOrgIdList = synchronizeMainTeachersRole(teacher, teacherAttributes);
+ organizationIdsToKeep.addAll(ppOrgIdList);
+
+ List<Organization> newTeacherOrgsCopy = new ArrayList<Organization>(newTeacherOrgs);
+
+ // Loop over the existing teacher organizations
+ for (Organization teacherOrg : existingOrgsList) {
+
+ //verify if the org is in school which doesn't use AAF import
+ if(schoolIdIsNotAAFImport.contains(teacherOrg.getOrganizationId())
+ || schoolIdIsNotAAFImport.contains(teacherOrg.getParentOrganizationId())){
+ continue;
+ }
+
+
+ //_log.info(" Testing membership for teacher "+teacher.getFullName()+" : testing for removal class/group "+teacherOrg.getName());
+
+ // Skip if organization is in the membership list (let this test first)
+ boolean isToKeep = false;
+ for (Organization newTeacherOrg : newTeacherOrgs) {
+ if (teacherOrg.getOrganizationId() == newTeacherOrg.getOrganizationId()) {
+ isToKeep = true;
+ newTeacherOrgsCopy.remove(newTeacherOrg);
+ }
+ }
+ if (isToKeep) {
+ continue;
+ }
+
+ // Skip archived organizations
+ if (!teacherOrg.getPreferences().getValue("migrate", "none").equals("none")) {
+ continue;
+ }
+
+ // Skip organizations to keep
+ if (doKeepOrg(teacherOrg, organizationIdsToKeep)) {
+ continue;
+ }
+
+ _log.info(" Remove teacher "+teacher.getFullName()+" from organization "+teacherOrg.getName());
+ UserLocalServiceUtil.unsetOrganizationUsers(teacherOrg.getOrganizationId(), new long[]{teacher.getUserId()});
+ SynchronizationReport.notifyTeacherMembershipRemoval(teacher, teacherOrg);
+
+ // Also remove teacher from associated parent org
+ try {
+ if (isStudentClass(teacherOrg)) {
+ Organization teacherParentOrg = OrganizationLocalServiceUtil.getOrganization(companyId, teacherOrg.getName() + OrganizationConstants.ORG_SUFFIX_PARENTS);
+ UserLocalServiceUtil.unsetOrganizationUsers(teacherParentOrg.getOrganizationId(), new long[]{teacher.getUserId()});
+ _log.info(" Remove teacher "+teacher.getFullName()+" from organization "+teacherParentOrg.getName());
+ }
+ } catch (Exception e) {
+ _log.error("Error when removing teacher "+teacher.getFullName()+" from parent-related organization of org "+teacherOrg.getName());
+ }
+ }
+
+ // Add remaining organizations
+ for (Organization orgToAdd : newTeacherOrgsCopy) {
+
+ //verify if the org is in school which doesn't use AAF import
+ if(schoolIdIsNotAAFImport.contains(orgToAdd.getOrganizationId())
+ || schoolIdIsNotAAFImport.contains(orgToAdd.getParentOrganizationId())){
+ continue;
+ }
+
+ UserLocalServiceUtil.addOrganizationUsers(orgToAdd.getOrganizationId(), new long[]{teacher.getUserId()});
+ _log.info(" Add teacher "+teacher.getUserId()+" to organization "+orgToAdd.getName());
+ SynchronizationReport.notifyTeacherMembershipCreation(teacher, orgToAdd);
+
+ // Also add teacher into associated parent org
+ try {
+ if (isStudentClass(orgToAdd)) {
+ Organization teacherParentOrgToAdd = OrganizationLocalServiceUtil.getOrganization(companyId, orgToAdd.getName() + OrganizationConstants.ORG_SUFFIX_PARENTS);
+ UserLocalServiceUtil.addOrganizationUsers(teacherParentOrgToAdd.getOrganizationId(), new long[]{teacher.getUserId()});
+ _log.info(" Add teacher "+teacher.getUserId()+" to organization "+teacherParentOrgToAdd.getName());
+ }
+ } catch (Exception e) {
+ _log.error("Error when adding teacher "+teacher.getFullName()+" into parent-related organization of org "+orgToAdd.getName());
+ }
+
+ }
+
+ } catch (Exception e) {
+ _log.error("Error when managing memberships for teacher "+teacher.getFullName(), e);
+ }
+
+ }
+
+ /**
+ * Fetch groups and classes where the teacher is main teacher
+ * return the list of orgs he is main teacher
+ * @param teacher
+ * @param attrs
+ */
+ private static List<Long> synchronizeMainTeachersRole (final User teacher, final Attributes attrs) {
+
+ _log.info("Start synchronizing main teacher role ...");
+ // The list of oraganizations in which the teacher is PP
+ List<Long> ppOrgIdList = new ArrayList<Long>();
+
+ try {
+ // Manage principal teachers
+ Role ppRole = RoleLocalServiceUtil.getRole(teacher.getCompanyId(), ENTRolesConstants.PROF_PRINCIPAL);
+ Role orgPPRole = RoleLocalServiceUtil.getRole(teacher.getCompanyId(), ENTRolesConstants.ORGANIZATION_PROF_PRINCIPAL);
+
+ // Build the list of existing groupIds where the teacher is PP
+ // From this list we will remove the new groupIds where the teacher is PP
+ // At the end, we will remove all remaining obsolete groupIds
+ List<UserGroupRole> existingUserGrpR = UserGroupRoleLocalServiceUtil.getUserGroupRoles(teacher.getUserId());
+ List<Long> ppGrpList = new ArrayList<Long>();
+ for (UserGroupRole ugr: existingUserGrpR){
+ if (ugr.getRoleId() == orgPPRole.getRoleId()) {
+ ppGrpList.add(ugr.getGroupId());
+ _log.info("Teacher "+teacher.getFullName()+" is currently PP for groupId "+ugr.getGroupId());
+ }
+ }
+
+ boolean needGlobalPPRole = false;
+
+ if (attrs.get(LDAPConstants.LDAP_ATTRIBUTE_ENTAUXENSCLASSESPRINCIPAL) != null){
+
+ for (int i=0; i < attrs.get(LDAPConstants.LDAP_ATTRIBUTE_ENTAUXENSCLASSESPRINCIPAL).size(); i++) {
+ try{
+ String profPrincClasse = attrs.get(LDAPConstants.LDAP_ATTRIBUTE_ENTAUXENSCLASSESPRINCIPAL).get(i).toString();
+
+ // Get class name from attribute
+ profPrincClasse = profPrincClasse.substring(3, profPrincClasse.indexOf(","));
+ if (profPrincClasse.equals("fakeDn")) {
+ break;
+ }
+ UserGroup ppClasseUserGroup = UserGroupLocalServiceUtil.getUserGroup(teacher.getCompanyId(), profPrincClasse);
+ Map<String, Organization> ppClasseOrg = SynchronizationUtils.checkAndGetOrganizationCreation(ppClasseUserGroup);
+ if (ppClasseOrg.keySet().size() > 0) {
+
+ Organization ppClasse = ppClasseOrg.get("classe");
+ Long ppUGId = ppClasse.getGroup().getGroupId();
+ _log.info("Teacher "+teacher.getFullName()+" is main teacher of class "+ppClasse.getName());
+
+ // Skip this org if the school org does not use 'AAF' datasource (but 'EDT' or 'UDT')
+ if(!isSchoolImportTypeAAF(ppClasse.getParentOrganizationId())){
+ _log.info("School is not AAF mode => skipping PP role for this teacher");
+ continue;
+ }
+ needGlobalPPRole = true;
+
+ // Output list
+ ppOrgIdList.add(ppClasse.getOrganizationId());
+
+ if (ppGrpList.contains(ppUGId)) {
+ ppGrpList.remove(ppUGId);
+ } else {
+ // Add teacher to org if needed
+ if (!OrganizationLocalServiceUtil.hasUserOrganization(teacher.getUserId(), ppClasse.getOrganizationId())) {
+ UserLocalServiceUtil.addOrganizationUsers(ppClasse.getOrganizationId(), new long[]{teacher.getUserId()});
+ SynchronizationReport.notifyTeacherMembershipCreation(teacher, ppClasse);
+ }
+ // Add role to the user group
+ UserGroupRoleLocalServiceUtil.addUserGroupRoles(teacher.getUserId(), ppUGId, new long[] {orgPPRole.getRoleId()});
+ _log.info(" Adding teacher "+teacher.getFullName()+" main teacher of class "+ppClasse.getName());
+ }
+ }
+ }
+ catch (Exception exc) {
+ _log.error("Error when adding role to user "+teacher.getFullName(), exc);
+ }
+ }
+ }
+
+ // Remove PP role in obsolete groupIds
+ for (Long grpIdLoc: ppGrpList) {
+ try{
+ _log.info("Delete PP role for teacher "+teacher.getFullName()+" in groupId "+grpIdLoc);
+ UserGroupRoleLocalServiceUtil.deleteUserGroupRoles(teacher.getUserId(), grpIdLoc, new long[] {orgPPRole.getRoleId()});
+ }
+ catch(Exception e){
+ _log.error(e);
+ }
+ }
+
+ // Manage global PP role
+ Boolean isPPGlobal = RoleLocalServiceUtil.hasUserRole(teacher.getUserId(), ppRole.getRoleId());
+ if (needGlobalPPRole && !isPPGlobal) {
+ RoleLocalServiceUtil.addUserRoles(teacher.getUserId(), new long[] {ppRole.getRoleId()});
+ _log.info("Adding global PP role to teacher "+teacher.getFullName());
+ } else if (!needGlobalPPRole && isPPGlobal){
+ RoleLocalServiceUtil.unsetUserRoles(teacher.getUserId(), new long[] {ppRole.getRoleId()});
+ _log.info("Removing global PP role to teacher "+teacher.getFullName());
+ }
+ } catch (Exception e) {
+ _log.error("Error when managing principal teacher role for teacher "+teacher.getFullName(), e);
+ }
+ _log.info("Teacher "+teacher.getFullName()+ " is main teacher for "+ppOrgIdList.size()+ " classes");
+ return ppOrgIdList;
+ }
+
+
+
+ /**
+ * Manage memberships for all other users (non-student, non-parent and non-teachers)
+ */
+ private static void manageAllOtherProfilesMemberships () {
+
+ try {
+ long otherAll0 = System.currentTimeMillis();
+ _log.info("At this point, remains "+allENTUserIds.size()+" users to manage membership ...");
+ for (Long userId: allENTUserIds) {
+
+ User user = UserLocalServiceUtil.getUser(userId);
+
+ // Skip inactive users
+ if (user.getStatus() == WorkflowConstants.STATUS_INACTIVE) {
+ continue;
+ }
+
+ Attributes userAttributes = SynchronizationManager.getUserAttributesMap().get(user.getScreenName());
+
+ manageOtherProfilesMembership(user, userAttributes);
+ }
+ long otherAll1 = System.currentTimeMillis();
+ int otherMembershipTime = (int)((otherAll1 - otherAll0)/1000);
+ SynchronizationReport.setOtherMembershipTime(otherMembershipTime);
+ _log.info("Whole other membership took "+otherMembershipTime+" seconds");
+ } catch (Exception e) {
+ _log.error("Error when managing memberships for all other profiles ", e);
+ }
+ }
+
+
+ /**
+ * Manage memberships for a given user
+ * @param user
+ * @param userAttributes
+ */
+ private static void manageOtherProfilesMembership(User user, Attributes userAttributes) {
+
+ _log.info("Managing memberships for user "+user.getFullName()+" (id "+user.getUserId()+")");
+ try {
+
+ List<Organization> newUserOrgs = new ArrayList<Organization>();
+
+ List<Long> organizationIdsToKeep = new ArrayList<Long>();
+ organizationIdsToKeep.add(rootOrg.getOrganizationId());
+
+ // Get all ENTPersonFonction attributes, get associated schools and school-level organizations
+ // And add them to the keep list
+ List<Organization> entPersonFonctionOrgs = SynchronizationUtils.getEntPersonFonctionsSchools(user, userAttributes);
+ for (Organization entPersonFonctionOrg : entPersonFonctionOrgs) {
+ List<Organization> schoolLevelOrgs = getSchoolLevelOrganizations(user, entPersonFonctionOrg);
+ for (Organization schoolLevelOrg : schoolLevelOrgs) {
+ newUserOrgs.add(schoolLevelOrg);
+ }
+ }
+
+ if (newUserOrgs.isEmpty()) {
+ Organization etabRattachement = SynchronizationManager.getRattachSchool(user);
+ if (etabRattachement == null) {
+ _log.error("No attached etab in LDAP for user " + user.getFullName() + " (login " + user.getLogin()+")");
+ return;
+ }
+
+ List<Organization> schoolLevelOrgs = getSchoolLevelOrganizations(user, etabRattachement);
+ for (Organization schoolLevelOrg : schoolLevelOrgs) {
+ if (!organizationIdsToKeep.contains(schoolLevelOrg.getOrganizationId())) {
+ organizationIdsToKeep.add(schoolLevelOrg.getOrganizationId());
+ }
+ }
+ }
+
+ // Keep manually added organizations
+ List<Long> manuallyAddedOrgIds = getManuallyAddedOrganizations(user);
+ organizationIdsToKeep.addAll(manuallyAddedOrgIds);
+
+ List<Organization> newUserOrgsCopy = new ArrayList<Organization>(newUserOrgs);
+
+ // Get current user organizations
+ List<Organization> userOrgs = OrganizationLocalServiceUtil.getUserOrganizations(user.getUserId());
+
+ // Loop over user's classes and groups
+ for (Organization userOrg: userOrgs) {
+
+ // Skip archived organizations
+ if (!userOrg.getPreferences().getValue("migrate", "none").equals("none")) {
+ //_log.info(" Skipping archived org "+teacherOrg.getName());
+ continue;
+ }
+
+ // Skip if organization is in the membership list
+ boolean isToKeep = false;
+ for (Organization newUserOrg : newUserOrgs) {
+ if (newUserOrg.getOrganizationId() == userOrg.getOrganizationId()) {
+ isToKeep = true;
+ newUserOrgsCopy.remove(userOrg);
+ }
+ }
+ if (isToKeep) {
+ //_log.info(" Skipping org to keep : "+userOrg.getName());
+ continue;
+ }
+
+ if (doKeepOrg(userOrg, organizationIdsToKeep)) {
+ //_log.info(" Skipping org id to keep : "+userOrg.getName());
+ continue;
+ }
+
+ _log.info("Remove user "+user.getUserId()+" from organization "+userOrg.getName());
+
+ UserLocalServiceUtil.unsetOrganizationUsers(userOrg.getOrganizationId(), new long[]{user.getUserId()});
+
+ // Report
+ SynchronizationReport.notifyOtherMembershipRemoval(user, userOrg);
+ }
+
+ // Now loop over the etabs which the user should not belong anymore
+ for (Organization orgToAdd: newUserOrgsCopy) {
+
+ UserLocalServiceUtil.addOrganizationUsers(orgToAdd.getOrganizationId(), new long[]{user.getUserId()});
+ _log.info("Add new organization "+orgToAdd.getName()+" to user "+user.getFullName());
+
+ // Report
+ SynchronizationReport.notifyOtherMembershipCreation(user, orgToAdd);
+
+ }
+ } catch (Exception e) {
+ _log.error("Error when managing other profile membership for user "+user.getFullName(), e);
+ }
+ }
+
+
+ /**
+ * Method called during manual user creation, that allows to synchronize its memberships
+ */
+ public static boolean manageUserMembership(long _companyId, Attributes userAttributes) {
+
+ companyId = _companyId;
+
+ try {
+ // Get screenName (ENTPersonLogin)
+ if (userAttributes.get(LDAPConstants.LDAP_ATTRIBUTE_ENTPERSONLOGIN) != null) {
+ String login = userAttributes.get(LDAPConstants.LDAP_ATTRIBUTE_ENTPERSONLOGIN).get().toString();
+ _log.info("manageUserMembership : login="+login);
+ User user = UserLocalServiceUtil.getUserByScreenName(companyId, login);
+
+ if (userAttributes.get(LDAPConstants.LDAP_ATTRIBUTE_ENTPERSONLOGIN) == null || userAttributes.get(LDAPConstants.LDAP_ATTRIBUTE_ENTPERSONSTRUCTRATTACH).get().toString() == null) {
+ _log.error("Manual user creation : membership sync impossible because user does not have ENTPersonLogin or ENTPersonStructRattach attribute");
+ return false;
+ }
+ initMembership(companyId);
+ SynchronizationManager.initUserRattachSchoolMap();
+ SynchronizationManager.getUserRattachSchoolMap().put(userAttributes.get(LDAPConstants.LDAP_ATTRIBUTE_ENTPERSONLOGIN).get().toString(), userAttributes.get(LDAPConstants.LDAP_ATTRIBUTE_ENTPERSONSTRUCTRATTACH).get().toString());
+
+ List<Role> roleList = RoleLocalServiceUtil.getUserRoles(user.getUserId());
+ for (Role role : roleList) {
+ _log.info("User "+user.getFullName()+" has role "+role.getName());
+ }
+
+ if (RoleLocalServiceUtil.hasUserRole(user.getUserId(), RoleLocalServiceUtil.getRole(_companyId, ENTRolesConstants.NATIONAL_1).getRoleId())) {
+ //if (RoleLocalServiceUtil.hasUserRole(user.getUserId(), companyId, ENTRolesConstants.NATIONAL_1, false)) {
+ manageStudentMembership(user, userAttributes);
+ } else if (RoleLocalServiceUtil.hasUserRole(user.getUserId(), RoleLocalServiceUtil.getRole(_companyId, ENTRolesConstants.NATIONAL_3).getRoleId())) {
+ manageTeacherMembership(user, userAttributes);
+ } else {
+ manageOtherProfilesMembership(user, userAttributes);
+ }
+ }
+ } catch (Exception e) {
+ _log.error("Error when synchronizing manually created user ", e);
+ return false;
+ }
+ return true;
+ }
+
+
+ /**
+ * Returns the list of school-level organizations for given user and school
+ * School-level orgs are the '-Parent' or '- Personnels' or '- Enseignants' sub-orgs
+ * @param companyId
+ * @param user
+ */
+ private static List<Organization> getSchoolLevelOrganizations (User user, Organization school) {
+
+ Boolean hasTeacherGrp = false;
+ Boolean hasPersonnelGrp = false;
+ Boolean hasParentGrp = false;
+ Boolean analyzeSchoolComplex = false;
+
+ long userId = user.getUserId();
+
+ List<Organization> resultOrgList = new ArrayList<Organization>();
+ resultOrgList.add(school);
+
+ try {
+
+
+ // Get user roles
+ List<Role> userRolesList = RoleLocalServiceUtil.getUserRoles(userId);
+
+ // Loop over the user's roles to detect school-level orgs
+ for (Role userRole : userRolesList) {
+ // Student and parents => quit
+ if (userRole.getName().equals(ENTRolesConstants.NATIONAL_1)) {
+ return resultOrgList;
+ }
+
+ // Parent group
+ if (userRole.getName().equals(ENTRolesConstants.NATIONAL_2)
+ || userRole.getName().equals(ENTRolesConstants.NATIONAL_4)
+ || userRole.getName().equals(ENTRolesConstants.NATIONAL_25)) {
+ hasParentGrp = true;
+ }
+
+ // Teacher group
+ if (userRole.getName().equals(ENTRolesConstants.NATIONAL_3)
+ || userRole.getName().equals(ENTRolesConstants.NATIONAL_24)
+ || userRole.getName().equals(ENTRolesConstants.NATIONAL_25)) {
+ hasTeacherGrp = true;
+ }
+
+ // Personnel group
+ if (userRole.getName().equals(ENTRolesConstants.NATIONAL_3)
+ || userRole.getName().equals(ENTRolesConstants.NATIONAL_4)
+ || userRole.getName().equals(ENTRolesConstants.NATIONAL_5)
+ || userRole.getName().equals(ENTRolesConstants.NATIONAL_6)
+ || userRole.getName().equals(ENTRolesConstants.NATIONAL_7)
+ || userRole.getName().equals(ENTRolesConstants.NATIONAL_25)) {
+ hasPersonnelGrp = true;
+ }
+
+ // School complex
+ if (userRole.getName().equals(ENTRolesConstants.NATIONAL_4)
+ || userRole.getName().equals(ENTRolesConstants.NATIONAL_5)
+ || userRole.getName().equals(ENTRolesConstants.NATIONAL_6)
+ || userRole.getName().equals(ENTRolesConstants.NATIONAL_7)) {
+ analyzeSchoolComplex = true;
+ }
+ }
+
+
+ // Parent school org
+ Organization parentOrg = ENTOrganizationsUtil.getOrCreateOrganization(user.getCompanyId(), school.getName() + OrganizationConstants.ORG_SUFFIX_PARENTS, school.getOrganizationId());
+ if (hasParentGrp) {
+ resultOrgList.add(parentOrg);
+ }
+
+ // Teacher school org
+ Organization teacherOrg = ENTOrganizationsUtil.getOrCreateOrganization(user.getCompanyId(), school.getName() + OrganizationConstants.ORG_SUFFIX_TEACHERS, school.getOrganizationId());
+ if (hasTeacherGrp) {
+ resultOrgList.add(teacherOrg);
+ }
+
+ // Personnel school org
+ Organization personnelOrg = ENTOrganizationsUtil.getOrCreateOrganization(user.getCompanyId(), school.getName() + OrganizationConstants.ORG_SUFFIX_PERSONNELS, school.getOrganizationId());
+ if (hasPersonnelGrp) {
+ resultOrgList.add(personnelOrg);
+ }
+
+
+ // If school complex and profile is National_4,5,6,7 => add user into other etabs from the school complex
+ if (analyzeSchoolComplex) {
+
+ List<Long> etabIdList = SynchronizationManager.getSchoolComplexAttachedEtabs(school.getOrganizationId());
+ for (Long etabIdToAttach : etabIdList) {
+ _log.info("Adding user "+user.getFullName()+" to cite scolaire etab "+etabIdToAttach);
+ Organization schoolComplexOrg = OrganizationLocalServiceUtil.getOrganization(etabIdToAttach);
+ resultOrgList.add(schoolComplexOrg);
+ }
+ }
+
+
+ } catch (Exception e) {
+ _log.error("Error when synchronizing school-level organizations for user "+user.getFullName()+ "(id "+user.getUserId()+")", e);
+ }
+ return resultOrgList;
+ }
+
+ /**
+ * Adds a given user to all school-level organization for given school, depending on its role
+ * @param user
+ * @param school
+ */
+ private static void addUserToSchoolLevelOrgs(User user, Organization school) {
+
+ List<Organization> schoolLevelOrgs = getSchoolLevelOrganizations(user, school);
+ for (Organization schoolLevelOrg : schoolLevelOrgs) {
+ try {
+ if (!UserLocalServiceUtil.hasOrganizationUser(schoolLevelOrg.getOrganizationId(), user.getUserId()) ) {
+ UserLocalServiceUtil.addOrganizationUsers(schoolLevelOrg.getOrganizationId(), new long[]{user.getUserId()});
+ _log.info(" Add user "+user.getUserId()+" to school-level organization "+schoolLevelOrg.getName());
+ SynchronizationReport.notifyTeacherMembershipCreation(user, schoolLevelOrg);
+ }
+ } catch (Exception e) {
+ _log.error("Error when adding user toschool-level organization for school "+school.getName(), e);
+ }
+ }
+ }
+
+ /**
+ * Returns true if the given organization is a student-level class or group
+ * @param org
+ * @return
+ */
+ private static boolean isStudentClass (Organization org) {
+ return (!org.getName().endsWith(OrganizationConstants.ORG_SUFFIX_PARENTS)
+ && !org.getName().endsWith(OrganizationConstants.ORG_SUFFIX_TEACHERS)
+ && !org.getName().endsWith(OrganizationConstants.ORG_SUFFIX_PERSONNELS));
+ }
+
+ /**
+ * Returns true if the organization is a school
+ * @param org
+ * @return
+ */
+ private static boolean isSchool(Organization org) {
+ try {
+ Organization parentOrg = org.getParentOrganization();
+ if (parentOrg != null && parentOrg.getParentOrganizationId() == 0) {
+ return true;
+ }
+ } catch (Exception e) {
+ _log.error("Error when getting parent organization for org "+org.getName());
+ }
+ return false;
+ }
+
+ /**
+ * Returns the list of organizations ids manually added to the user userId
+ * @param userId
+ * @return the list of org ids
+ */
+ private static List<Long> getManuallyAddedOrganizations(User user) {
+
+ List<Long> manuallyAddedOrgIds = new ArrayList<Long>();
+ try {
+ Role orgUserRole = RoleLocalServiceUtil.getRole(companyId, ENTRolesConstants.ORGANIZATION_USER);
+
+ List<UserGroupRole> userGroupRoleList = UserGroupRoleLocalServiceUtil.getUserGroupRoles(user.getUserId());
+ for (UserGroupRole userGroupRole : userGroupRoleList) {
+ if (userGroupRole.getRoleId() == orgUserRole.getRoleId()) {
+ Group group = GroupLocalServiceUtil.getGroup(userGroupRole.getGroupId());
+ Organization manualOrg = OrganizationLocalServiceUtil.getOrganization(group.getClassPK());
+
+ // Skip archived organizations
+ if (!manualOrg.getPreferences().getValue("migrate", "none").equals("none")) {
+ continue;
+ }
+
+ _log.info(" Found manually added organization is "+manualOrg.getName());
+ manuallyAddedOrgIds.add(manualOrg.getOrganizationId());
+
+ // Add school-level organizations (for schools only)
+ if (isSchool(manualOrg)) {
+ List<Organization> schoolLevelOrgList = getSchoolLevelOrganizations(user, manualOrg);
+ for (Organization schoolLevelOrg : schoolLevelOrgList) {
+ _log.info(" Found manually added organization is "+schoolLevelOrg.getName());
+ manuallyAddedOrgIds.add(schoolLevelOrg.getOrganizationId());
+ }
+ }
+ }
+ }
+ } catch (Exception e) {
+ _log.error("Error when fetching manually added organizations for user "+user.getFullName());
+ }
+ return manuallyAddedOrgIds;
+ }
+
+ /**
+ * Synchronizes subjects. Based on attribute ENTAuxEnsMatiereEnseignEtab
+ *
+ * @param user
+ * @param atts
+ */
+ private static void synchronizeSubjects(User user, Attributes atts, long schoolId)
+ throws UserSynchronizationException {
+
+ try {
+ // Get attributes ENTAuxEnsMatiereEnseignEtab
+ if (atts.get(LDAPConstants.LDAP_ATTRIBUTE_ENTAUXENSMATIEREENSEIGNETAB) != null) {
+
+ Attribute entAuxEnsMatiereEnseignEtab = atts.get(LDAPConstants.LDAP_ATTRIBUTE_ENTAUXENSMATIEREENSEIGNETAB);
+
+ // Get all matieres
+ for (int i=0; i<entAuxEnsMatiereEnseignEtab.size(); i++) {
+
+ final String fullSubject = entAuxEnsMatiereEnseignEtab.get(i).toString();
+ if (fullSubject.startsWith("cn=fakeDn")) {
+ continue;
+ }
+ System.out.println("synchronize subject : teacher "+user.getFullName()+" has subject "+fullSubject);
+ final String[] spl = fullSubject.split("\\$");
+ String subjectDn = spl[1];
+ String[] subjectTab = subjectDn.split(",");
+ String subjectLongLabel = subjectTab[0];
+
+ // Check if subject exists, if not, create it
+ Subject subject = SubjectLocalServiceUtil.getSubjectByLongLabel(subjectLongLabel);
+ if (subject == null) {
+ _log.error("Subject with long label "+subjectLongLabel+" does not exist in database");
+ continue;
+ }
+
+ // Check if subject already exists for this school
+ // If not, create it
+ List<SchoolSubject> schoolSubjectList = SchoolSubjectLocalServiceUtil.getSubjectsForSchoolSubject(schoolId, subject.getSubjectId());
+ if (schoolSubjectList == null || schoolSubjectList.size() == 0) {
+ SchoolSubjectLocalServiceUtil.createSchoolSubject(subject.getSubjectId(), schoolId, CDTColorUtil.getRandomColor());
+ }
+
+ }
+ }
+
+ } catch (Exception e) {
+ _log.error("Error when synchronizing subjects for teacher "+user.getFullName());
+ e.printStackTrace();
+ throw new UserSynchronizationException(user);
+ }
+ }
+
+
+
+ /**
+ * Is this school configured to use 'AAF' for data source membership ?
+ * @param schoolOrgId
+ * @return true if AAF mode for this school
+ */
+ public static boolean isSchoolImportTypeAAF(long schoolOrgId) {
+ // Default mode is currently AAF, because no EDT is available for 2015-2016 schoolyear
+ return (schoolsUsingAAFMode.contains(schoolOrgId));
+ }
+
+ /**
+ * Init all variables for membership
+ */
+ private static void initMembership(long companyId) {
+
+ initAAFMode();
+
+ childModificationsMap = new HashMap<Long, Boolean>();
+ try {
+ rootOrg = ENTOrganizationsUtil.getOrCreateRootOrg(companyId);
+ } catch (Exception e) {
+ _log.error("Error while retrieving root org", e);
+ }
+ }
+
+
+ /**
+ * Init the list of schools using AAF mode for membership management
+ */
+ private static void initAAFMode() {
+
+ schoolsUsingAAFMode = new ArrayList<Long>();
+ try {
+ List<SynchroConfig> synchroConfigList = SynchroConfigLocalServiceUtil.getSynchroConfigs(QueryUtil.ALL_POS, QueryUtil.ALL_POS);
+ for (SynchroConfig schoolConfig : synchroConfigList) {
+ if (schoolConfig.getDataSource().equals("AAF") && schoolConfig.getConfigDone()) {
+ _log.info("Adding school org "+schoolConfig.getEtabName()+" to AAF mode");
+ schoolsUsingAAFMode.add(schoolConfig.getEtabId());
+ }
+ }
+ } catch (Exception e) {
+ _log.error("Error when checking the list of schools that use AAF as datasource for membership.", e);
+ }
+ }
+
+
+ /**
+ * Does the given org belong to the orgId list ?
+ * @param org
+ * @param orgsToKeep
+ * @return
+ */
+ private static boolean doKeepOrg(Organization org, List<Long> orgIdsToKeep) {
+
+ for (Long orgIdToKeep : orgIdsToKeep) {
+ if (org.getOrganizationId() == orgIdToKeep) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+
+}