001/*
002 *  Copyright 2018 Anyware Services
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.ametys.plugins.userdirectory.synchronize;
017
018import java.util.ArrayList;
019import java.util.HashMap;
020import java.util.LinkedHashMap;
021import java.util.List;
022import java.util.Map;
023import java.util.Optional;
024import java.util.Set;
025
026import org.apache.avalon.framework.service.ServiceException;
027import org.apache.avalon.framework.service.ServiceManager;
028import org.apache.commons.lang3.StringUtils;
029import org.apache.commons.lang3.tuple.Triple;
030import org.slf4j.Logger;
031
032import org.ametys.cms.content.references.OutgoingReferencesExtractor;
033import org.ametys.cms.data.ContentSynchronizationResult;
034import org.ametys.cms.repository.Content;
035import org.ametys.cms.repository.ContentQueryHelper;
036import org.ametys.cms.repository.ContentTypeExpression;
037import org.ametys.cms.repository.LanguageExpression;
038import org.ametys.cms.repository.ModifiableContent;
039import org.ametys.cms.repository.WorkflowAwareContent;
040import org.ametys.cms.workflow.AbstractContentWorkflowComponent;
041import org.ametys.cms.workflow.EditContentFunction;
042import org.ametys.core.user.CurrentUserProvider;
043import org.ametys.plugins.contentio.synchronize.impl.SQLSynchronizableContentsCollection;
044import org.ametys.plugins.contentio.synchronize.workflow.EditSynchronizedContentFunction;
045import org.ametys.plugins.repository.AmetysObjectIterable;
046import org.ametys.plugins.repository.AmetysObjectIterator;
047import org.ametys.plugins.repository.data.holder.ModifiableModelLessDataHolder;
048import org.ametys.plugins.repository.data.holder.values.SynchronizationResult;
049import org.ametys.plugins.repository.query.expression.AndExpression;
050import org.ametys.plugins.repository.query.expression.Expression;
051import org.ametys.plugins.repository.query.expression.Expression.Operator;
052import org.ametys.plugins.repository.query.expression.ExpressionContext;
053import org.ametys.plugins.repository.query.expression.StringExpression;
054import org.ametys.plugins.userdirectory.DeleteOrgUnitComponent;
055import org.ametys.plugins.userdirectory.OrganisationChartPageHandler;
056import org.ametys.plugins.userdirectory.UserDirectoryPageHandler;
057import org.ametys.plugins.workflow.AbstractWorkflowComponent;
058import org.ametys.runtime.i18n.I18nizableText;
059import org.ametys.runtime.model.View;
060import org.ametys.runtime.model.type.ModelItemTypeConstants;
061
062import com.opensymphony.workflow.InvalidActionException;
063import com.opensymphony.workflow.WorkflowException;
064
065/**
066 * Synchronizable collection for UD Orgunits
067 */
068public class SQLSynchronizableUDOrgunitCollection extends SQLSynchronizableContentsCollection
069{
070    /** The internal data name for orgUnit remote sql id */
071    public static final String ORGUNIT_REMOTE_ID_INTERNAL_DATA = "orgunit-remote-id";
072    
073    /** The result map key for number of deleted contents */
074    public static final String RESULT_NB_SYNCHRONIZED_ORGUNITS_RELATIONS = "nbSynchronizedOrgUnitsRelations";
075
076    private static final String __PARAM_SQL_TABLE_USER = "tableNameUser";
077    private static final String __PARAM_SQL_ORGUNIT_JOIN_COLUMN_NAME = "orgUnitJoinColumnName";
078    private static final String __PARAM_LOGIN_USER_ATTRIBUTE_NAME = "loginUser";
079    private static final String __PARAM_SQL_LOGIN_USER_COLUMN_NAME = "loginColumnName";
080    private static final String __PARAM_SQL_ROLE_USER_COLUMN_NAME = "roleColumnName";
081    private static final String __PARAM_SQL_ORGUNIT_REMOTE_ID_COLUMN_NAME = "orgunitRemoteIdColumnName";
082    private static final String __PARAM_SQL_USER_SORT_COLUMN_NAME = "sortColumnName";
083    private static final String __PARAM_SQL_USER_SORT_PREVAIL_NAME = "sortPrevail";
084    
085    /** The map which link orgunit with this parent */
086    protected Map<String, String> _orgUnitParents;
087    
088    /** The map which link users (userId and role) to their orgunits */
089    protected Map<String, Map<String, String>> _usersByOrgUnitId;
090    
091    /** The map which link orgunit with sql remote ids */
092    protected Map<String, String> _orgUnitRemoteIds;
093    
094    
095    /** Number of updated contents for parent-child org unit relation */
096    protected Integer _nbSynchronizedOrgUnit;
097    
098    /** The sql user search DAO */
099    protected SQLUserSearchDAO _sqlUserDAO;
100    
101    /** The organisation chart page handler */
102    protected OrganisationChartPageHandler _orgChartPageHandler;
103    
104    /** The current user provider */
105    protected CurrentUserProvider _userProvider;
106    
107    /** The OutgoingReferences extractor */
108    protected OutgoingReferencesExtractor _outgoingReferencesExtractor;
109    
110    /** The delete orgUnit component */
111    protected DeleteOrgUnitComponent _deleteOrgUnitComponent;
112
113    @Override
114    public void service(ServiceManager smanager) throws ServiceException
115    {
116        super.service(smanager);
117        _sqlUserDAO = (SQLUserSearchDAO) smanager.lookup(SQLUserSearchDAO.ROLE);
118        _orgChartPageHandler = (OrganisationChartPageHandler) smanager.lookup(OrganisationChartPageHandler.ROLE);
119        _userProvider = (CurrentUserProvider) smanager.lookup(CurrentUserProvider.ROLE);
120        _outgoingReferencesExtractor = (OutgoingReferencesExtractor) smanager.lookup(OutgoingReferencesExtractor.ROLE);
121        _deleteOrgUnitComponent = (DeleteOrgUnitComponent) smanager.lookup(DeleteOrgUnitComponent.ROLE);
122    }
123    
124    public boolean handleRightAssignmentContext()
125    {
126        return false;
127    }
128    
129    /**
130     * Get the name of user SQL table
131     * @return The user SQL table name
132     */
133    public String getUserTableName()
134    {
135        return (String) getParameterValues().get(__PARAM_SQL_TABLE_USER);
136    }
137    
138    /**
139     * Get the name of the orgunit join column name of the user table 
140     * @return The name of the orgunit join column name
141     */
142    public String getOrgunitJoinColumnNameForUser()
143    {
144        return (String) getParameterValues().get(__PARAM_SQL_ORGUNIT_JOIN_COLUMN_NAME);
145    }
146    
147    /**
148     * Get the login user attribute name
149     * @return The login user attribute name
150     */
151    public String getLoginUserAttributeName()
152    {
153        return (String) getParameterValues().get(__PARAM_LOGIN_USER_ATTRIBUTE_NAME);
154    }
155    
156    /**
157     * Get the login user column name
158     * @return The login user column name
159     */
160    public String getLoginUserColumnName()
161    {
162        return (String) getParameterValues().get(__PARAM_SQL_LOGIN_USER_COLUMN_NAME);
163    }
164    
165    /**
166     * Get the role user column name
167     * @return The role user column name
168     */
169    public String getRoleUserColumnName()
170    {
171        return (String) getParameterValues().get(__PARAM_SQL_ROLE_USER_COLUMN_NAME);
172    }
173    
174    /**
175     * Get the user sort column name
176     * @return The user sort column name
177     */
178    public String getUserSortColumnName()
179    {
180        return (String) getParameterValues().get(__PARAM_SQL_USER_SORT_COLUMN_NAME);
181    }
182    
183    /**
184     * Get the orgunit remote id column name
185     * @return The orgunit remote id column name
186     */
187    public String getOrgUnitRemoteIdColumnName()
188    {
189        return (String) getParameterValues().get(__PARAM_SQL_ORGUNIT_REMOTE_ID_COLUMN_NAME);
190    }
191    
192    /**
193     * True if the SQL sort for users prevail
194     * @return true if the SQL sort for users prevail
195     */
196    public boolean isUserSortPrevail()
197    {
198        return Boolean.valueOf((String) getParameterValues().get(__PARAM_SQL_USER_SORT_PREVAIL_NAME));
199    }
200    
201    @Override
202    protected Map<String, Object> _getSearchParameters(Map<String, Object> parameters, int offset, int limit, List<Object> sort, List<String> columns)
203    {
204        // Add the sql column name for the orgunit id. 
205        // It's for setting the ametys-internal:orgunit-remote-id and retrieve easily the orgUnit content with this Id 
206        String orgUnitIdColumnName = getOrgUnitRemoteIdColumnName();
207        if (!columns.contains(orgUnitIdColumnName))
208        {
209            columns.add(orgUnitIdColumnName);
210        }
211        
212        return super._getSearchParameters(parameters, offset, limit, sort, columns);
213    }
214    
215    @Override
216    protected Map<String, Map<String, Object>> internalSearch(Map<String, Object> searchParameters, int offset, int limit, List<Object> sort, Logger logger)
217    {
218        Map<String, Map<String, Object>> internalSearch = super.internalSearch(searchParameters, offset, limit, sort, logger);
219
220        // Fill _orgUnitRemoteIds and _orgUnitParents maps with search results
221        String orgUnitRemoteIdColumnName = getOrgUnitRemoteIdColumnName();
222        for (String orgUnitIdValue : internalSearch.keySet())
223        {
224            Map<String, Object> orgUnitValues = internalSearch.get(orgUnitIdValue);
225            _orgUnitRemoteIds.put(orgUnitIdValue, orgUnitValues.get(orgUnitRemoteIdColumnName).toString());
226            
227            Map<String, List<String>> mapping = getMapping();
228            if (mapping.containsKey(OrganisationChartPageHandler.PARENT_ORGUNIT_ATTRIBUTE_NAME))
229            {
230                // We take the first because it's no sense to defined two sql columns to define orgunit parents
231                String parentColumn = mapping.get(OrganisationChartPageHandler.PARENT_ORGUNIT_ATTRIBUTE_NAME).get(0); 
232                if (orgUnitValues.containsKey(parentColumn))
233                {
234                    String parentId = Optional.of(orgUnitValues)
235                            .map(v -> v.get(parentColumn))
236                            .map(Object::toString)
237                            .orElse(null); 
238                    _orgUnitParents.put(orgUnitIdValue, parentId);
239                }
240            }
241        }
242        
243        return internalSearch;
244    }
245    
246    @Override
247    protected void _logSynchronizationResult(Logger logger)
248    {
249        super._logSynchronizationResult(logger);
250        logger.info("{} contents have been modified to update the parent-child relations.", _nbSynchronizedOrgUnit);
251    }
252    
253    @Override
254    protected boolean _hasSomethingChanged()
255    {
256        return super._hasSomethingChanged() || _nbSynchronizedOrgUnit > 0;
257    }
258    
259    @Override
260    protected List<ModifiableContent> _internalPopulate(Logger logger)
261    {
262        _orgUnitParents = new HashMap<>();
263        _orgUnitRemoteIds = new HashMap<>();
264        _usersByOrgUnitId = _getUsersByOrgUnit(logger);
265        _nbSynchronizedOrgUnit = 0;
266        
267        List<ModifiableContent> contents = super._internalPopulate(logger);
268        
269        // All orgunits are imported, now we can set relation with parent orgunit
270        _setContentsRelationWithParentOrgunit(contents, logger);
271        
272        return contents;
273    }
274    
275    /**
276     * Retrieves the user attributes for all org units
277     * @param logger The logger
278     * @return the org units' users
279     */
280    protected Map<String, Map<String, String>> _getUsersByOrgUnit(Logger logger)
281    {
282        Map<String, Map<String, String>> orgUnitsUsers = new HashMap<>();
283
284        Map<String, List<String>> mapping = getMapping();
285        String idField = getIdField();
286        List<String> orgUnitKeys = mapping.get(idField);
287        if (orgUnitKeys != null && orgUnitKeys.size() > 0)
288        {
289            String orgUnitKey = orgUnitKeys.get(0);
290            Map<String, Object> userParameters = _getSearchUserParameters(orgUnitKey, logger);
291            List<Map<String, Object>> searchUserList = _sqlUserDAO.searchUser(userParameters, getDataSourceId());
292
293            String loginColumnName = getLoginUserColumnName();
294            String roleColumnName = getRoleUserColumnName();
295
296            List<String> userColumns = new ArrayList<>();
297            userColumns.add(loginColumnName);
298            userColumns.add(orgUnitKey);
299            if (StringUtils.isNotBlank(roleColumnName))
300            {
301                userColumns.add(roleColumnName);
302            }
303
304            for (Map<String, Object> userMap : searchUserList)
305            {
306                Map<String, Object> normalizedUserMap = _getNormalizedSearchResult(userColumns, userMap);
307                Optional<Triple<String, String, String>> orgUnitUser = _getUserByOrgUnit(orgUnitKey, loginColumnName, roleColumnName, normalizedUserMap, logger);
308                if (orgUnitUser.isPresent())
309                {
310                    Triple<String, String, String> triple = orgUnitUser.get();
311                    String orgUnitId = triple.getLeft();
312                    Map<String, String> orgUnitUsers = orgUnitsUsers.computeIfAbsent(orgUnitId, __ -> new LinkedHashMap<>());
313                    orgUnitUsers.put(triple.getMiddle(), triple.getRight());
314                }
315            }
316        }
317
318        return orgUnitsUsers;
319    }
320    
321    /**
322     * Retrieves a {@link Triple} containing the orgunit id, and the user's login and role for the given normalized user
323     * @param orgUnitKey the orgUnit key
324     * @param loginColumnName the login column name
325     * @param roleColumnName the role column name
326     * @param normalizedUser the normalized user
327     * @param logger the logger
328     * @return the user info as a {@link Triple}
329     */
330    protected Optional<Triple<String, String, String>> _getUserByOrgUnit(String orgUnitKey, String loginColumnName, String roleColumnName, Map<String, Object> normalizedUser, Logger logger)
331    {
332        String loginValue = (normalizedUser.get(loginColumnName) == null) ? null : String.valueOf(normalizedUser.get(loginColumnName)); 
333        String orgUnitIdValue = normalizedUser.get(orgUnitKey).toString();
334        
335        if (StringUtils.isNotBlank(loginValue))
336        {
337            String roleValue = null;
338            if (StringUtils.isNotBlank(roleColumnName))
339            {
340                roleValue = (normalizedUser.get(roleColumnName) == null) ? null :  String.valueOf(normalizedUser.get(roleColumnName));
341            }
342            
343            return Optional.of(Triple.of(orgUnitIdValue, loginValue, roleValue));
344        }
345        else
346        {
347            logger.warn("Can't add user to orgunit '" + orgUnitIdValue + "' because the login value is blank ...");
348            return Optional.empty();
349        }
350    }
351    
352    @Override
353    protected Optional<ModifiableContent> _importOrSynchronizeContent(String idValue, String lang, Map<String, List<Object>> remoteValues, boolean forceImport, Logger logger)
354    {
355        // Do not set relation with parent orgunit now, while they might miss some that are not yet created
356        remoteValues.remove(OrganisationChartPageHandler.PARENT_ORGUNIT_ATTRIBUTE_NAME);
357        
358        return super._importOrSynchronizeContent(idValue, lang, remoteValues, forceImport, logger);
359    }
360    
361    @Override
362    public ContentSynchronizationResult additionalCommonOperations(ModifiableContent content, Map<String, Object> additionalParameters, Logger logger)
363    {
364        ContentSynchronizationResult result = super.additionalCommonOperations(content, additionalParameters, logger);
365        
366        try
367        {
368            SynchronizationResult additionalResult = new SynchronizationResult();
369            String orgUnitIdValue = content.getValue(getIdField());
370            ModifiableModelLessDataHolder internalDataHolder = content.getInternalDataHolder();
371            
372            String oldValue = internalDataHolder.getValueOfType(ORGUNIT_REMOTE_ID_INTERNAL_DATA, ModelItemTypeConstants.STRING_TYPE_ID);
373            String value = _orgUnitRemoteIds.get(orgUnitIdValue);
374            
375            if (!value.equals(oldValue))
376            {
377                internalDataHolder.setValue(ORGUNIT_REMOTE_ID_INTERNAL_DATA, value);
378                additionalResult.setHasChanged(true);
379            }
380            
381            result.aggregateResult(additionalResult);
382        }
383        catch (Exception e)
384        {
385            _nbError++;
386            logger.error("An error occurred while importing or synchronizing orgunit '{}' and setting its remote id.", content, e);
387        }
388        
389        return result;
390    }
391    
392    @Override
393    protected Map<String, Object> getAdditionalAttributeValues(String idValue, Content content, Map<String, Object> additionalParameters, boolean create, Logger logger)
394    {
395        Map<String, Object> additionalRemoteValues = super.getAdditionalAttributeValues(idValue, content, additionalParameters, create, logger);
396        
397        List<Map<String, Object>> orgUnitUsers = _getOrgUnitUsers(idValue, content.getLanguage());
398        if (!orgUnitUsers.isEmpty())
399        {
400            additionalRemoteValues.put(OrganisationChartPageHandler.ORGUNIT_USERS_ATTRIBUTE_NAME, orgUnitUsers);
401        }
402        
403        return additionalRemoteValues;
404    }
405    
406    /**
407     * Retrieves the users of the given orgunit
408     * @param orgUnitId the orgunit identifier
409     * @param lang the language of the orgunit
410     * @return the users linked to the given orgunit
411     */
412    protected List<Map<String, Object>> _getOrgUnitUsers(String orgUnitId, String lang)
413    {
414        if (_usersByOrgUnitId.containsKey(orgUnitId))
415        {
416            List<Map<String, Object>> users = new ArrayList<>();
417            for (Map.Entry<String, String> user : _usersByOrgUnitId.get(orgUnitId).entrySet())
418            {
419                Map<String, Object> orgUnitUser = new HashMap<>();
420                
421                String loginValue = user.getKey();
422                Content userContent = _getUserContent(loginValue, lang);
423                
424                if (userContent != null)
425                {
426                    String roleValue = user.getValue();
427                    orgUnitUser.put(OrganisationChartPageHandler.ORGUNIT_USER_ROLE_ATTRIBUTE_NAME, roleValue);
428                    orgUnitUser.put(OrganisationChartPageHandler.ORGUNIT_USER_ATTRIBUTE_NAME, userContent);
429                    
430                    users.add(orgUnitUser);
431                }
432            }
433            
434            return users;
435        }
436        else
437        {
438            return List.of();
439        }
440    }
441    
442    /**
443     * Set all orgunit parents relation for each synchronized content
444     * @param orgUnitContents the synchronized content
445     * @param logger the logger
446     */
447    protected void _setContentsRelationWithParentOrgunit(List<ModifiableContent> orgUnitContents, Logger logger)
448    {
449        for (ModifiableContent orgUnitContent : orgUnitContents)
450        {
451            try
452            {
453                if (orgUnitContent instanceof WorkflowAwareContent)
454                {
455                    I18nizableText commentText = new I18nizableText("plugin.user-directory", "PLUGINS_USER_DIRECTORY_WORKFLOW_ACTION_EDIT_ORGUNIT_REFERENCE_MSG");
456                    String comment = _i18nUtils.translate(commentText, orgUnitContent.getLanguage());
457
458                    View view = View.of(orgUnitContent.getModel(), OrganisationChartPageHandler.PARENT_ORGUNIT_ATTRIBUTE_NAME);
459
460                    Map<String, Object> values = new HashMap<>();
461                    String orgUnitIdValue = orgUnitContent.getValue(getIdField());
462                    String newParentSynchroId = _orgUnitParents.get(orgUnitIdValue);
463                    Content newParentContent = newParentSynchroId != null ? _getOrgUnitContentFromRemoteId(newParentSynchroId, orgUnitContent.getLanguage(), logger) : null;
464                    values.put(OrganisationChartPageHandler.PARENT_ORGUNIT_ATTRIBUTE_NAME, newParentContent);
465
466                    Map<String, Object> inputs = new HashMap<>();
467                    if (StringUtils.isNotEmpty(comment))
468                    {
469                        inputs.put("comment", comment);
470                    }
471
472                    Map<String, Object> parameters = new HashMap<>();
473
474                    parameters.put(EditContentFunction.VIEW, view);
475                    parameters.put(EditContentFunction.VALUES_KEY, values);
476                    parameters.put(EditContentFunction.QUIT, true);
477                    inputs.put(AbstractWorkflowComponent.CONTEXT_PARAMETERS_KEY, parameters);
478
479                    inputs.put(EditSynchronizedContentFunction.SYNCHRO_INVERT_EDIT_ACTION_ID_KEY, getSynchronizeActionId());
480
481                    Map<String, Object> actionResult = _contentWorkflowHelper.doAction((WorkflowAwareContent) orgUnitContent, getSynchronizeActionId(), inputs);
482                    if ((boolean) actionResult.getOrDefault(AbstractContentWorkflowComponent.HAS_CHANGED_KEY, false))
483                    {
484                        _nbSynchronizedOrgUnit++;
485                    }
486                }
487            }
488            catch (WorkflowException | InvalidActionException e)
489            {
490                logger.error("An error occurred while updating parent-child relations of org unit {} ", orgUnitContent, e);
491            }
492        }
493    }
494    
495    /**
496     * Get user content from login value
497     * @param loginValue the login value
498     * @param lang the language
499     * @return the user content
500     */
501    protected Content _getUserContent(String loginValue, String lang)
502    {
503        String loginAttribute = getLoginUserAttributeName();
504        Set<String> contentTypes = _contentTypeEP.getSubTypes(UserDirectoryPageHandler.ABSTRACT_USER_CONTENT_TYPE);
505        
506        Expression ctypeExpression = new ContentTypeExpression(Operator.EQ, contentTypes.toArray(new String[contentTypes.size()]));
507        LanguageExpression languageExpression = new LanguageExpression(Operator.EQ, lang);
508        StringExpression loginExp = new StringExpression(loginAttribute, Operator.EQ, loginValue);
509        
510        Expression userExp = new AndExpression(loginExp, ctypeExpression, languageExpression);
511        String xPathQuery = ContentQueryHelper.getContentXPathQuery(userExp);
512        
513        AmetysObjectIterable<Content> contentQuery = _resolver.query(xPathQuery);
514        AmetysObjectIterator<Content> contentIterator = contentQuery.iterator();
515        if (contentIterator.hasNext())
516        {
517            return contentIterator.next();
518        }
519        
520        return null;
521    }
522    
523    /**
524     * Get orgunit content from the remote Id
525     * @param remoteId the remote Id
526     * @param lang the language
527     * @param logger the logger
528     * @return the orgunit content
529     */
530    protected Content _getOrgUnitContentFromRemoteId(String remoteId, String lang, Logger logger)
531    {
532        Expression ctypeExpression = new ContentTypeExpression(Operator.EQ, OrganisationChartPageHandler.ORGUNIT_CONTENT_TYPE);
533        StringExpression remoteIdOrgunitExpression = new StringExpression(ORGUNIT_REMOTE_ID_INTERNAL_DATA, Operator.EQ, remoteId, ExpressionContext.newInstance().withInternal(true));
534        LanguageExpression languageExpression = new LanguageExpression(Operator.EQ, lang);
535        
536        Expression collectionExpression = _sccHelper.getCollectionExpression(getId());
537        
538        Expression userExp = new AndExpression(remoteIdOrgunitExpression, ctypeExpression, languageExpression, collectionExpression);
539        String xPathQuery = ContentQueryHelper.getContentXPathQuery(userExp);
540        
541        AmetysObjectIterable<Content> contentQuery = _resolver.query(xPathQuery);
542        AmetysObjectIterator<Content> contentIterator = contentQuery.iterator();
543        if (contentIterator.hasNext())
544        {
545            return contentIterator.next();
546        }
547        
548        return null;
549    }
550    
551    /**
552     * Get the parameters map for user mybatis search
553     * @param orgUnitColumnKey the column name of the orgunit key
554     * @param logger the logger
555     * @return the parameter map
556     */
557    protected Map<String, Object> _getSearchUserParameters(String orgUnitColumnKey, Logger logger)
558    {
559        Map<String, Object> params = new HashMap<>();
560        params.put("loginColumnName", getLoginUserColumnName());
561        params.put("tableUser", getUserTableName());
562        params.put("tableOrgUnit", getTableName());
563        params.put("joinColumnName", getOrgunitJoinColumnNameForUser());
564        params.put("orgUnitColumnKey", orgUnitColumnKey);
565        params.put("orgUnitIdColumnName", getOrgUnitRemoteIdColumnName());
566        
567        String roleUserColumnName = getRoleUserColumnName();
568        if (StringUtils.isNotBlank(roleUserColumnName))
569        {
570            params.put("roleColumnName", roleUserColumnName);
571        }
572        
573        String sortColumnName = getUserSortColumnName();
574        if (StringUtils.isNotBlank(sortColumnName))
575        {
576            params.put("sortColumnName", sortColumnName);
577        }
578
579        return params;
580    }
581    
582    @Override
583    protected int _deleteContents(List<Content> contentsToRemove, Logger logger)
584    {
585        return _deleteOrgUnitComponent.deleteContentsWithLog(contentsToRemove, Map.of(DeleteOrgUnitComponent.SCC_ID_PARAMETERS_KEY, getId()), Map.of(), logger);
586    }
587    
588    @Override
589    public Map<String, Integer> getSynchronizationResult()
590    {
591        Map<String, Integer> result = super.getSynchronizationResult();
592
593        result.put(RESULT_NB_SYNCHRONIZED_ORGUNITS_RELATIONS, _nbSynchronizedOrgUnit);
594
595        return result;
596    }
597}