001/* 002 * Copyright 2017 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.page; 017 018import java.util.ArrayList; 019import java.util.Collections; 020import java.util.List; 021import java.util.Locale; 022import java.util.Map; 023import java.util.Set; 024import java.util.stream.Collectors; 025 026import javax.jcr.Node; 027import javax.jcr.RepositoryException; 028import javax.jcr.Value; 029 030import org.apache.commons.lang.StringUtils; 031 032import org.ametys.cms.repository.Content; 033import org.ametys.cms.repository.DefaultContent; 034import org.ametys.core.group.GroupIdentity; 035import org.ametys.core.right.RightManager; 036import org.ametys.core.user.UserIdentity; 037import org.ametys.plugins.contentio.synchronize.SynchronizableContentsCollection; 038import org.ametys.plugins.contentio.synchronize.SynchronizableContentsCollectionDAO; 039import org.ametys.plugins.explorer.resources.ResourceCollection; 040import org.ametys.plugins.repository.ACLAmetysObject; 041import org.ametys.plugins.repository.AmetysObject; 042import org.ametys.plugins.repository.AmetysObjectIterable; 043import org.ametys.plugins.repository.AmetysObjectResolver; 044import org.ametys.plugins.repository.AmetysRepositoryException; 045import org.ametys.plugins.repository.CollectionIterable; 046import org.ametys.plugins.repository.UnknownAmetysObjectException; 047import org.ametys.plugins.repository.data.holder.ModelAwareDataHolder; 048import org.ametys.plugins.repository.data.holder.ModelLessDataHolder; 049import org.ametys.plugins.repository.data.holder.impl.DefaultModelLessDataHolder; 050import org.ametys.plugins.repository.data.repositorydata.RepositoryData; 051import org.ametys.plugins.repository.data.repositorydata.impl.MemoryRepositoryData; 052import org.ametys.plugins.repository.data.type.RepositoryModelItemType; 053import org.ametys.plugins.repository.metadata.CompositeMetadata; 054import org.ametys.plugins.userdirectory.OrganisationChartPageHandler; 055import org.ametys.runtime.plugin.component.AbstractThreadSafeComponentExtensionPoint; 056import org.ametys.web.repository.page.Page; 057import org.ametys.web.repository.page.UnknownZoneException; 058import org.ametys.web.repository.page.Zone; 059import org.ametys.web.repository.site.Site; 060import org.ametys.web.repository.sitemap.Sitemap; 061import org.ametys.web.skin.Skin; 062import org.ametys.web.skin.SkinsManager; 063 064/** 065 * Page representing an orgUnit page. 066 */ 067public class OrgUnitPage implements Page, ACLAmetysObject 068{ 069 private static final String __ORGUNIT_PAGE_TEMPLATE = "udorgunit"; 070 071 private Page _root; 072 private String _title; 073 private AmetysObjectResolver _resolver; 074 private OrganisationChartPageHandler _organisationChartPageHandler; 075 private SynchronizableContentsCollectionDAO _syncContentsCollectionDAO; 076 private SkinsManager _skinsManager; 077 private String _path; 078 private Content _syncContent; 079 080 private AbstractThreadSafeComponentExtensionPoint<RepositoryModelItemType> _pageDataTypeExtensionPoint; 081 private AbstractThreadSafeComponentExtensionPoint<RepositoryModelItemType> _zoneDataTypeExtensionPoint; 082 private AbstractThreadSafeComponentExtensionPoint<RepositoryModelItemType> _zoneItemDataTypeExtensionPoint; 083 084 /** 085 * Constructor. 086 * @param root the root page. 087 * @param syncContent the synchronized content 088 * @param path the path 089 * @param resolver the {@link AmetysObjectResolver}. 090 * @param organisationChartPageHandler the organisation chart page handler 091 * @param syncContentsCollectionDAO The DAO for synchronizable collections 092 * @param skinsManager the skins manager 093 * @param pageDataTypeExtensionPoint the extension point with available data types for pages 094 * @param zoneDataTypeExtensionPoint the extension point with available data types for zones 095 * @param zoneItemDataTypeExtensionPoint the extension point with available data types for zone items 096 */ 097 public OrgUnitPage(Page root, Content syncContent, String path, AmetysObjectResolver resolver, OrganisationChartPageHandler organisationChartPageHandler, SynchronizableContentsCollectionDAO syncContentsCollectionDAO, SkinsManager skinsManager, AbstractThreadSafeComponentExtensionPoint<RepositoryModelItemType> pageDataTypeExtensionPoint, AbstractThreadSafeComponentExtensionPoint<RepositoryModelItemType> zoneDataTypeExtensionPoint, AbstractThreadSafeComponentExtensionPoint<RepositoryModelItemType> zoneItemDataTypeExtensionPoint) 098 { 099 _root = root; 100 _path = path; 101 _resolver = resolver; 102 _organisationChartPageHandler = organisationChartPageHandler; 103 _syncContentsCollectionDAO = syncContentsCollectionDAO; 104 _skinsManager = skinsManager; 105 _syncContent = syncContent; 106 107 _title = _syncContent.getTitle(new Locale(root.getSitemapName())); 108 109 _pageDataTypeExtensionPoint = pageDataTypeExtensionPoint; 110 _zoneDataTypeExtensionPoint = zoneDataTypeExtensionPoint; 111 _zoneItemDataTypeExtensionPoint = zoneItemDataTypeExtensionPoint; 112 } 113 114 /** 115 * Returns the associated synchronizable {@link Content}. 116 * @return the associated synchronizable {@link Content}. 117 */ 118 public Content getSyncContent() 119 { 120 return _syncContent; 121 } 122 123 @Override 124 public int getDepth() throws AmetysRepositoryException 125 { 126 return _root.getDepth() + (_path != null ? _path.split("/").length : 0); 127 } 128 129 @Override 130 public Set<String> getReferers() throws AmetysRepositoryException 131 { 132 return null; 133 } 134 135 @Override 136 public ResourceCollection getRootAttachments() throws AmetysRepositoryException 137 { 138 return null; 139 } 140 141 @Override 142 public String getTemplate() throws AmetysRepositoryException 143 { 144 Skin skin = _skinsManager.getSkin(getSite().getSkinId()); 145 146 if (skin.getTemplate(__ORGUNIT_PAGE_TEMPLATE) != null) 147 { 148 return __ORGUNIT_PAGE_TEMPLATE; 149 } 150 151 return "page"; 152 } 153 154 @Override 155 public String getTitle() throws AmetysRepositoryException 156 { 157 return _title; 158 } 159 160 @Override 161 public String getLongTitle() throws AmetysRepositoryException 162 { 163 return _title; 164 } 165 166 @Override 167 public PageType getType() throws AmetysRepositoryException 168 { 169 return PageType.CONTAINER; 170 } 171 172 @Override 173 public String getURL() throws AmetysRepositoryException 174 { 175 throw new UnsupportedOperationException("#getURL is not supported on virtual orgUnit pages"); 176 } 177 178 @Override 179 public LinkType getURLType() throws AmetysRepositoryException 180 { 181 throw new UnsupportedOperationException("#getURLType is not supported on virtual orgUnit pages"); 182 } 183 184 @Override 185 public Zone getZone(String name) throws UnknownZoneException, AmetysRepositoryException 186 { 187 if (!"default".equals(name)) 188 { 189 throw new IllegalArgumentException("Only the zone named 'default' is actually supported on virtual orgUnit pages."); 190 } 191 192 return new OrgUnitZone(this, _zoneDataTypeExtensionPoint, _zoneItemDataTypeExtensionPoint); 193 } 194 195 @Override 196 public AmetysObjectIterable< ? extends Zone> getZones() throws AmetysRepositoryException 197 { 198 ArrayList<Zone> zones = new ArrayList<>(); 199 zones.add(new OrgUnitZone(this, _zoneDataTypeExtensionPoint, _zoneItemDataTypeExtensionPoint)); 200 return new CollectionIterable<>(zones); 201 } 202 203 @Override 204 public boolean hasZone(String name) throws AmetysRepositoryException 205 { 206 return "default".equals(name); 207 } 208 209 @Override 210 public AmetysObjectIterable<? extends Page> getChildrenPages() throws AmetysRepositoryException 211 { 212 ArrayList<Page> children = new ArrayList<>(); 213 214 for (Content content : _organisationChartPageHandler.getChildContents(_syncContent)) 215 { 216 children.add(new OrgUnitPage(_root, content, _path + "/" + content.getName() , _resolver, _organisationChartPageHandler, _syncContentsCollectionDAO, _skinsManager, _pageDataTypeExtensionPoint, _zoneDataTypeExtensionPoint, _zoneItemDataTypeExtensionPoint)); 217 } 218 219 return new CollectionIterable<>(children); 220 } 221 222 @Override 223 public String getPathInSitemap() throws AmetysRepositoryException 224 { 225 return _root.getPathInSitemap() + "/" + _path; 226 } 227 228 @Override 229 public Site getSite() throws AmetysRepositoryException 230 { 231 return _root.getSite(); 232 } 233 234 @Override 235 public String getSiteName() throws AmetysRepositoryException 236 { 237 return _root.getSiteName(); 238 } 239 240 @Override 241 public Sitemap getSitemap() throws AmetysRepositoryException 242 { 243 return _root.getSitemap(); 244 } 245 246 @Override 247 public String getSitemapName() throws AmetysRepositoryException 248 { 249 return _root.getSitemapName(); 250 } 251 252 @SuppressWarnings("unchecked") 253 @Override 254 public <A extends AmetysObject> A getChild(String path) throws AmetysRepositoryException, UnknownAmetysObjectException 255 { 256 if (path.isEmpty()) 257 { 258 throw new AmetysRepositoryException("path must be non empty"); 259 } 260 261 Content childContent = _organisationChartPageHandler.getChildFromPath(_syncContent, path); 262 if (childContent != null) 263 { 264 OrgUnitPage page = new OrgUnitPage(_root, childContent, _path + "/" + path , _resolver, _organisationChartPageHandler, _syncContentsCollectionDAO, _skinsManager, _pageDataTypeExtensionPoint, _zoneDataTypeExtensionPoint, _zoneItemDataTypeExtensionPoint); 265 return (A) page; 266 } 267 268 return null; 269 } 270 271 272 @SuppressWarnings("unchecked") 273 @Override 274 public AmetysObjectIterable<? extends AmetysObject> getChildren() throws AmetysRepositoryException 275 { 276 return getChildrenPages(); 277 } 278 279 @Override 280 public boolean hasChild(String name) throws AmetysRepositoryException 281 { 282 return !_organisationChartPageHandler.getChildContents(_syncContent).stream().filter(c -> c.getName().equals(name)).collect(Collectors.toList()).isEmpty(); 283 } 284 285 @Override 286 public String getId() throws AmetysRepositoryException 287 { 288 // E.g: udorgunit://path?rootId=...&contentId=... 289 return "udorgunit://" + _path + "?rootId=" + _root.getId() + "&contentId=" + _syncContent.getId(); 290 } 291 292 @Override 293 public String getName() throws AmetysRepositoryException 294 { 295 return _syncContent.getName(); 296 } 297 298 @SuppressWarnings("unchecked") 299 @Override 300 public Page getParent() throws AmetysRepositoryException 301 { 302 Content parentContent = _organisationChartPageHandler.getParentContent(_syncContent); 303 if (parentContent != null) 304 { 305 return new OrgUnitPage(_root, parentContent, StringUtils.substringBeforeLast(_path, "/"), _resolver, _organisationChartPageHandler, _syncContentsCollectionDAO, _skinsManager, _pageDataTypeExtensionPoint, _zoneDataTypeExtensionPoint, _zoneItemDataTypeExtensionPoint); 306 } 307 308 return _root; 309 } 310 311 @Override 312 public String getParentPath() throws AmetysRepositoryException 313 { 314 if (_path.contains("/")) 315 { 316 return _root.getPath() + "/" + StringUtils.substringBeforeLast(_path, "/"); 317 } 318 else 319 { 320 return _root.getPath(); 321 } 322 } 323 324 @Override 325 public String getPath() throws AmetysRepositoryException 326 { 327 return _root.getPath() + "/" + _path; 328 } 329 330 public ModelLessDataHolder getDataHolder() 331 { 332 RepositoryData repositoryData = new MemoryRepositoryData(getName()); 333 return new DefaultModelLessDataHolder(_pageDataTypeExtensionPoint, repositoryData); 334 } 335 336 @Override 337 public Set<String> getTags() throws AmetysRepositoryException 338 { 339 return Collections.emptySet(); 340 } 341 342 @Override 343 public AmetysObjectIterable< ? extends Page> getChildrenPages(boolean includeInvisiblePage) throws AmetysRepositoryException 344 { 345 return getChildrenPages(); 346 } 347 348 @Override 349 public boolean isVisible() throws AmetysRepositoryException 350 { 351 return true; 352 } 353 354 @Override 355 public Page getChildPageAt(int index) throws UnknownAmetysObjectException, AmetysRepositoryException 356 { 357 throw new UnknownAmetysObjectException("There is no child for orgUnit page"); 358 } 359 360 @Override 361 public Set<String> getAllowedProfilesForAnyConnectedUser() 362 { 363 return Collections.EMPTY_SET; 364 } 365 366 @Override 367 public Set<String> getDeniedProfilesForAnyConnectedUser() 368 { 369 return Collections.EMPTY_SET; 370 } 371 372 @Override 373 public Set<String> getAllowedProfilesForAnonymous() 374 { 375 return Collections.EMPTY_SET; 376 } 377 378 @Override 379 public Set<String> getDeniedProfilesForAnonymous() 380 { 381 List<String> collectionIds = _getCollectionIds(); 382 for (String collectionId : collectionIds) 383 { 384 SynchronizableContentsCollection collection = _syncContentsCollectionDAO.getSynchronizableContentsCollection(collectionId); 385 String restrictedField = collection.getRestrictedField(); 386 if (!StringUtils.isEmpty(restrictedField) && _isContentRestricted(restrictedField)) 387 { 388 return Collections.singleton(RightManager.READER_PROFILE_ID); 389 } 390 } 391 return Collections.EMPTY_SET; 392 } 393 394 @Override 395 public Set<String> getAllowedProfilesForUser(UserIdentity user) 396 { 397 return Collections.EMPTY_SET; 398 } 399 400 @Override 401 public Map<UserIdentity, Set<String>> getAllowedProfilesForUsers() 402 { 403 return Collections.EMPTY_MAP; 404 } 405 406 @Override 407 public Set<UserIdentity> getAllowedUsers(String profileId) 408 { 409 return Collections.EMPTY_SET; 410 } 411 412 @Override 413 public Map<GroupIdentity, Set<String>> getAllowedProfilesForGroups() 414 { 415 return Collections.EMPTY_MAP; 416 } 417 418 @Override 419 public Set<GroupIdentity> getAllowedGroups(String profileId) 420 { 421 return Collections.EMPTY_SET; 422 } 423 424 @Override 425 public Set<String> getDeniedProfilesForUser(UserIdentity user) 426 { 427 return Collections.EMPTY_SET; 428 } 429 430 @Override 431 public Map<UserIdentity, Set<String>> getDeniedProfilesForUsers() 432 { 433 return Collections.EMPTY_MAP; 434 } 435 436 @Override 437 public Set<UserIdentity> getDeniedUsers(String profileId) 438 { 439 return Collections.EMPTY_SET; 440 } 441 442 @Override 443 public Map<GroupIdentity, Set<String>> getDeniedProfilesForGroups() 444 { 445 return Collections.EMPTY_MAP; 446 } 447 448 @Override 449 public Set<GroupIdentity> getDeniedGroups(String profileId) 450 { 451 return Collections.EMPTY_SET; 452 } 453 454 @Override 455 public boolean isInheritanceDisallowed() 456 { 457 return false; 458 } 459 460 private boolean _isContentRestricted (String metadataPath) 461 { 462 CompositeMetadata metadataHolder = _syncContent.getMetadataHolder(); 463 464 String[] pathSegments = metadataPath.split("/"); 465 466 for (int i = 0; i < pathSegments.length - 1; i++) 467 { 468 if (!metadataHolder.hasMetadata(pathSegments[i])) 469 { 470 return false; 471 } 472 metadataHolder = metadataHolder.getCompositeMetadata(pathSegments[i]); 473 } 474 475 String metadataName = pathSegments[pathSegments.length - 1]; 476 return metadataHolder.getBoolean(metadataName, false); 477 } 478 479 private List<String> _getCollectionIds() throws AmetysRepositoryException 480 { 481 List<String> collectionIds = new ArrayList<>(); 482 483 if (_syncContent instanceof DefaultContent) 484 { 485 try 486 { 487 Node node = ((DefaultContent) _syncContent).getNode(); 488 if (node.hasProperty(SynchronizableContentsCollection.COLLECTION_ID_PROPERTY)) 489 { 490 Value[] values = node.getProperty(SynchronizableContentsCollection.COLLECTION_ID_PROPERTY).getValues(); 491 for (Value value : values) 492 { 493 collectionIds.add(value.getString()); 494 } 495 } 496 } 497 catch (RepositoryException e) 498 { 499 throw new AmetysRepositoryException("Failed to get linked synchronizable collections for content " + _syncContent.getId(), e); 500 } 501 } 502 503 return collectionIds; 504 } 505 506 public ModelAwareDataHolder getTemplateParametersHolder() throws AmetysRepositoryException 507 { 508 return null; 509 } 510}