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