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