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