001/* 002 * Copyright 2010 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.web.repository.page.jcr; 017 018import java.util.List; 019import java.util.Optional; 020 021import javax.jcr.Node; 022import javax.jcr.RepositoryException; 023 024import org.apache.commons.collections4.MapUtils; 025import org.slf4j.Logger; 026import org.slf4j.LoggerFactory; 027import org.xml.sax.ContentHandler; 028import org.xml.sax.SAXException; 029 030import org.ametys.cms.repository.Content; 031import org.ametys.cms.repository.DefaultContent; 032import org.ametys.plugins.repository.AmetysObject; 033import org.ametys.plugins.repository.AmetysRepositoryException; 034import org.ametys.plugins.repository.CopiableAmetysObject; 035import org.ametys.plugins.repository.ModifiableTraversableAmetysObject; 036import org.ametys.plugins.repository.RepositoryConstants; 037import org.ametys.plugins.repository.RepositoryIntegrityViolationException; 038import org.ametys.plugins.repository.data.holder.ModifiableModelAwareDataHolder; 039import org.ametys.plugins.repository.data.holder.ModifiableModelLessDataHolder; 040import org.ametys.plugins.repository.data.holder.impl.DefaultModifiableModelAwareDataHolder; 041import org.ametys.plugins.repository.data.holder.impl.DefaultModifiableModelLessDataHolder; 042import org.ametys.plugins.repository.data.repositorydata.ModifiableRepositoryData; 043import org.ametys.plugins.repository.data.repositorydata.impl.JCRRepositoryData; 044import org.ametys.plugins.repository.jcr.JCRAmetysObject; 045import org.ametys.plugins.repository.jcr.SimpleAmetysObject; 046import org.ametys.plugins.repository.metadata.ModifiableCompositeMetadata; 047import org.ametys.runtime.model.View; 048import org.ametys.runtime.model.exception.NotUniqueTypeException; 049import org.ametys.runtime.model.exception.UnknownTypeException; 050import org.ametys.runtime.model.type.DataContext; 051import org.ametys.web.parameters.view.ViewParametersManager; 052import org.ametys.web.parameters.view.ViewParametersModel; 053import org.ametys.web.repository.content.SharedContent; 054import org.ametys.web.repository.content.WebContent; 055import org.ametys.web.repository.page.ModifiableZone; 056import org.ametys.web.repository.page.ModifiableZoneItem; 057import org.ametys.web.repository.page.Page; 058import org.ametys.web.repository.page.Zone; 059import org.ametys.web.service.Service; 060 061/** 062 * implementation of a {@link ModifiableZoneItem}. 063 */ 064public class DefaultZoneItem extends SimpleAmetysObject<DefaultZoneItemFactory> implements ModifiableZoneItem, CopiableAmetysObject 065{ 066 /** Constant for title metadata. */ 067 public static final String METADATA_TYPE = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":type"; 068 /** Constant for service metadata. */ 069 public static final String METADATA_SERVICE = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":service"; 070 /** Constant for content metadata. */ 071 public static final String METADATA_CONTENT = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":content"; 072 /** Constant service parameters node type. */ 073 public static final String SERVICE_PARAM_NODETYPE = RepositoryConstants.NAMESPACE_PREFIX + ":compositeMetadata"; 074 075 /** 076 * Constant for "metadata set name" metadata. 077 * @deprecated Use {@link #METADATA_VIEW_NAME} instead 078 */ 079 @Deprecated 080 public static final String METADATA_METADATASET_NAME = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":metadataSetName"; 081 082 /** 083 * Constant for view name metadata. 084 * TODO NEWATTRIBUTEAPI_SERVICE: Change the name of this metadata to viewName (there may be a lot of impacts..) 085 */ 086 public static final String METADATA_VIEW_NAME = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":metadataSetName"; 087 088 private static final Logger __LOGGER = LoggerFactory.getLogger(DefaultZoneItem.class); 089 090 /** 091 * Creates a {@link DefaultZoneItem}. 092 * 093 * @param node the node backing this {@link AmetysObject}. 094 * @param parentPath the parent path in the Ametys hierarchy. 095 * @param factory the {@link DefaultZoneItemFactory} which creates the AmetysObject. 096 */ 097 public DefaultZoneItem(Node node, String parentPath, DefaultZoneItemFactory factory) 098 { 099 super(node, parentPath, factory); 100 } 101 102 public ZoneType getType() throws AmetysRepositoryException 103 { 104 try 105 { 106 return ZoneType.valueOf(getNode().getProperty(METADATA_TYPE).getString()); 107 } 108 catch (RepositoryException e) 109 { 110 throw new AmetysRepositoryException("Unable to get type property", e); 111 } 112 } 113 114 public void setType(ZoneType type) throws AmetysRepositoryException 115 { 116 try 117 { 118 getNode().setProperty(METADATA_TYPE, type.name()); 119 } 120 catch (RepositoryException e) 121 { 122 throw new AmetysRepositoryException("Unable to set type property", e); 123 } 124 } 125 126 public <C extends Content> C getContent() throws AmetysRepositoryException 127 { 128 try 129 { 130 return _getFactory().<C>resolveAmetysObject(getNode().getProperty(METADATA_CONTENT).getNode()); 131 } 132 catch (RepositoryException e) 133 { 134 throw new AmetysRepositoryException("Unable to get content property", e); 135 } 136 } 137 138 public <C extends Content> void setContent(C content) throws AmetysRepositoryException 139 { 140 try 141 { 142 // FIXME stop using getNode that is JCR but use AmetysObject API... if it seems cool to you 143 getNode().setProperty(METADATA_CONTENT, ((JCRAmetysObject) content).getNode()); 144 } 145 catch (RepositoryException e) 146 { 147 throw new AmetysRepositoryException("Unable to set content property", e); 148 } 149 } 150 151 public String getViewName() throws AmetysRepositoryException 152 { 153 try 154 { 155 if (getNode().hasProperty(METADATA_VIEW_NAME)) 156 { 157 return getNode().getProperty(METADATA_VIEW_NAME).getString(); 158 } 159 return null; 160 } 161 catch (RepositoryException e) 162 { 163 throw new AmetysRepositoryException("Unable to get view name property.", e); 164 } 165 } 166 167 @Override 168 public void setViewName(String viewName) throws AmetysRepositoryException 169 { 170 try 171 { 172 getNode().setProperty(METADATA_VIEW_NAME, viewName); 173 } 174 catch (RepositoryException e) 175 { 176 throw new AmetysRepositoryException("Unable to set view name property.", e); 177 } 178 } 179 180 @Override 181 public String getServiceId() throws AmetysRepositoryException 182 { 183 try 184 { 185 return getNode().getProperty(METADATA_SERVICE).getString(); 186 } 187 catch (RepositoryException e) 188 { 189 throw new AmetysRepositoryException("Unable to get service property", e); 190 } 191 } 192 193 @Override 194 public void setServiceId(String serviceId) throws AmetysRepositoryException 195 { 196 try 197 { 198 getNode().setProperty(METADATA_SERVICE, serviceId); 199 } 200 catch (RepositoryException e) 201 { 202 throw new AmetysRepositoryException("Unable to set service property", e); 203 } 204 } 205 206 /** 207 * {@inheritDoc} 208 * @throws IllegalStateException if the service referenced by this zone item does not exist 209 */ 210 public ModifiableModelAwareDataHolder getServiceParameters() throws AmetysRepositoryException, IllegalStateException 211 { 212 String serviceId = getServiceId(); 213 Service service = _getFactory().getService(serviceId); 214 if (service != null) 215 { 216 ModifiableRepositoryData repositoryData = _getServiceParametersRepositoryData(); 217 return new DefaultModifiableModelAwareDataHolder(repositoryData, service); 218 } 219 else 220 { 221 throw new IllegalStateException("The service '" + serviceId + "' referenced by the zone item '" + getId() + "' (" + getPath() + ") does not exist"); 222 } 223 } 224 225 /** 226 * Retrieves the {@link ModifiableRepositoryData} containing the service parameters of this zone item 227 * @return the {@link ModifiableRepositoryData} containing the service parameters of this zone item 228 */ 229 protected ModifiableRepositoryData _getServiceParametersRepositoryData() 230 { 231 JCRRepositoryData zoneItemRepositoryData = new JCRRepositoryData(getNode()); 232 return zoneItemRepositoryData.hasValue(SERVICE_PARAMETERS_DATA_NAME) ? zoneItemRepositoryData.getRepositoryData(SERVICE_PARAMETERS_DATA_NAME) : zoneItemRepositoryData.addRepositoryData(SERVICE_PARAMETERS_DATA_NAME, SERVICE_PARAM_NODETYPE); 233 } 234 235 public ModifiableModelAwareDataHolder getZoneItemParametersHolder() throws AmetysRepositoryException 236 { 237 JCRRepositoryData zoneItemRepositoryData = new JCRRepositoryData(getNode()); 238 239 ViewParametersManager viewParametersManager = _getFactory().getViewParametersManager(); 240 Optional<ViewParametersModel> zoneItemViewParameters = viewParametersManager.getZoneItemViewParametersModel(this); 241 if (zoneItemViewParameters.isEmpty()) 242 { 243 zoneItemViewParameters = Optional.of(new ViewParametersModel("zoneitem-no-param", new View(), MapUtils.EMPTY_SORTED_MAP)); 244 } 245 return viewParametersManager.getParametersHolder(zoneItemRepositoryData, zoneItemViewParameters.get()); 246 } 247 248 public ModifiableModelLessDataHolder getDataHolder() 249 { 250 ModifiableRepositoryData repositoryData = new JCRRepositoryData(getNode()); 251 return new DefaultModifiableModelLessDataHolder(_getFactory().getPageDataTypeExtensionPoint(), repositoryData); 252 } 253 254 /* 255 * The default implementation is overridden here to not use the getDataHolder()#dataToSAX implementation 256 * Each #dataToSAX method calls the next one and the implementation that has to change is the last one. 257 * If the getDataHolder() is used for one, the next ones will be called with the default implementation 258 */ 259 public void dataToSAX(ContentHandler contentHandler, DataContext context) throws SAXException, UnknownTypeException, NotUniqueTypeException 260 { 261 for (String name : getDataNames()) 262 { 263 dataToSAX(contentHandler, name, context); 264 } 265 } 266 267 public void dataToSAX(ContentHandler contentHandler, String dataPath, DataContext context) throws SAXException 268 { 269 if (!SERVICE_PARAMETERS_DATA_NAME.equals(dataPath) 270 && !ViewParametersManager.CONTENT_VIEW_PARAMETERS_COMPOSITE_NAME.equals(dataPath) 271 && !ViewParametersManager.SERVICE_VIEW_PARAMETERS_COMPOSITE_NAME.equals(dataPath) 272 && !ViewParametersManager.VIEW_PARAMETERS_COMPOSITE_NAME.equals(dataPath)) 273 { 274 ModifiableZoneItem.super.dataToSAX(contentHandler, dataPath, context); 275 } 276 } 277 278 @Override 279 public ModifiableCompositeMetadata getMetadataHolder() 280 { 281 __LOGGER.warn("The getMetadataHolder method of DefaultZoneItem is deprecated. Use the getDataHolder instead. StackTrace: ", new Exception()); 282 return super.getMetadataHolder(); 283 } 284 285 @Override 286 public ModifiableZoneItem copyTo(ModifiableTraversableAmetysObject parent, String name, List<String> restrictTo) throws AmetysRepositoryException 287 { 288 return copyTo(parent, name); 289 } 290 291 @Override 292 public ModifiableZoneItem copyTo(ModifiableTraversableAmetysObject parent, String name) throws AmetysRepositoryException 293 { 294 if (parent instanceof DefaultZone) 295 { 296 ModifiableZone parentZone = (ModifiableZone) parent; 297 ModifiableZoneItem cZoneItem = parentZone.addZoneItem(); 298 299 ZoneType type = getType(); 300 cZoneItem.setType(type); 301 302 // Copy view parameters 303 getZoneItemParametersHolder().copyTo(cZoneItem.getZoneItemParametersHolder()); 304 305 ViewParametersManager viewParametersManager = _getFactory().getViewParametersManager(); 306 if (ZoneType.SERVICE.equals(type)) 307 { 308 cZoneItem.setServiceId(getServiceId()); 309 getServiceParameters().copyTo(cZoneItem.getServiceParameters()); 310 311 // Copy view parameters 312 viewParametersManager.copyServiceViewParameters(this, cZoneItem); 313 } 314 else 315 { 316 Content content = getContent(); 317 if (content instanceof WebContent) 318 { 319 WebContent webContent = (WebContent) content; 320 if (getZone().getPage().getSiteName().equals(webContent.getSiteName())) 321 { 322 if (webContent instanceof SharedContent) 323 { 324 // No need to copy a shared content, set the reference to the shared content 325 cZoneItem.setContent(webContent); 326 cZoneItem.setViewName(getViewName()); 327 } 328 else if (webContent instanceof DefaultContent) 329 { 330 Content cContent = ((DefaultContent) webContent).copyTo(webContent.getSite().getRootContents(), null); 331 cZoneItem.setContent(cContent); 332 333 // Update references to AmetysObject in RichText 334 _getFactory().getCopySiteComponent().updateLinksInRichText(getZone().getPage(), cZoneItem.getZone().getPage(), content, cContent); 335 } 336 } 337 else 338 { 339 // Copy reference 340 cZoneItem.setContent(getContent()); 341 } 342 } 343 else 344 { 345 // Copy reference 346 cZoneItem.setContent(getContent()); 347 } 348 349 String viewName = getViewName(); 350 if (viewName != null) 351 { 352 cZoneItem.setViewName(viewName); 353 } 354 355 // Copy view parameters 356 viewParametersManager.copyContentViewParameters(this, cZoneItem); 357 } 358 359 return cZoneItem; 360 } 361 else 362 { 363 throw new AmetysRepositoryException("Can not copy a zone item " + getId() + " of type DefaultZoneItem to something that is not a DefaultZone " + parent.getId()); 364 } 365 } 366 367 @Override 368 public boolean canMoveTo(AmetysObject newParent) throws AmetysRepositoryException 369 { 370 return newParent instanceof DefaultZone; 371 } 372 373 @Override 374 public void moveTo(AmetysObject newParent, boolean renameIfExist) throws AmetysRepositoryException, RepositoryIntegrityViolationException 375 { 376 if (!canMoveTo(newParent)) 377 { 378 throw new AmetysRepositoryException("Can not move a zone item " + getId() + " of type DefaultZoneItem to something that is not a DefaultZone " + newParent.getId()); 379 } 380 381 Node node = getNode(); 382 383 try 384 { 385 DefaultZone newParentZone = (DefaultZone) newParent; 386 387 // Move node 388 node.getSession().move(node.getPath(), newParentZone.getNode().getPath() + "/" + DefaultZone.ZONEITEMS_NODE_NAME + "/" + node.getName()); 389 } 390 catch (RepositoryException e) 391 { 392 throw new AmetysRepositoryException("Can not move DefaultZoneItem " + getId() + " to DefaultZone " + newParent.getId(), e); 393 } 394 } 395 396 @Override 397 public void orderBefore(AmetysObject siblingObject) throws AmetysRepositoryException 398 { 399 if (siblingObject != null && !(siblingObject instanceof JCRAmetysObject)) 400 { 401 throw new AmetysRepositoryException(String.format("Unable to order zone item '%s' before sibling '%s' (sibling is not a JCRAmetysObject)", this.getId(), siblingObject.getId())); 402 } 403 404 Node node = getNode(); 405 Node siblingNode = siblingObject != null ? ((JCRAmetysObject) siblingObject).getNode() : null; 406 407 try 408 { 409 node.getParent().orderBefore(node.getName() + "[" + node.getIndex() + "]", siblingNode != null ? siblingNode.getName() + "[" + siblingNode.getIndex() + "]" : null); 410 } 411 catch (RepositoryException e) 412 { 413 throw new AmetysRepositoryException(String.format("Unable to order zone item '%s' before sibling '%s'", this.getId(), siblingObject != null ? siblingObject.getId() : ""), e); 414 } 415 } 416 417 @Override 418 public Zone getZone() 419 { 420 return getParent().getParent(); 421 } 422 423 public ModifiableModelAwareDataHolder getContentViewParametersHolder(String contentViewName) throws AmetysRepositoryException 424 { 425 if (getType() != ZoneType.CONTENT) 426 { 427 throw new AmetysRepositoryException("Can't get content view parameters from a not content zone item"); 428 } 429 430 JCRRepositoryData zoneItemRepositoryData = new JCRRepositoryData(getNode()); 431 432 Content content = getContent(); 433 434 // Get the skin Id 435 Page page = getZone().getPage(); 436 String skinId = page.getSite().getSkinId(); 437 438 ViewParametersManager viewParametersManager = _getFactory().getViewParametersManager(); 439 Optional<ViewParametersModel> contentViewParameters = viewParametersManager.getContentViewParametersModel(skinId, content, contentViewName); 440 if (contentViewParameters.isEmpty()) 441 { 442 contentViewParameters = Optional.of(new ViewParametersModel("content-no-param", new View(), MapUtils.EMPTY_SORTED_MAP)); 443 } 444 445 return viewParametersManager.getContentViewParametersHolder(zoneItemRepositoryData, contentViewName, contentViewParameters.get()); 446 } 447 448 public ModifiableModelAwareDataHolder getServiceViewParametersHolder(String serviceViewName) throws AmetysRepositoryException 449 { 450 if (getType() != ZoneType.SERVICE) 451 { 452 throw new AmetysRepositoryException("Can't get service view parameters from a not service zone item"); 453 } 454 455 JCRRepositoryData zoneItemRepositoryData = new JCRRepositoryData(getNode()); 456 457 String serviceId = getServiceId(); 458 459 // Get the skin Id 460 Page page = getZone().getPage(); 461 String skinId = page.getSite().getSkinId(); 462 463 ViewParametersManager viewParametersManager = _getFactory().getViewParametersManager(); 464 Optional<ViewParametersModel> serviceViewParameters = viewParametersManager.getServiceViewParametersModel(skinId, serviceId, serviceViewName); 465 if (serviceViewParameters.isEmpty()) 466 { 467 serviceViewParameters = Optional.of(new ViewParametersModel("service-no-param", new View(), MapUtils.EMPTY_SORTED_MAP)); 468 } 469 470 return viewParametersManager.getServiceViewParametersHolder(zoneItemRepositoryData, serviceViewName, serviceViewParameters.get()); 471 } 472}