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