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.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.metadata.CompositeMetadata; 047import org.ametys.plugins.userdirectory.UserDirectoryPageHandler; 048import org.ametys.web.repository.page.Page; 049import org.ametys.web.repository.page.UnknownZoneException; 050import org.ametys.web.repository.page.Zone; 051import org.ametys.web.repository.site.Site; 052import org.ametys.web.repository.sitemap.Sitemap; 053import org.ametys.web.skin.Skin; 054import org.ametys.web.skin.SkinsManager; 055 056/** 057 * Page representing a second-level page. 058 */ 059public class UserPage implements Page, ACLAmetysObject 060{ 061 private static final String __USER_PAGE_TEMPLATE = "user-page"; 062 063 private Page _root; 064 private int _initialDepth; 065 private String _title; 066 private AmetysObjectResolver _resolver; 067 private UserDirectoryPageHandler _userDirectoryPageHandler; 068 private SynchronizableContentsCollectionDAO _syncContentsCollectionDAO; 069 private SkinsManager _skinsManager; 070 private String _path; 071 private Content _syncContent; 072 073 074 075 /** 076 * Constructor. 077 * @param root the root page. 078 * @param syncContent the synchronized content 079 * @param path the path 080 * @param resolver the {@link AmetysObjectResolver}. 081 * @param userDirectoryPageHandler the user directory page handler 082 * @param syncContentsCollectionDAO The DAO for synchronizable collections 083 * @param skinsManager the skins manager 084 */ 085 public UserPage(Page root, Content syncContent, String path, AmetysObjectResolver resolver, UserDirectoryPageHandler userDirectoryPageHandler, SynchronizableContentsCollectionDAO syncContentsCollectionDAO, SkinsManager skinsManager) 086 { 087 _root = root; 088 _path = path; 089 _resolver = resolver; 090 _userDirectoryPageHandler = userDirectoryPageHandler; 091 _syncContentsCollectionDAO = syncContentsCollectionDAO; 092 _skinsManager = skinsManager; 093 _syncContent = syncContent; 094 095 _title = _syncContent.getTitle(); 096 _initialDepth = _userDirectoryPageHandler.getDepth(_root); 097 } 098 099 /** 100 * Returns the associated synchronizable {@link Content}. 101 * @return the associated synchronizable {@link Content}. 102 */ 103 public Content getSyncContent() 104 { 105 return _syncContent; 106 } 107 108 @Override 109 public int getDepth() throws AmetysRepositoryException 110 { 111 return _root.getDepth() + _initialDepth + 1; 112 } 113 114 @Override 115 public Set<String> getReferers() throws AmetysRepositoryException 116 { 117 return null; 118 } 119 120 @Override 121 public ResourceCollection getRootAttachments() throws AmetysRepositoryException 122 { 123 return null; 124 } 125 126 @Override 127 public String getTemplate() throws AmetysRepositoryException 128 { 129 Skin skin = _skinsManager.getSkin(getSite().getSkinId()); 130 131 if (skin.getTemplate(__USER_PAGE_TEMPLATE) != null) 132 { 133 return __USER_PAGE_TEMPLATE; 134 } 135 136 return "page"; 137 } 138 139 @Override 140 public String getTitle() throws AmetysRepositoryException 141 { 142 return _title; 143 } 144 145 @Override 146 public String getLongTitle() throws AmetysRepositoryException 147 { 148 return _title; 149 } 150 151 @Override 152 public PageType getType() throws AmetysRepositoryException 153 { 154 return PageType.CONTAINER; 155 } 156 157 @Override 158 public String getURL() throws AmetysRepositoryException 159 { 160 throw new UnsupportedOperationException("#getURL is not supported on virtual user pages"); 161 } 162 163 @Override 164 public LinkType getURLType() throws AmetysRepositoryException 165 { 166 throw new UnsupportedOperationException("#getURLType is not supported on virtual user pages"); 167 } 168 169 @Override 170 public Zone getZone(String name) throws UnknownZoneException, AmetysRepositoryException 171 { 172 if (!"default".equals(name)) 173 { 174 throw new IllegalArgumentException("Only the zone named 'default' is actually supported on virtual user pages."); 175 } 176 177 return new UserZone(this); 178 } 179 180 @Override 181 public AmetysObjectIterable< ? extends Zone> getZones() throws AmetysRepositoryException 182 { 183 ArrayList<Zone> zones = new ArrayList<>(); 184 zones.add(new UserZone(this)); 185 return new CollectionIterable<>(zones); 186 } 187 188 @Override 189 public boolean hasZone(String name) throws AmetysRepositoryException 190 { 191 return "default".equals(name); 192 } 193 194 @Override 195 public AmetysObjectIterable<? extends Page> getChildrenPages() throws AmetysRepositoryException 196 { 197 ArrayList<Page> children = new ArrayList<>(); 198 return new CollectionIterable<>(children); 199 } 200 201 @Override 202 public String getPathInSitemap() throws AmetysRepositoryException 203 { 204 if (_path.equals("_root")) 205 { 206 return _root.getPathInSitemap() + "/" + getName(); 207 } 208 else 209 { 210 String path = StringUtils.lowerCase(_path); 211 return _root.getPathInSitemap() + "/" + path + "/" + getName(); 212 } 213 } 214 215 @Override 216 public Site getSite() throws AmetysRepositoryException 217 { 218 return _root.getSite(); 219 } 220 221 @Override 222 public String getSiteName() throws AmetysRepositoryException 223 { 224 return _root.getSiteName(); 225 } 226 227 @Override 228 public Sitemap getSitemap() throws AmetysRepositoryException 229 { 230 return _root.getSitemap(); 231 } 232 233 @Override 234 public String getSitemapName() throws AmetysRepositoryException 235 { 236 return _root.getSitemapName(); 237 } 238 239 @Override 240 public <A extends AmetysObject> A getChild(String path) throws AmetysRepositoryException, UnknownAmetysObjectException 241 { 242 if (path.isEmpty()) 243 { 244 throw new AmetysRepositoryException("path must be non empty"); 245 } 246 247 return null; 248 } 249 250 @SuppressWarnings("unchecked") 251 @Override 252 public AmetysObjectIterable<? extends AmetysObject> getChildren() throws AmetysRepositoryException 253 { 254 return getChildrenPages(); 255 } 256 257 @Override 258 public boolean hasChild(String name) throws AmetysRepositoryException 259 { 260 return false; 261 } 262 263 @Override 264 public String getId() throws AmetysRepositoryException 265 { 266 // E.g: uduser://path?rootId=...&contentId=... 267 return "uduser://" + _path + "?rootId=" + _root.getId() + "&contentId=" + _syncContent.getId(); 268 } 269 270 @Override 271 public String getName() throws AmetysRepositoryException 272 { 273 return _syncContent.getName(); 274 } 275 276 @SuppressWarnings("unchecked") 277 @Override 278 public Page getParent() throws AmetysRepositoryException 279 { 280 if (_initialDepth > 0) 281 { 282 String pathName = StringUtils.substringAfterLast(_path, "/"); 283 String name = _userDirectoryPageHandler.getName(pathName); 284 return new TransitionalPage(_root, name, _path, _resolver, _userDirectoryPageHandler, _syncContentsCollectionDAO, _skinsManager); 285 } 286 else 287 { 288 return _root; 289 } 290 } 291 292 @Override 293 public String getParentPath() throws AmetysRepositoryException 294 { 295 if (_initialDepth > 0) 296 { 297 String path = StringUtils.lowerCase(_path); 298 return _root.getPath() + "/" + path; 299 } 300 else 301 { 302 return _root.getPath(); 303 } 304 } 305 306 @Override 307 public String getPath() throws AmetysRepositoryException 308 { 309 return getParentPath() + "/" + getName(); 310 } 311 312 @Override 313 public CompositeMetadata getMetadataHolder() 314 { 315 return new StaticCompositeMetadata(new HashMap<>()); 316 } 317 318 @Override 319 public Set<String> getTags() throws AmetysRepositoryException 320 { 321 return Collections.emptySet(); 322 } 323 324 @Override 325 public AmetysObjectIterable< ? extends Page> getChildrenPages(boolean includeInvisiblePage) throws AmetysRepositoryException 326 { 327 ArrayList<Page> children = new ArrayList<>(); 328 return new CollectionIterable<>(children); 329 } 330 331 @Override 332 public boolean isVisible() throws AmetysRepositoryException 333 { 334 return false; 335 } 336 337 @Override 338 public Page getChildPageAt(int index) throws UnknownAmetysObjectException, AmetysRepositoryException 339 { 340 throw new UnknownAmetysObjectException("There is no child for user 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}