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.Arrays; 020import java.util.Collections; 021import java.util.List; 022import java.util.Map; 023import java.util.Map.Entry; 024import java.util.Set; 025import java.util.SortedSet; 026import java.util.stream.Collectors; 027 028import org.apache.commons.lang.StringUtils; 029 030import org.ametys.cms.repository.Content; 031import org.ametys.plugins.contentio.synchronize.SynchronizableContentsCollectionDAO; 032import org.ametys.plugins.explorer.resources.ResourceCollection; 033import org.ametys.plugins.repository.AmetysObject; 034import org.ametys.plugins.repository.AmetysObjectIterable; 035import org.ametys.plugins.repository.AmetysObjectResolver; 036import org.ametys.plugins.repository.AmetysRepositoryException; 037import org.ametys.plugins.repository.CollectionIterable; 038import org.ametys.plugins.repository.UnknownAmetysObjectException; 039import org.ametys.plugins.repository.data.holder.ModelLessDataHolder; 040import org.ametys.plugins.repository.data.holder.impl.DefaultModelLessDataHolder; 041import org.ametys.plugins.repository.data.repositorydata.RepositoryData; 042import org.ametys.plugins.repository.data.repositorydata.impl.MemoryRepositoryData; 043import org.ametys.plugins.repository.data.type.RepositoryModelItemType; 044import org.ametys.plugins.userdirectory.UserDirectoryPageHandler; 045import org.ametys.runtime.plugin.component.AbstractThreadSafeComponentExtensionPoint; 046import org.ametys.web.repository.page.Page; 047import org.ametys.web.repository.page.UnknownZoneException; 048import org.ametys.web.repository.page.Zone; 049import org.ametys.web.repository.site.Site; 050import org.ametys.web.repository.sitemap.Sitemap; 051import org.ametys.web.service.ServiceExtensionPoint; 052import org.ametys.web.skin.SkinsManager; 053 054 055/** 056 * Page representing a second-level page. 057 */ 058public class TransitionalPage implements Page 059{ 060 private Page _root; 061 private String _path; 062 private int _initialDepth; 063 private String _prefix; 064 private AmetysObjectResolver _resolver; 065 private UserDirectoryPageHandler _userDirectoryPageHandler; 066 private SynchronizableContentsCollectionDAO _syncContentsCollectionDAO; 067 private SkinsManager _skinsManager; 068 069 private AbstractThreadSafeComponentExtensionPoint<RepositoryModelItemType> _pageDataTypeExtensionPoint; 070 private AbstractThreadSafeComponentExtensionPoint<RepositoryModelItemType> _zoneDataTypeExtensionPoint; 071 private ServiceExtensionPoint _serviceExtensionPoint; 072 private AbstractThreadSafeComponentExtensionPoint<RepositoryModelItemType> _zoneItemDataTypeExtensionPoint; 073 074 /** 075 * Constructor. 076 * @param root the user directory root page. 077 * @param prefix the page's title. 078 * @param resolver the {@link AmetysObjectResolver}. 079 * @param path the path 080 * @param userDirectoryPageHandler the user directory page handler component 081 * @param syncContentsCollectionDAO The DAO for synchronizable collections 082 * @param skinsManager the skins manager 083 * @param pageDataTypeExtensionPoint the extension point with available data types for pages 084 * @param zoneDataTypeExtensionPoint the extension point with available data types for zones 085 * @param serviceExtensionPoint the service extension point 086 * @param zoneItemDataTypeExtensionPoint the extension point with available data types for zone items 087 */ 088 public TransitionalPage(Page root, String prefix, String path, AmetysObjectResolver resolver, UserDirectoryPageHandler userDirectoryPageHandler, SynchronizableContentsCollectionDAO syncContentsCollectionDAO, SkinsManager skinsManager, AbstractThreadSafeComponentExtensionPoint<RepositoryModelItemType> pageDataTypeExtensionPoint, AbstractThreadSafeComponentExtensionPoint<RepositoryModelItemType> zoneDataTypeExtensionPoint, ServiceExtensionPoint serviceExtensionPoint, AbstractThreadSafeComponentExtensionPoint<RepositoryModelItemType> zoneItemDataTypeExtensionPoint) 089 { 090 _root = root; 091 _prefix = prefix; 092 _path = path; 093 _resolver = resolver; 094 _userDirectoryPageHandler = userDirectoryPageHandler; 095 _syncContentsCollectionDAO = syncContentsCollectionDAO; 096 _skinsManager = skinsManager; 097 098 _initialDepth = _userDirectoryPageHandler.getDepth(_root); 099 100 _pageDataTypeExtensionPoint = pageDataTypeExtensionPoint; 101 _zoneDataTypeExtensionPoint = zoneDataTypeExtensionPoint; 102 _serviceExtensionPoint = serviceExtensionPoint; 103 _zoneItemDataTypeExtensionPoint = zoneItemDataTypeExtensionPoint; 104 } 105 106 @Override 107 public int getDepth() throws AmetysRepositoryException 108 { 109 return _root.getDepth() + _path.split("/").length; 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 return "page"; 128 } 129 130 @Override 131 public String getTitle() throws AmetysRepositoryException 132 { 133 return StringUtils.upperCase(_prefix); 134 } 135 136 @Override 137 public String getLongTitle() throws AmetysRepositoryException 138 { 139 return StringUtils.upperCase(_prefix); 140 } 141 142 @Override 143 public PageType getType() throws AmetysRepositoryException 144 { 145 return PageType.CONTAINER; 146 } 147 148 @Override 149 public String getURL() throws AmetysRepositoryException 150 { 151 throw new UnsupportedOperationException("getURL not supported on virtual user directory pages"); 152 } 153 154 @Override 155 public LinkType getURLType() throws AmetysRepositoryException 156 { 157 throw new UnsupportedOperationException("getURLType not supported on virtual user directory pages"); 158 } 159 160 @Override 161 public Zone getZone(String name) throws UnknownZoneException, AmetysRepositoryException 162 { 163 if (!"default".equals(name)) 164 { 165 throw new IllegalArgumentException("Only the zone named 'default' is actually supported on virtual transitional pages."); 166 } 167 168 return new TransitionalZone(this, _zoneDataTypeExtensionPoint, _serviceExtensionPoint, _zoneItemDataTypeExtensionPoint); 169 } 170 171 @Override 172 public AmetysObjectIterable< ? extends Zone> getZones() throws AmetysRepositoryException 173 { 174 List<Zone> zones = new ArrayList<>(); 175 zones.add(new TransitionalZone(this, _zoneDataTypeExtensionPoint, _serviceExtensionPoint, _zoneItemDataTypeExtensionPoint)); 176 return new CollectionIterable<>(zones); 177 } 178 179 @Override 180 public boolean hasZone(String name) throws AmetysRepositoryException 181 { 182 return "default".equals(name); 183 } 184 185 @Override 186 public AmetysObjectIterable<? extends Page> getChildrenPages() throws AmetysRepositoryException 187 { 188 ArrayList<Page> children = new ArrayList<>(); 189 190 int depth = _initialDepth - _path.split("/").length; 191 if (depth > 0) 192 { 193 SortedSet<String> transitionalPagesName = _userDirectoryPageHandler.getTransitionalPagesName(_root, _userDirectoryPageHandler.getName(_path)); 194 for (String name : transitionalPagesName) 195 { 196 String pathName = _userDirectoryPageHandler.getPathName(name); 197 children.add(new TransitionalPage(_root, name, _path + "/" + pathName, _resolver, _userDirectoryPageHandler, _syncContentsCollectionDAO, _skinsManager, _pageDataTypeExtensionPoint, _zoneDataTypeExtensionPoint, _serviceExtensionPoint, _zoneItemDataTypeExtensionPoint)); 198 } 199 200 Map<String, String> userPagesContent = _userDirectoryPageHandler.getUserPagesContent(_root, _userDirectoryPageHandler.getName(_path)); 201 for (Entry<String, String> entry : userPagesContent.entrySet()) 202 { 203 String contentTypeId = _userDirectoryPageHandler.getContentTypeId(_root); 204 String classificationMetadata = _userDirectoryPageHandler.getClassificationMetadata(_root); 205 Content content; 206 try 207 { 208 content = _resolver.resolveById(entry.getValue()); 209 } 210 catch (AmetysRepositoryException e) 211 { 212 // content does not exist, skip to next iteration 213 break; 214 } 215 if (content == null || !Arrays.asList(content.getTypes()).contains(contentTypeId) || !content.getMetadataHolder().hasMetadata(classificationMetadata)) 216 { 217 break; 218 } 219 220 String metadataValue = _userDirectoryPageHandler.getTransformedClassificationMetadataValue(_root, content); 221 if (metadataValue != null && metadataValue.length() == _path.split("/").length) 222 { 223 children.add(new UserPage(_root, content, _path, _resolver, _userDirectoryPageHandler, _syncContentsCollectionDAO, _skinsManager, _pageDataTypeExtensionPoint, _zoneDataTypeExtensionPoint, _serviceExtensionPoint, _zoneItemDataTypeExtensionPoint)); 224 } 225 } 226 } 227 else 228 { 229 Map<String, String> userPagesContent = _userDirectoryPageHandler.getUserPagesContent(_root, _path); 230 for (String contentId : userPagesContent.values()) 231 { 232 try 233 { 234 Content content = _resolver.resolveById(contentId); 235 children.add(new UserPage(_root, content, _path, _resolver, _userDirectoryPageHandler, _syncContentsCollectionDAO, _skinsManager, _pageDataTypeExtensionPoint, _zoneDataTypeExtensionPoint, _serviceExtensionPoint, _zoneItemDataTypeExtensionPoint)); 236 } 237 catch (UnknownAmetysObjectException e) 238 { 239 System.out.println("Content does not exist anymore"); 240 } 241 } 242 } 243 244 return new CollectionIterable<>(children); 245 } 246 247 @Override 248 public AmetysObjectIterable< ? extends Page> getChildrenPages(boolean includeInvisiblePage) throws AmetysRepositoryException 249 { 250 if (includeInvisiblePage) 251 { 252 return getChildrenPages(); 253 } 254 else 255 { 256 ArrayList<Page> children = new ArrayList<>(); 257 return new CollectionIterable<>(children); 258 } 259 } 260 261 @Override 262 public String getPathInSitemap() throws AmetysRepositoryException 263 { 264 String path = StringUtils.lowerCase(_path); 265 return _root.getPathInSitemap() + "/" + path; 266 } 267 268 @Override 269 public Site getSite() throws AmetysRepositoryException 270 { 271 return _root.getSite(); 272 } 273 274 @Override 275 public String getSiteName() throws AmetysRepositoryException 276 { 277 return _root.getSiteName(); 278 } 279 280 @Override 281 public Sitemap getSitemap() throws AmetysRepositoryException 282 { 283 return _root.getSitemap(); 284 } 285 286 @Override 287 public String getSitemapName() throws AmetysRepositoryException 288 { 289 return _root.getSitemapName(); 290 } 291 292 @SuppressWarnings("unchecked") 293 @Override 294 public <A extends AmetysObject> A getChild(String path) throws AmetysRepositoryException, UnknownAmetysObjectException 295 { 296 if (path.isEmpty()) 297 { 298 throw new AmetysRepositoryException("path must be non empty"); 299 } 300 301 String completePath = _path + "/" + path; 302 int depth = _initialDepth - completePath.split("/").length + 1; 303 if (depth > 0) 304 { 305 String namePath = StringUtils.substringAfterLast(completePath, "/"); 306 String parentPath = StringUtils.substringBeforeLast(completePath, "/"); 307 308 SortedSet<String> transitionalPagesName = _userDirectoryPageHandler.getTransitionalPagesName(_root, parentPath); 309 Map<String, String> userPagesContent = _userDirectoryPageHandler.getUserPagesContent(_root, parentPath); 310 String name = _userDirectoryPageHandler.getName(namePath); 311 if (transitionalPagesName.contains(name)) 312 { 313 TransitionalPage page = new TransitionalPage(_root, name, completePath, _resolver, _userDirectoryPageHandler, _syncContentsCollectionDAO, _skinsManager, _pageDataTypeExtensionPoint, _zoneDataTypeExtensionPoint, _serviceExtensionPoint, _zoneItemDataTypeExtensionPoint); 314 return (A) page; 315 } 316 else if (userPagesContent.containsKey(name)) 317 { 318 String contentId = userPagesContent.get(name); 319 Content syncContent = _resolver.resolveById(contentId); 320 UserPage page = new UserPage(_root, syncContent, parentPath, _resolver, _userDirectoryPageHandler, _syncContentsCollectionDAO, _skinsManager, _pageDataTypeExtensionPoint, _zoneDataTypeExtensionPoint, _serviceExtensionPoint, _zoneItemDataTypeExtensionPoint); 321 return (A) page; 322 } 323 else 324 { 325 throw new UnknownAmetysObjectException("No transitional page named " + name + " (full page path " + path + ")."); 326 } 327 } 328 else 329 { 330 String userPath = StringUtils.substringBeforeLast(completePath, "/"); 331 String contentName = StringUtils.substringAfterLast(completePath, "/"); 332 333 Map<String, String> userPagesContent = _userDirectoryPageHandler.getUserPagesContent(_root, userPath); 334 if (userPagesContent.containsKey(contentName)) 335 { 336 Content content = _resolver.resolveById(userPagesContent.get(contentName)); 337 UserPage page = new UserPage(_root, content, userPath, _resolver, _userDirectoryPageHandler, _syncContentsCollectionDAO, _skinsManager, _pageDataTypeExtensionPoint, _zoneDataTypeExtensionPoint, _serviceExtensionPoint, _zoneItemDataTypeExtensionPoint); 338 return (A) page; 339 } 340 else 341 { 342 throw new UnknownAmetysObjectException("No user content named " + contentName + " (full page path " + path + ")."); 343 } 344 } 345 } 346 347 @SuppressWarnings("unchecked") 348 @Override 349 public AmetysObjectIterable<? extends AmetysObject> getChildren() throws AmetysRepositoryException 350 { 351 return getChildrenPages(); 352 } 353 354 @Override 355 public boolean hasChild(String name) throws AmetysRepositoryException 356 { 357 int depth = _initialDepth - _path.split("/").length; 358 if (depth > 0) 359 { 360 SortedSet<String> transitionalPagesName = _userDirectoryPageHandler.getTransitionalPagesName(_root, _path); 361 Map<String, String> userPagesContent = _userDirectoryPageHandler.getUserPagesContent(_root, _path); 362 return transitionalPagesName.contains(name) || userPagesContent.containsKey(name); 363 } 364 else 365 { 366 Map<String, String> userPagesContent = _userDirectoryPageHandler.getUserPagesContent(_root, _path); 367 return userPagesContent.containsKey(name); 368 } 369 } 370 371 @Override 372 public String getId() throws AmetysRepositoryException 373 { 374 // E.g: udtransitional://path?rootId=... 375 return "udtransitional://" + _path + "?rootId=" + _root.getId(); 376 } 377 378 @Override 379 public String getName() throws AmetysRepositoryException 380 { 381 return StringUtils.lowerCase(_prefix); 382 } 383 384 @SuppressWarnings("unchecked") 385 @Override 386 public Page getParent() throws AmetysRepositoryException 387 { 388 if (_path.split("/").length > 1) 389 { 390 String parentPath = StringUtils.substringBeforeLast(_path, "/"); 391 String pathName = parentPath; 392 if (StringUtils.contains(pathName, "/")) 393 { 394 pathName = StringUtils.substringAfterLast(pathName, "/"); 395 } 396 397 String name = _userDirectoryPageHandler.getName(pathName); 398 return new TransitionalPage(_root, name, parentPath, _resolver, _userDirectoryPageHandler, _syncContentsCollectionDAO, _skinsManager, _pageDataTypeExtensionPoint, _zoneDataTypeExtensionPoint, _serviceExtensionPoint, _zoneItemDataTypeExtensionPoint); 399 } 400 else 401 { 402 return _root; 403 } 404 } 405 406 @Override 407 public String getParentPath() throws AmetysRepositoryException 408 { 409 if (_path.split("/").length > 1) 410 { 411 String path = StringUtils.lowerCase(_path); 412 return _root.getPath() + "/" + StringUtils.substringBeforeLast(path, "/"); 413 } 414 else 415 { 416 return _root.getPath(); 417 } 418 } 419 420 @Override 421 public String getPath() throws AmetysRepositoryException 422 { 423 return getParentPath() + "/" + getName(); 424 } 425 426 public ModelLessDataHolder getDataHolder() 427 { 428 RepositoryData repositoryData = new MemoryRepositoryData(getName()); 429 return new DefaultModelLessDataHolder(_pageDataTypeExtensionPoint, repositoryData); 430 } 431 432 @Override 433 public Set<String> getTags() throws AmetysRepositoryException 434 { 435 return Collections.emptySet(); 436 } 437 438 @Override 439 public boolean isVisible() throws AmetysRepositoryException 440 { 441 return false; 442 } 443 444 @Override 445 public Page getChildPageAt(int index) throws UnknownAmetysObjectException, AmetysRepositoryException 446 { 447 return getChildrenPages().stream().collect(Collectors.toList()).get(index); 448 } 449}