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