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.plugins.odfweb.clientside; 017 018import java.util.Arrays; 019import java.util.HashMap; 020import java.util.Map; 021import java.util.Set; 022 023import javax.jcr.Node; 024import javax.jcr.Property; 025import javax.jcr.RepositoryException; 026import javax.jcr.Value; 027 028import org.apache.avalon.framework.service.ServiceException; 029import org.apache.avalon.framework.service.ServiceManager; 030import org.apache.commons.lang.StringUtils; 031import org.apache.jackrabbit.value.StringValue; 032 033import org.ametys.core.observation.Event; 034import org.ametys.core.observation.ObservationManager; 035import org.ametys.core.ui.Callable; 036import org.ametys.core.ui.StaticClientSideElement; 037import org.ametys.core.util.LambdaUtils; 038import org.ametys.odf.catalog.Catalog; 039import org.ametys.odf.catalog.CatalogsManager; 040import org.ametys.plugins.odfweb.OdfWebObservationConstants; 041import org.ametys.plugins.odfweb.repository.OdfPageHandler; 042import org.ametys.plugins.odfweb.repository.FirstLevelPageFactory; 043import org.ametys.plugins.repository.AmetysObjectResolver; 044import org.ametys.plugins.repository.jcr.JCRAmetysObject; 045import org.ametys.plugins.repository.metadata.CompositeMetadata; 046import org.ametys.plugins.repository.metadata.ModifiableCompositeMetadata; 047import org.ametys.runtime.i18n.I18nizableText; 048import org.ametys.web.ObservationConstants; 049import org.ametys.web.repository.page.ModifiablePage; 050import org.ametys.web.repository.page.Page; 051 052import com.google.common.collect.ObjectArrays; 053 054/** 055 * This element creates an action button to set the ODF root page 056 */ 057public class ODFRootClientSideElement extends StaticClientSideElement 058{ 059 /** Ametys resolver */ 060 protected AmetysObjectResolver _resolver; 061 /** Catalog manager */ 062 protected CatalogsManager _catalogsManager; 063 /** Odf page handler */ 064 protected OdfPageHandler _odfPageHandler; 065 /** Observation manager */ 066 protected ObservationManager _observationManager; 067 068 @Override 069 public void service(ServiceManager smanager) throws ServiceException 070 { 071 super.service(smanager); 072 _resolver = (AmetysObjectResolver) smanager.lookup(AmetysObjectResolver.ROLE); 073 _catalogsManager = (CatalogsManager) smanager.lookup(CatalogsManager.ROLE); 074 _odfPageHandler = (OdfPageHandler) smanager.lookup(OdfPageHandler.ROLE); 075 _observationManager = (ObservationManager) smanager.lookup(ObservationManager.ROLE); 076 } 077 078 /** 079 * Get odf root page status for the current page 080 * @param pageId the page identifier 081 * @return A map representing its status 082 */ 083 @Callable 084 public Map<String, Object> getStatus(String pageId) 085 { 086 Map<String, Object> results = new HashMap<>(); 087 Page page = _resolver.resolveById(pageId); 088 089 // page id 090 results.put("page-id", page.getId()); 091 092 if (page instanceof JCRAmetysObject) 093 { 094 if (_odfPageHandler.isODFRootPage(page)) 095 { 096 results.put("is-odf-root", true); 097 098 // Odf root page description 099 I18nizableText rawDescription = (I18nizableText) this._script.getParameters().get("odf-root-page-description"); 100 Map<String, I18nizableText> i18nParams = new HashMap<>(); 101 i18nParams.put("title", new I18nizableText(page.getTitle())); 102 103 I18nizableText description = new I18nizableText(rawDescription.getCatalogue(), rawDescription.getKey(), i18nParams); 104 results.put("odf-root-page-description", description); 105 106 // remove odf root 107 rawDescription = (I18nizableText) this._script.getParameters().get("remove-odf-page-description"); 108 description = new I18nizableText(rawDescription.getCatalogue(), rawDescription.getKey(), i18nParams); 109 results.put("remove-odf-page-description", description); 110 111 // Catalog Odf root page descrption 112 String catalogCode = page.getMetadataHolder().getString(OdfPageHandler.CATALOG_METADATA_NAME, null); 113 if (StringUtils.isNotEmpty(catalogCode)) 114 { 115 Catalog catalog = _catalogsManager.getCatalog(catalogCode); 116 I18nizableText catalogText = new I18nizableText(catalog != null ? catalog.getTitle() : catalogCode); 117 118 i18nParams = new HashMap<>(); 119 i18nParams.put("catalog", catalogText); 120 121 rawDescription = (I18nizableText) this._script.getParameters().get("catalog-description"); 122 description = new I18nizableText(rawDescription.getCatalogue(), rawDescription.getKey(), i18nParams); 123 results.put("catalog-description", description); 124 } 125 } 126 else 127 { 128 // Add odf root page 129 results.put("add-odf-page", true); 130 131 I18nizableText rawDescription = (I18nizableText) this._script.getParameters().get("add-odf-page-description"); 132 Map<String, I18nizableText> i18nParams = new HashMap<>(); 133 i18nParams.put("title", new I18nizableText(page.getTitle())); 134 135 I18nizableText description = new I18nizableText(rawDescription.getCatalogue(), rawDescription.getKey(), i18nParams); 136 results.put("add-odf-page-description", description); 137 } 138 } 139 else 140 { 141 // Invalid page 142 results.put("invalid-page", true); 143 144 I18nizableText rawDescription = (I18nizableText) this._script.getParameters().get("no-jcr-page-description"); 145 Map<String, I18nizableText> i18nParams = new HashMap<>(); 146 i18nParams.put("title", new I18nizableText(page.getTitle())); 147 148 I18nizableText description = new I18nizableText(rawDescription.getCatalogue(), rawDescription.getKey(), i18nParams); 149 results.put("no-jcr-page-description", description); 150 } 151 152 return results; 153 } 154 155 /** 156 * Retrieves odf root page properties 157 * @param pageId page identifier 158 * @return the map of properties 159 */ 160 @Callable 161 public Map<String, Object> getOdfRootPageInfo(String pageId) 162 { 163 Map<String, Object> properties = new HashMap<>(); 164 165 Page page = _resolver.resolveById(pageId); 166 Set<Page> currentODFPages = _odfPageHandler.getOdfRootPages(page.getSiteName(), page.getSitemapName()); 167 168 if (_catalogsManager.getCatalogs().isEmpty()) 169 { 170 properties.put("noCatalog", true); 171 } 172 173 if (!currentODFPages.contains(page)) 174 { 175 properties.put("isOdfRootPage", false); 176 } 177 else 178 { 179 properties.put("isOdfRootPage", true); 180 181 CompositeMetadata metadataHolder = page.getMetadataHolder(); 182 properties.put("catalog", metadataHolder.getString(OdfPageHandler.CATALOG_METADATA_NAME, "")); 183 properties.put("firstLevel", metadataHolder.getString(OdfPageHandler.LEVEL1_METADATA_NAME, "")); 184 properties.put("secondLevel", metadataHolder.getString(OdfPageHandler.LEVEL2_METADATA_NAME, "")); 185 } 186 187 return properties; 188 } 189 190 /** 191 * Add or update the odf root page property on a page 192 * @param pageId the page identifier 193 * @param catalog the catalog to set 194 * @param firstLevel the first level to set 195 * @param secondLevel the second level to set 196 * @throws RepositoryException if a repository error occurs 197 * @return true if the page has actually been modified 198 */ 199 @Callable 200 public boolean addOrUpdateOdfRootProperty(String pageId, String catalog, String firstLevel, String secondLevel) throws RepositoryException 201 { 202 Page page = _resolver.resolveById(pageId); 203 Set<Page> currentODFPages = _odfPageHandler.getOdfRootPages(page.getSiteName(), page.getSitemapName()); 204 205 if (!currentODFPages.contains(page)) 206 { 207 return _addOdfRootProperty(page, catalog, firstLevel, secondLevel); 208 } 209 else 210 { 211 return _updateOdfRootProperty(page, catalog, firstLevel, secondLevel); 212 } 213 } 214 215 private boolean _addOdfRootProperty(Page page, String catalog, String firstLevel, String secondLevel) throws RepositoryException 216 { 217 boolean hasChanges = false; 218 219 if (page instanceof JCRAmetysObject) 220 { 221 JCRAmetysObject jcrPage = (JCRAmetysObject) page; 222 223 // Add odf root property 224 Property virtualProperty = _getVirtualProperty(jcrPage); 225 Value[] oldValues = virtualProperty != null ? virtualProperty.getValues() : new Value[]{}; 226 227 boolean hasVirtualProperty = Arrays.stream(oldValues) 228 .anyMatch(LambdaUtils.wrapPredicate(ODFRootClientSideElement::isVirtualPageFactoryValue)); 229 230 if (!hasVirtualProperty) 231 { 232 Value[] newValues = ObjectArrays.concat(oldValues, new StringValue(FirstLevelPageFactory.class.getName())); 233 _setVirtualProperty(jcrPage, newValues); 234 } 235 236 ModifiableCompositeMetadata pageMeta = jcrPage.getMetadataHolder(); 237 238 // Set the ODF catalog property 239 if (StringUtils.isNotEmpty(catalog)) 240 { 241 pageMeta.setMetadata(OdfPageHandler.CATALOG_METADATA_NAME, catalog); 242 } 243 244 // Set the level properties. 245 if (StringUtils.isNotEmpty(firstLevel)) 246 { 247 pageMeta.setMetadata(OdfPageHandler.LEVEL1_METADATA_NAME, firstLevel); 248 } 249 if (StringUtils.isNotEmpty(secondLevel)) 250 { 251 pageMeta.setMetadata(OdfPageHandler.LEVEL2_METADATA_NAME, secondLevel); 252 } 253 254 if (jcrPage.needsSave()) 255 { 256 jcrPage.saveChanges(); 257 hasChanges = true; 258 } 259 } 260 261 if (hasChanges) 262 { 263 _notifyOdfRootPageChange(page); 264 } 265 266 return hasChanges; 267 } 268 269 private boolean _updateOdfRootProperty(Page page, String catalog, String firstLevel, String secondLevel) 270 { 271 boolean hasChanges = false; 272 273 if (page instanceof ModifiablePage) 274 { 275 ModifiablePage modifiablePage = (ModifiablePage) page; 276 ModifiableCompositeMetadata pageMeta = modifiablePage.getMetadataHolder(); 277 278 // Set the ODF catalog property 279 if (StringUtils.isNotEmpty(catalog)) 280 { 281 pageMeta.setMetadata(OdfPageHandler.CATALOG_METADATA_NAME, catalog); 282 } 283 284 // Set the level properties. 285 if (StringUtils.isNotEmpty(firstLevel)) 286 { 287 pageMeta.setMetadata(OdfPageHandler.LEVEL1_METADATA_NAME, firstLevel); 288 } 289 if (StringUtils.isNotEmpty(secondLevel)) 290 { 291 pageMeta.setMetadata(OdfPageHandler.LEVEL2_METADATA_NAME, secondLevel); 292 } 293 294 if (modifiablePage.needsSave()) 295 { 296 modifiablePage.saveChanges(); 297 hasChanges = true; 298 } 299 } 300 301 if (hasChanges) 302 { 303 // Odf root updated observer 304 Map<String, Object> eventParams = new HashMap<>(); 305 eventParams.put(ObservationConstants.ARGS_PAGE, page); 306 _observationManager.notify(new Event(OdfWebObservationConstants.ODF_ROOT_UPDATED, _currentUserProvider.getUser(), eventParams)); 307 308 _notifyOdfRootPageChange(page); 309 } 310 311 return hasChanges; 312 } 313 314 /** 315 * Remove the odf root page property on a page 316 * @param pageId the page identifier 317 * @return true if the property has actually been removed 318 * @throws RepositoryException if a repository error occurs 319 */ 320 @Callable 321 public boolean removeOdfRootPage(String pageId) throws RepositoryException 322 { 323 boolean hasChanges = false; 324 Page page = _resolver.resolveById(pageId); 325 326 if (page instanceof JCRAmetysObject) 327 { 328 JCRAmetysObject jcrPage = (JCRAmetysObject) page; 329 Property virtualProperty = _getVirtualProperty(jcrPage); 330 331 if (virtualProperty != null) 332 { 333 Value[] oldValues = virtualProperty.getValues(); 334 335 // remove the virtual page property 336 Value[] newValues = Arrays.stream(oldValues) 337 .filter(LambdaUtils.wrapPredicate(ODFRootClientSideElement::isVirtualPageFactoryValue).negate()) 338 .toArray(Value[]::new); 339 340 _setVirtualProperty(jcrPage, newValues); 341 342 // Remove the level properties. 343 ModifiableCompositeMetadata pageMeta = jcrPage.getMetadataHolder(); 344 345 if (pageMeta.hasMetadata(OdfPageHandler.LEVEL1_METADATA_NAME)) 346 { 347 pageMeta.removeMetadata(OdfPageHandler.LEVEL1_METADATA_NAME); 348 } 349 if (pageMeta.hasMetadata(OdfPageHandler.LEVEL2_METADATA_NAME)) 350 { 351 pageMeta.removeMetadata(OdfPageHandler.LEVEL2_METADATA_NAME); 352 } 353 if (pageMeta.hasMetadata(OdfPageHandler.CATALOG_METADATA_NAME)) 354 { 355 pageMeta.removeMetadata(OdfPageHandler.CATALOG_METADATA_NAME); 356 } 357 358 if (jcrPage.needsSave()) 359 { 360 jcrPage.saveChanges(); 361 hasChanges = true; 362 } 363 } 364 } 365 366 if (hasChanges) 367 { 368 // Odf root deleted observer 369 Map<String, Object> eventParams = new HashMap<>(); 370 eventParams.put(ObservationConstants.ARGS_PAGE, page); 371 _observationManager.notify(new Event(OdfWebObservationConstants.ODF_ROOT_DELETED, _currentUserProvider.getUser(), eventParams)); 372 373 _notifyOdfRootPageChange(page); 374 } 375 376 return hasChanges; 377 } 378 379 private void _notifyOdfRootPageChange(Page page) 380 { 381 // clear root cache 382 _odfPageHandler.clearRootCache(); 383 384 // Page changed observer 385 Map<String, Object> eventParams = new HashMap<>(); 386 eventParams.put(ObservationConstants.ARGS_PAGE, page); 387 _observationManager.notify(new Event(ObservationConstants.EVENT_PAGE_CHANGED, _currentUserProvider.getUser(), eventParams)); 388 } 389 390 private Property _getVirtualProperty(JCRAmetysObject jcrPage) throws RepositoryException 391 { 392 Node node = jcrPage.getNode(); 393 394 if (node.hasProperty(AmetysObjectResolver.VIRTUAL_PROPERTY)) 395 { 396 return node.getProperty(AmetysObjectResolver.VIRTUAL_PROPERTY); 397 } 398 399 return null; 400 } 401 402 private void _setVirtualProperty(JCRAmetysObject jcrPage, Value[] values) throws RepositoryException 403 { 404 jcrPage.getNode().setProperty(AmetysObjectResolver.VIRTUAL_PROPERTY, values); 405 } 406 407 /** 408 * Tests if a JCR Value represents the virtual page property 409 * @param value the value to test 410 * @return true if it is the case 411 * @throws RepositoryException if a repository error occurs 412 */ 413 private static boolean isVirtualPageFactoryValue(Value value) throws RepositoryException 414 { 415 return StringUtils.equals(value.getString(), FirstLevelPageFactory.class.getName()); 416 } 417}