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