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