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