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