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