001/* 002 * Copyright 2015 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; 017 018import java.time.LocalDate; 019import java.time.ZonedDateTime; 020import java.util.ArrayList; 021import java.util.Collection; 022import java.util.Collections; 023import java.util.Date; 024import java.util.HashMap; 025import java.util.HashSet; 026import java.util.Iterator; 027import java.util.List; 028import java.util.Locale; 029import java.util.Map; 030import java.util.Set; 031 032import javax.jcr.Node; 033import javax.jcr.Repository; 034import javax.jcr.RepositoryException; 035import javax.jcr.Session; 036 037import org.apache.avalon.framework.component.Component; 038import org.apache.avalon.framework.logger.AbstractLogEnabled; 039import org.apache.avalon.framework.service.ServiceException; 040import org.apache.avalon.framework.service.ServiceManager; 041import org.apache.avalon.framework.service.Serviceable; 042import org.apache.commons.lang.StringUtils; 043 044import org.ametys.cms.FilterNameHelper; 045import org.ametys.cms.contenttype.ContentType; 046import org.ametys.cms.contenttype.ContentTypeExtensionPoint; 047import org.ametys.cms.languages.Language; 048import org.ametys.cms.repository.Content; 049import org.ametys.cms.repository.ContentDAO.TagMode; 050import org.ametys.cms.repository.ModifiableContent; 051import org.ametys.cms.repository.TaggableAmetysObject; 052import org.ametys.cms.repository.WorkflowAwareContent; 053import org.ametys.cms.tag.CMSTag; 054import org.ametys.cms.tag.Tag; 055import org.ametys.cms.tag.TagProviderExtensionPoint; 056import org.ametys.core.observation.Event; 057import org.ametys.core.observation.ObservationManager; 058import org.ametys.core.right.RightManager; 059import org.ametys.core.right.RightManager.RightResult; 060import org.ametys.core.ui.Callable; 061import org.ametys.core.user.CurrentUserProvider; 062import org.ametys.core.user.UserIdentity; 063import org.ametys.plugins.explorer.ExplorerNode; 064import org.ametys.plugins.explorer.resources.ModifiableResourceCollection; 065import org.ametys.plugins.explorer.resources.Resource; 066import org.ametys.plugins.repository.AmetysObject; 067import org.ametys.plugins.repository.AmetysObjectIterable; 068import org.ametys.plugins.repository.AmetysObjectIterator; 069import org.ametys.plugins.repository.AmetysObjectResolver; 070import org.ametys.plugins.repository.AmetysRepositoryException; 071import org.ametys.plugins.repository.CopiableAmetysObject; 072import org.ametys.plugins.repository.ModifiableAmetysObject; 073import org.ametys.plugins.repository.ModifiableTraversableAmetysObject; 074import org.ametys.plugins.repository.RemovableAmetysObject; 075import org.ametys.plugins.repository.TraversableAmetysObject; 076import org.ametys.plugins.repository.UnknownAmetysObjectException; 077import org.ametys.plugins.repository.jcr.JCRAmetysObject; 078import org.ametys.plugins.repository.jcr.SimpleAmetysObject; 079import org.ametys.plugins.repository.lock.LockHelper; 080import org.ametys.plugins.repository.lock.LockableAmetysObject; 081import org.ametys.plugins.repository.version.VersionableAmetysObject; 082import org.ametys.plugins.workflow.support.WorkflowProvider; 083import org.ametys.plugins.workflow.support.WorkflowProvider.AmetysObjectWorkflow; 084import org.ametys.runtime.i18n.I18nizableText; 085import org.ametys.runtime.model.type.ElementType; 086import org.ametys.runtime.model.type.ModelItemTypeConstants; 087import org.ametys.web.ObservationConstants; 088import org.ametys.web.WebConstants; 089import org.ametys.web.alias.Alias.TargetType; 090import org.ametys.web.alias.AliasHelper; 091import org.ametys.web.alias.DefaultAlias; 092import org.ametys.web.languages.WebLanguagesManager; 093import org.ametys.web.repository.content.SharedContent; 094import org.ametys.web.repository.content.WebContent; 095import org.ametys.web.repository.content.WebContentDAO; 096import org.ametys.web.repository.content.shared.SharedContentManager; 097import org.ametys.web.repository.page.Page.LinkType; 098import org.ametys.web.repository.page.Page.PageType; 099import org.ametys.web.repository.page.ZoneItem.ZoneType; 100import org.ametys.web.repository.page.jcr.DefaultPage; 101import org.ametys.web.repository.site.Site; 102import org.ametys.web.repository.sitemap.Sitemap; 103import org.ametys.web.rights.PageRightAssignmentContext; 104import org.ametys.web.service.Service; 105import org.ametys.web.service.ServiceExtensionPoint; 106import org.ametys.web.skin.Skin; 107import org.ametys.web.skin.SkinTemplate; 108import org.ametys.web.skin.SkinTemplateZone; 109import org.ametys.web.skin.SkinsManager; 110import org.ametys.web.skin.TemplatesAssignmentHandler; 111import org.ametys.web.synchronization.SynchronizeComponent; 112 113/** 114 * DAO for manipulating pages 115 * 116 */ 117public class PageDAO extends AbstractLogEnabled implements Serviceable, Component 118{ 119 /** Constant for untouched binary metadata. */ 120 public static final String __SERVICE_PARAM_UNTOUCHED_BINARY = "untouched"; 121 122 /** Avalon Role */ 123 public static final String ROLE = PageDAO.class.getName(); 124 125 private AmetysObjectResolver _resolver; 126 private ObservationManager _observationManager; 127 private CurrentUserProvider _currentUserProvider; 128 private SkinsManager _skinsManager; 129 private TemplatesAssignmentHandler _templatesHandler; 130 private ServicesAssignmentHandler _serviceHandler; 131 private ContentTypesAssignmentHandler _cTypeHandler; 132 private ContentTypeExtensionPoint _contentTypeExtensionPoint; 133 private ServiceExtensionPoint _serviceExtensionPoint; 134 private WorkflowProvider _workflowProvider; 135 private SharedContentManager _sharedContentManager; 136 private TagProviderExtensionPoint _tagProvider; 137 private WebLanguagesManager _webLanguagesManager; 138 private WebContentDAO _contentDAO; 139 private CopySiteComponent _copySiteComponent; 140 private RightManager _rightManager; 141 private SynchronizeComponent _synchronizeComponent; 142 private Repository _repository; 143 private PageDataTypeExtensionPoint _pageDataTypeExtensionPoint; 144 145 @Override 146 public void service(ServiceManager smanager) throws ServiceException 147 { 148 _resolver = (AmetysObjectResolver) smanager.lookup(AmetysObjectResolver.ROLE); 149 _observationManager = (ObservationManager) smanager.lookup(ObservationManager.ROLE); 150 _currentUserProvider = (CurrentUserProvider) smanager.lookup(CurrentUserProvider.ROLE); 151 _skinsManager = (SkinsManager) smanager.lookup(SkinsManager.ROLE); 152 _templatesHandler = (TemplatesAssignmentHandler) smanager.lookup(TemplatesAssignmentHandler.ROLE); 153 _cTypeHandler = (ContentTypesAssignmentHandler) smanager.lookup(ContentTypesAssignmentHandler.ROLE); 154 _serviceHandler = (ServicesAssignmentHandler) smanager.lookup(ServicesAssignmentHandler.ROLE); 155 _cTypeHandler = (ContentTypesAssignmentHandler) smanager.lookup(ContentTypesAssignmentHandler.ROLE); 156 _contentTypeExtensionPoint = (ContentTypeExtensionPoint) smanager.lookup(ContentTypeExtensionPoint.ROLE); 157 _serviceExtensionPoint = (ServiceExtensionPoint) smanager.lookup(ServiceExtensionPoint.ROLE); 158 _workflowProvider = (WorkflowProvider) smanager.lookup(WorkflowProvider.ROLE); 159 _sharedContentManager = (SharedContentManager) smanager.lookup(SharedContentManager.ROLE); 160 _tagProvider = (TagProviderExtensionPoint) smanager.lookup(TagProviderExtensionPoint.ROLE); 161 _webLanguagesManager = (WebLanguagesManager) smanager.lookup(WebLanguagesManager.ROLE); 162 _contentDAO = (WebContentDAO) smanager.lookup(WebContentDAO.ROLE); 163 _copySiteComponent = (CopySiteComponent) smanager.lookup(CopySiteComponent.ROLE); 164 _rightManager = (RightManager) smanager.lookup(RightManager.ROLE); 165 _synchronizeComponent = (SynchronizeComponent) smanager.lookup(SynchronizeComponent.ROLE); 166 _repository = (Repository) smanager.lookup(Repository.class.getName()); 167 _pageDataTypeExtensionPoint = (PageDataTypeExtensionPoint) smanager.lookup(PageDataTypeExtensionPoint.ROLE); 168 } 169 170 /** 171 * Get the properties of given pages 172 * @param pageIds the id of pages 173 * @return the properties of pages in a result map 174 */ 175 @Callable 176 public Map<String, Object> getPagesInfos (List<String> pageIds) 177 { 178 Map<String, Object> result = new HashMap<>(); 179 180 List<Map<String, Object>> pages = new ArrayList<>(); 181 List<String> pagesNotFound = new ArrayList<>(); 182 183 for (String pageId : pageIds) 184 { 185 try 186 { 187 Page page = _resolver.resolveById(pageId); 188 pages.add(getPageInfos(page)); 189 } 190 catch (UnknownAmetysObjectException e) 191 { 192 pagesNotFound.add(pageId); 193 } 194 } 195 196 result.put("pages", pages); 197 result.put("pagesNotFound", pagesNotFound); 198 199 return result; 200 } 201 202 /** 203 * Get the page's properties 204 * @param pageId the page ID 205 * @return the properties 206 */ 207 @Callable 208 public Map<String, Object> getPageInfos (String pageId) 209 { 210 Page page = _resolver.resolveById(pageId); 211 return getPageInfos(page); 212 } 213 214 /** 215 * Get the page's properties 216 * @param page the page 217 * @return the properties 218 */ 219 public Map<String, Object> getPageInfos (Page page) 220 { 221 Map<String, Object> infos = new HashMap<>(); 222 223 infos.put("id", page.getId()); 224 infos.put("name", page.getName()); 225 infos.put("parentId", page.getParent().getId()); 226 infos.put("title", page.getTitle()); 227 infos.put("longTitle", page.getLongTitle()); 228 infos.put("path", page.getPathInSitemap()); 229 infos.put("siteName", page.getSiteName()); 230 infos.put("type", page.getType()); 231 infos.put("lang", page.getSitemapName()); 232 infos.put("isModifiable", page instanceof ModifiablePage); 233 infos.put("isMoveable", page instanceof MoveablePage); 234 infos.put("isTaggable", page instanceof TaggableAmetysObject); 235 infos.put("isVisible", page.isVisible()); 236 infos.put("isParentInvisible", _isParentInvisible(page)); 237 // Publication information 238 infos.put("publication", _publication2Json(page)); 239 infos.put("isPreviewable", _isPreviewable(page)); 240 241 String skinId = page.getSite().getSkinId(); 242 Skin skin = _skinsManager.getSkin(skinId); 243 infos.put("isPageValid", _synchronizeComponent.isPageValid(page, skin)); 244 245 Session liveSession = null; 246 try 247 { 248 liveSession = _repository.login(WebConstants.LIVE_WORKSPACE); 249 infos.put("isLiveHierarchyValid", _synchronizeComponent.isHierarchyValid(page, liveSession)); 250 } 251 catch (RepositoryException e) 252 { 253 throw new RuntimeException("Unable to check live workspace", e); 254 } 255 finally 256 { 257 if (liveSession != null) 258 { 259 liveSession.logout(); 260 } 261 } 262 263 PageType type = page.getType(); 264 switch (type) 265 { 266 case CONTAINER: 267 infos.put("template", page.getTemplate()); 268 269 List<Map<String, Object>> zones = new ArrayList<>(); 270 for (Zone zone : page.getZones()) 271 { 272 zones.add(_zone2json(zone)); 273 } 274 infos.put("zones", zones); 275 break; 276 277 case LINK: 278 infos.put("url", page.getURL()); 279 280 LinkType urlType = page.getURLType(); 281 infos.put("urlType", urlType.toString()); 282 283 switch (urlType) 284 { 285 case PAGE: 286 try 287 { 288 Page targetPage = _resolver.resolveById(page.getURL()); 289 infos.put("urlTitle", targetPage.getTitle()); 290 } 291 catch (UnknownAmetysObjectException e) 292 { 293 getLogger().error("Page '" + page.getId() + "' redirects to an unexisting page '" + page.getURL() + "'"); 294 } 295 break; 296 297 default: 298 break; 299 } 300 break; 301 default: 302 AmetysObjectIterator< ? extends Page> iterator = page.getChildrenPages().iterator(); 303 if (iterator.hasNext()) 304 { 305 Page firstSubPage = iterator.next(); 306 infos.put("url", firstSubPage.getId()); 307 infos.put("urlTitle", firstSubPage.getTitle()); 308 infos.put("urlType", LinkType.PAGE.toString()); 309 break; 310 311 } 312 break; 313 } 314 315 316 infos.put("rights", getUserRights(page)); 317 318 return infos; 319 } 320 321 /** 322 * Get the page's properties 323 * @param pageId the id of page 324 * @return the properties 325 */ 326 @Callable 327 public Map<String, Object> getPageProperties (String pageId) 328 { 329 Page page = _resolver.resolveById(pageId); 330 331 Map<String, Object> infos = new HashMap<>(); 332 333 infos.put("id", page.getId()); 334 infos.put("name", page.getName()); 335 infos.put("parentId", page.getParent().getId()); 336 infos.put("title", page.getTitle()); 337 infos.put("longTitle", page.getLongTitle()); 338 infos.put("path", page.getPathInSitemap()); 339 infos.put("siteName", page.getSiteName()); 340 infos.put("siteTitle", page.getSite().getTitle()); 341 infos.put("type", page.getType()); 342 343 String lang = page.getSitemapName(); 344 infos.put("lang", lang); 345 Language language = _webLanguagesManager.getAvailableLanguages().get(lang); 346 if (language != null) 347 { 348 infos.put("langIcon", language.getSmallIcon()); 349 infos.put("langLabel", language.getLabel()); 350 } 351 352 PageType type = page.getType(); 353 switch (type) 354 { 355 case CONTAINER: 356 infos.put("template", page.getTemplate()); 357 break; 358 359 case LINK: 360 infos.put("url", page.getURL()); 361 LinkType urlType = page.getURLType(); 362 infos.put("urlType", urlType.toString()); 363 break; 364 default: 365 break; 366 } 367 368 Map<String, Object> contextParameters = new HashMap<>(); 369 contextParameters.put("siteName", page.getSiteName()); 370 371 // Tags 372 List<I18nizableText> tags = new ArrayList<>(); 373 for (String tagName : page.getTags()) 374 { 375 Tag tag = _tagProvider.getTag(tagName, contextParameters); 376 tags.add(tag.getTitle()); 377 } 378 infos.put("tags", tags); 379 380 // Incoming references 381 List<Map<String, Object>> incomingContents = new ArrayList<>(); 382 AmetysObjectIterable<Content> contents = _getIncomingContentReferences (page.getId()); 383 for (Content content : contents) 384 { 385 incomingContents.add(_content2Json(content, new Locale(page.getSitemapName()))); 386 } 387 infos.put("inComingContents", incomingContents); 388 389 List<Map<String, Object>> incomingPages = new ArrayList<>(); 390 AmetysObjectIterable<Page> pages = _getIncomingPageReferences (page.getId()); 391 for (Page pageRef : pages) 392 { 393 incomingPages.add(_page2Json(pageRef)); 394 } 395 infos.put("inComingPages", incomingPages); 396 397 // Publication information 398 infos.put("publication", _publication2Json(page)); 399 400 return infos; 401 } 402 403 /** 404 * Check current user right on given page or sitemap 405 * @param id The id of the page or sitemap 406 * @param rightId The if of right to check 407 * @return true if user has right 408 */ 409 @Callable 410 public boolean hasRight(String id, String rightId) 411 { 412 UserIdentity user = _currentUserProvider.getUser(); 413 PagesContainer page = _resolver.resolveById(id); 414 415 return _rightManager.hasRight(user, rightId, page) == RightResult.RIGHT_ALLOW; 416 } 417 418 /** 419 * Create a new page 420 * @param parentId The parent id. Can not be null. 421 * @param title The page's title. Can not be null. 422 * @param longTitle The page's long title. Can be null or blank. 423 * @return The result map with id of created page 424 */ 425 @Callable 426 public Map<String, Object> createPage (String parentId, String title, String longTitle) 427 { 428 Map<String, Object> result = new HashMap<>(); 429 430 PagesContainer parent = _resolver.resolveById(parentId); 431 432 assert parent instanceof ModifiableTraversableAmetysObject; 433 434 Site site = parent.getSite(); 435 String originalPageName = ""; 436 try 437 { 438 originalPageName = FilterNameHelper.filterName(title); 439 } 440 catch (IllegalArgumentException e) 441 { 442 result.put("invalid-name", title); 443 return result; 444 } 445 446 String pageName = originalPageName; 447 int index = 2; 448 while (parent.hasChild(pageName)) 449 { 450 pageName = originalPageName + "-" + (index++); 451 } 452 453 ModifiablePage page = ((ModifiableTraversableAmetysObject) parent).createChild(pageName, "ametys:defaultPage"); 454 455 // Check rights 456 if (!_canCreate(parent)) 457 { 458 throw new IllegalStateException("You do not have the rights to create a page under '/" + parent.getSitemapName() + "/" + parent.getPathInSitemap() + "'"); 459 } 460 461 page.setTitle(title); 462 page.setType(PageType.NODE); 463 page.setSiteName(site.getName()); 464 page.setSitemapName(page.getSitemap().getName()); 465 466 if (!StringUtils.isBlank(longTitle)) 467 { 468 page.setLongTitle(longTitle); 469 } 470 471 site.saveChanges(); 472 473 Map<String, Object> eventParams = new HashMap<>(); 474 eventParams.put(ObservationConstants.ARGS_PAGE, page); 475 _observationManager.notify(new Event(ObservationConstants.EVENT_PAGE_ADDED, _currentUserProvider.getUser(), eventParams)); 476 477 result.put("id", page.getId()); 478 result.put("parentId", parent.getId()); 479 result.put("lang", page.getSitemapName()); 480 result.put("title", page.getTitle()); 481 482 return result; 483 } 484 485 /** 486 * Copy a page 487 * @param id The id of page to copy 488 * @param target The id of parent target page 489 * @param keepReferences true to keep references 490 * @return the result map 491 * @throws RepositoryException if an error occurred during copy 492 */ 493 @Callable 494 public Map<String, String> copyPage (String id, String target, boolean keepReferences) throws RepositoryException 495 { 496 Map<String, String> result = new HashMap<>(); 497 498 Page page = _resolver.resolveById(id); 499 500 if (!(page instanceof CopiableAmetysObject)) 501 { 502 throw new IllegalArgumentException("The page '" + page.getId() + "' is not a copiable ametys object, it can not be copied"); 503 } 504 505 if (!(page instanceof JCRAmetysObject)) 506 { 507 throw new IllegalArgumentException("The page '" + page.getId() + "' is not a JCR ametys object, it can not be copied"); 508 } 509 510 PagesContainer parent = _resolver.resolveById(target); 511 512 // Check rights 513 if (!_canCreate(parent)) 514 { 515 throw new IllegalStateException("You do not have the rights to create a page under '/" + parent.getSitemapName() + "/" + parent.getPathInSitemap() + "'"); 516 } 517 518 if (parent instanceof ModifiableTraversableAmetysObject) 519 { 520 Page cPage = null; 521 if (!keepReferences) 522 { 523 // Restrict the copy to the page and its current children to avoid infinitive loop 524 List<String> pagesToCopy = new ArrayList<>(); 525 pagesToCopy.add(page.getId()); 526 pagesToCopy.addAll(_getChildrenPageIds(page)); 527 528 // Copy and duplicate contents 529 cPage = (Page) ((CopiableAmetysObject) page).copyTo((ModifiableTraversableAmetysObject) parent, null, pagesToCopy); 530 _copySiteComponent.updateReferencesAfterCopy(page, cPage); 531 532 // Creates the first version on all copied contents 533 _updateContentsAfterCopy (cPage); 534 } 535 else 536 { 537 538 // Copy without duplicating contents (keep references) 539 String pageName = page.getName(); 540 int index = 2; 541 while (parent.hasChild(pageName)) 542 { 543 pageName = page.getName() + "-" + (index++); 544 } 545 546 String pagePath = ((SimpleAmetysObject) parent).getNode().getPath() + "/" + pageName; 547 Node node = ((JCRAmetysObject) page).getNode(); 548 node.getSession().getWorkspace().copy(node.getPath(), pagePath); 549 550 cPage = parent.getChild(pageName); 551 } 552 553 ((ModifiableTraversableAmetysObject) parent).saveChanges(); 554 result.put("id", cPage.getId()); 555 556 Map<String, Object> eventParams = new HashMap<>(); 557 eventParams.put(ObservationConstants.ARGS_PAGE, cPage); 558 _observationManager.notify(new Event(ObservationConstants.EVENT_PAGE_ADDED, _currentUserProvider.getUser(), eventParams)); 559 } 560 561 return result; 562 } 563 564 /** 565 * Copy a page under another page 566 * @param targetId the page to copy in 567 * @param sourceId the page to copy 568 * @param keepReferences true to keep references for contents, or false to duplicate contents 569 * @return The id of created page is a result map. 570 * @throws RepositoryException if an error occurs 571 */ 572 @Callable 573 @Deprecated 574 public Map<String, String> pastePage(String targetId, String sourceId, boolean keepReferences) throws RepositoryException 575 { 576 return copyPage(sourceId, targetId, keepReferences); 577 } 578 579 /** 580 * Move a page 581 * @param id The page id 582 * @param parentId The id of parent destination 583 * @param index The position in parent child nodes where page will be inserted. -1 means as the last child. 584 * @return the result map 585 */ 586 @Callable 587 public Map<String, Object> movePage (String id, String parentId, int index) 588 { 589 Map<String, Object> result = new HashMap<>(); 590 591 String oldPathInSitemap = null; 592 String newPathInSitemap = null; 593 594 Page page = _resolver.resolveById(id); 595 PagesContainer srcParent = page.getParent(); 596 oldPathInSitemap = page.getPathInSitemap(); 597 Sitemap sitemap = page.getSitemap(); 598 599 // check rights of deletion on old parent page 600 if (!srcParent.getId().equals(parentId) && !_canDelete(page)) 601 { 602 throw new IllegalStateException("You do not have the rights to delete the page '/" + page.getSitemapName() + "/" + oldPathInSitemap + "'"); 603 } 604 605 if (!(page instanceof MoveablePage)) 606 { 607 throw new IllegalArgumentException("The page '/" + page.getSitemapName() + "/" + oldPathInSitemap + "' is not a moveable page"); 608 } 609 610 if (srcParent.getId().equals(parentId) && index != -1) 611 { 612 try 613 { 614 Page brother = srcParent.getChildPageAt(index); 615 ((MoveablePage) page).orderBefore(brother); 616 } 617 catch (UnknownAmetysObjectException e) 618 { 619 // Move the last child position 620 ((MoveablePage) page).orderBefore(null); 621 } 622 623 // Path is not modified 624 newPathInSitemap = oldPathInSitemap; 625 } 626 else 627 { 628 PagesContainer newParentPage = _resolver.resolveById(parentId); 629 630 // check right on creation on new parent page 631 if (!_canCreate(newParentPage)) 632 { 633 throw new IllegalStateException("You do not have the rights to create a page under '/" + newParentPage.getSitemapName() + "/" + newParentPage.getPathInSitemap() + "'"); 634 } 635 636 ((MoveablePage) page).moveTo(newParentPage, true); 637 if (index != -1) 638 { 639 Page brother = newParentPage.getChildPageAt(index); 640 641 ((MoveablePage) page).orderBefore(brother); 642 } 643 644 // Path is modified 645 newPathInSitemap = page.getPathInSitemap(); 646 } 647 648 if (sitemap.needsSave()) 649 { 650 sitemap.saveChanges(); 651 } 652 653 // Notify observers that the page has been moved 654 Map<String, Object> eventParams = new HashMap<>(); 655 eventParams.put(ObservationConstants.ARGS_SITEMAP, sitemap); 656 eventParams.put(ObservationConstants.ARGS_PAGE, page); 657 eventParams.put("page.old.path", oldPathInSitemap); 658 eventParams.put("page.old.parent", srcParent); 659 eventParams.put(ObservationConstants.ARGS_PAGE_PATH, newPathInSitemap); 660 _observationManager.notify(new Event(ObservationConstants.EVENT_PAGE_MOVED, _currentUserProvider.getUser(), eventParams)); 661 662 result.put("id", page.getId()); 663 result.put("parentId", page.getParent().getId()); 664 665 return result; 666 } 667 668 private boolean _canCreate(PagesContainer parentPage) 669 { 670 UserIdentity user = _currentUserProvider.getUser(); 671 if (_rightManager.hasRight(user, "Web_Rights_Page_Create", parentPage) == RightResult.RIGHT_ALLOW) 672 { 673 return true; 674 } 675 676 if (getLogger().isInfoEnabled()) 677 { 678 getLogger().info("The user '" + user + "' tried to create page under '/" + parentPage.getSitemapName() + "/" + parentPage.getPathInSitemap() + "' without sufficient rights"); 679 } 680 681 return false; 682 } 683 684 private boolean _canDelete(Page page) 685 { 686 UserIdentity user = _currentUserProvider.getUser(); 687 PagesContainer parent = page.getParent(); 688 if (_rightManager.hasRight(user, "Web_Rights_Page_Delete", parent) == RightResult.RIGHT_ALLOW) 689 { 690 return true; 691 } 692 693 if (getLogger().isInfoEnabled()) 694 { 695 getLogger().info("The user '" + user + "' tried to move page '/" + page.getSitemapName() + "/" + page.getPathInSitemap() + "' without sufficient rights"); 696 } 697 698 return false; 699 } 700 701 /** 702 * Set pages as redirection 703 * @param pageIds the id of pages to modify 704 * @param url the url of redirection 705 * @param urlType the type of redirection 706 * @return the id of pages which succeeded or failed. 707 */ 708 @Callable 709 public Map<String, Object> setLink (List<String> pageIds, String url, String urlType) 710 { 711 Map<String, Object> result = new HashMap<>(); 712 713 if (StringUtils.isEmpty(url)) 714 { 715 throw new IllegalArgumentException("Can not set page as a redirection with an empty url"); 716 } 717 718 List<String> successes = new ArrayList<>(); 719 List<Map<String, Object>> failures = new ArrayList<>(); 720 721 for (String pageId : pageIds) 722 { 723 try 724 { 725 Page page = _resolver.resolveById(pageId); 726 if (!(page instanceof ModifiablePage)) 727 { 728 throw new IllegalArgumentException("Can not set page as a redirection on a non-modifiable page " + pageId); 729 } 730 731 ModifiablePage mPage = (ModifiablePage) page; 732 733 if (page.getType().equals(PageType.CONTAINER)) 734 { 735 // Remove zones 736 for (ModifiableZone zone : mPage.getZones()) 737 { 738 zone.remove(); 739 } 740 } 741 742 if (pageId.equals(url)) 743 { 744 throw new IllegalArgumentException("A page can not redirect to itself"); 745 } 746 747 mPage.setType(PageType.LINK); 748 mPage.setURL(LinkType.valueOf(urlType), url); 749 mPage.getSitemap().saveChanges(); 750 751 successes.add(pageId); 752 753 Map<String, Object> eventParams = new HashMap<>(); 754 eventParams.put(ObservationConstants.ARGS_PAGE, page); 755 _observationManager.notify(new Event(ObservationConstants.EVENT_PAGE_CHANGED, _currentUserProvider.getUser(), eventParams)); 756 } 757 catch (Exception e) 758 { 759 getLogger().error("Cannot set the page '" + pageId + "' as link [" + url + ", " + urlType.toString() + "]", e); 760 761 Map<String, Object> failure = new HashMap<>(); 762 failure.put("id", pageId); 763 failure.put("error", e.toString()); 764 failures.add(failure); 765 } 766 } 767 768 result.put("success", successes); 769 result.put("failure", failures); 770 771 return result; 772 } 773 774 /** 775 * Get available template for specified page 776 * @param pageId The page's id 777 * @return the list of available template 778 */ 779 @Callable 780 public List<Map<String, Object>> getAvailableTemplates (String pageId) 781 { 782 List<Map<String, Object>> templates = new ArrayList<>(); 783 784 Page page = _resolver.resolveById(pageId); 785 786 Set<String> availableTemplateIds = _templatesHandler.getAvailablesTemplates(page); 787 for (String templateName : availableTemplateIds) 788 { 789 String skinId = page.getSite().getSkinId(); 790 Skin skin = _skinsManager.getSkin(skinId); 791 792 SkinTemplate template = skin.getTemplate(templateName); 793 794 Map<String, Object> template2json = new HashMap<>(); 795 template2json.put("id", template.getId()); 796 template2json.put("label", template.getLabel()); 797 template2json.put("description", template.getDescription()); 798 template2json.put("iconSmall", template.getSmallImage()); 799 template2json.put("iconMedium", template.getMediumImage()); 800 template2json.put("iconLarge", template.getLargeImage()); 801 template2json.put("zone", template.getDefaultZoneId()); 802 803 templates.add(template2json); 804 } 805 806 return templates; 807 } 808 809 /** 810 * Get available content types for specified page 811 * @param pageId The page's id 812 * @param zoneName the name of the zone 813 * @return the list of available content types 814 */ 815 @Callable 816 public List<Map<String, Object>> getAvailableContentTypes (String pageId, String zoneName) 817 { 818 List<Map<String, Object>> contenttypes = new ArrayList<>(); 819 820 Page page = _resolver.resolveById(pageId); 821 822 Set<String> contentTypeIds = _cTypeHandler.getAvailableContentTypes(page, zoneName); 823 for (String contentTypeId : contentTypeIds) 824 { 825 ContentType cType = _contentTypeExtensionPoint.getExtension(contentTypeId); 826 827 if (cType != null && _hasRight(cType, page)) 828 { 829 Map<String, Object> ctype2json = new HashMap<>(); 830 ctype2json.put("id", cType.getId()); 831 ctype2json.put("label", cType.getLabel()); 832 ctype2json.put("description", cType.getDescription()); 833 ctype2json.put("iconGlyph", cType.getIconGlyph()); 834 ctype2json.put("iconDecorator", cType.getIconDecorator()); 835 ctype2json.put("iconSmall", cType.getSmallIcon()); 836 ctype2json.put("iconMedium", cType.getMediumIcon()); 837 ctype2json.put("iconLarge", cType.getLargeIcon()); 838 ctype2json.put("defaultTitle", cType.getDefaultTitle()); 839 840 contenttypes.add(ctype2json); 841 } 842 } 843 844 return contenttypes; 845 } 846 847 /** 848 * Get available content types for a page being created 849 * @param pageId The page's id. Can be null of the page is not yet created 850 * @param zoneName the name of the zone 851 * @param parentId The id of parent page 852 * @param pageTitle The title of page to create 853 * @param template The template of page to create 854 * @return the list of available services 855 */ 856 @Callable 857 public List<Map<String, Object>> getAvailableContentTypesForCreation(String pageId, String zoneName, String parentId, String pageTitle, String template) 858 { 859 if (StringUtils.isNotEmpty(pageId)) 860 { 861 // Get available services for a page 862 return getAvailableContentTypes(pageId, zoneName); 863 } 864 else if (StringUtils.isNotEmpty(parentId)) 865 { 866 // Get available services for a not yet existing page 867 PagesContainer parent = _resolver.resolveById(parentId); 868 869 // Create page temporarily 870 Page page = _createPage(parent, pageTitle, template); 871 872 List<Map<String, Object>> availableServices = getAvailableContentTypes(page.getId(), zoneName); 873 874 // Cancel page creation 875 page.getSitemap().revertChanges(); 876 877 return availableServices; 878 } 879 880 return Collections.EMPTY_LIST; 881 } 882 883 private boolean _hasRight(ContentType contentType, Page page) 884 { 885 String right = contentType.getRight(); 886 887 if (right == null) 888 { 889 return true; 890 } 891 else 892 { 893 UserIdentity user = _currentUserProvider.getUser(); 894 return _rightManager.hasRight(user, right, page) == RightResult.RIGHT_ALLOW; 895 } 896 } 897 898 /** 899 * Get available services for specified page 900 * @param pageId The page's id 901 * @param zoneName the name of the zone 902 * @return the list of available services 903 */ 904 @Callable 905 public List<Map<String, Object>> getAvailableServices (String pageId, String zoneName) 906 { 907 List<Map<String, Object>> services = new ArrayList<>(); 908 909 Page page = _resolver.resolveById(pageId); 910 911 Set<String> serviceIds = _serviceHandler.getAvailableServices(page, zoneName); 912 for (String serviceId : serviceIds) 913 { 914 Service service = _serviceExtensionPoint.getExtension(serviceId); 915 if (service != null && _hasRight(service, page)) 916 { 917 Map<String, Object> serviceMap = new HashMap<>(); 918 serviceMap.put("id", service.getId()); 919 serviceMap.put("label", service.getLabel()); 920 serviceMap.put("description", service.getDescription()); 921 serviceMap.put("iconGlyph", service.getIconGlyph()); 922 serviceMap.put("iconDecorator", service.getIconDecorator()); 923 serviceMap.put("iconSmall", service.getSmallIcon()); 924 serviceMap.put("iconMedium", service.getMediumIcon()); 925 serviceMap.put("iconLarge", service.getLargeIcon()); 926 serviceMap.put("parametersAction", service.getParametersScript().getScriptClassname()); 927 928 services.add(serviceMap); 929 } 930 } 931 932 return services; 933 } 934 935 /** 936 * Get available services for a page being created 937 * @param pageId The page's id. Can be null of the page is not yet created 938 * @param zoneName the name of the zone 939 * @param parentId The id of parent page 940 * @param pageTitle The title of page to create 941 * @param template The template of page to create 942 * @return the list of available services 943 */ 944 @Callable 945 public List<Map<String, Object>> getAvailableServicesForCreation(String pageId, String zoneName, String parentId, String pageTitle, String template) 946 { 947 if (StringUtils.isNotEmpty(pageId)) 948 { 949 // Get available services for a page 950 return getAvailableServices(pageId, zoneName); 951 } 952 else if (StringUtils.isNotEmpty(parentId)) 953 { 954 // Get available services for a not yet existing page 955 PagesContainer parent = _resolver.resolveById(parentId); 956 957 // Create page temporarily 958 Page page = _createPage(parent, pageTitle, template); 959 960 List<Map<String, Object>> availableServices = getAvailableServices(page.getId(), zoneName); 961 962 // Cancel page creation 963 page.getSitemap().revertChanges(); 964 965 return availableServices; 966 } 967 968 return Collections.EMPTY_LIST; 969 } 970 971 private Page _createPage (PagesContainer parent, String pageTitle, String template) 972 { 973 Site site = parent.getSite(); 974 String originalPageName = FilterNameHelper.filterName(pageTitle); 975 976 String pageName = originalPageName; 977 int index = 2; 978 while (parent.hasChild(pageName)) 979 { 980 pageName = originalPageName + "-" + index++; 981 } 982 983 ModifiablePage page = ((ModifiableTraversableAmetysObject) parent).createChild(pageName, "ametys:defaultPage"); 984 985 page.setTitle(pageTitle); 986 page.setType(PageType.NODE); 987 page.setSiteName(site.getName()); 988 page.setSitemapName(page.getSitemap().getName()); 989 990 if (template != null) 991 { 992 String skinId = page.getSite().getSkinId(); 993 SkinTemplate tpl = _skinsManager.getSkin(skinId).getTemplate(template); 994 if (tpl == null) 995 { 996 throw new IllegalStateException("Template '" + template + "' does not exist on skin '" + skinId + "'"); 997 } 998 999 // Set temporary the template to get available services 1000 page.setType(PageType.CONTAINER); 1001 page.setTemplate(template); 1002 } 1003 1004 return page; 1005 } 1006 1007 private boolean _hasRight(Service service, Page page) 1008 { 1009 String right = service.getRight(); 1010 1011 if (right == null) 1012 { 1013 return true; 1014 } 1015 else 1016 { 1017 UserIdentity user = _currentUserProvider.getUser(); 1018 return _rightManager.hasRight(user, right, page) == RightResult.RIGHT_ALLOW; 1019 } 1020 } 1021 1022 /** 1023 * Get available template for specified pages 1024 * @param pageIds The id of pages 1025 * @return the list of available template 1026 */ 1027 @Callable 1028 public List<Map<String, Object>> getAvailableTemplates (List<String> pageIds) 1029 { 1030 List<String> templateIds = new ArrayList<>(); 1031 1032 List<Map<String, Object>> templates = new ArrayList<>(); 1033 1034 for (String pageId : pageIds) 1035 { 1036 List<Map<String, Object>> pageTemplates = getAvailableTemplates(pageId); 1037 1038 for (Map<String, Object> template : pageTemplates) 1039 { 1040 String templateName = (String) template.get("id"); 1041 1042 if (!templateIds.contains(templateName)) 1043 { 1044 templateIds.add(templateName); 1045 templates.add(template); 1046 } 1047 } 1048 } 1049 1050 return templates; 1051 } 1052 1053 /** 1054 * Get available content types for a page being created 1055 * @param pageId The page's id. Can be null of the page is not yet created 1056 * @param parentId The id of parent page 1057 * @param pageTitle The title of page to create 1058 * @return the list of available services 1059 */ 1060 @Callable 1061 public List<Map<String, Object>> getAvailableTemplatesForCreation (String pageId, String parentId, String pageTitle) 1062 { 1063 if (StringUtils.isNotEmpty(pageId)) 1064 { 1065 // Get available template for a page 1066 return getAvailableTemplates(pageId); 1067 } 1068 else if (StringUtils.isNotEmpty(parentId)) 1069 { 1070 // Get available template for a not yet existing page 1071 PagesContainer parent = _resolver.resolveById(parentId); 1072 1073 // Create page temporarily 1074 Page page = _createPage(parent, pageTitle, null); 1075 1076 List<Map<String, Object>> availableTemplates = getAvailableTemplates(page.getId()); 1077 1078 // Cancel page creation 1079 page.getSitemap().revertChanges(); 1080 1081 return availableTemplates; 1082 } 1083 1084 return Collections.EMPTY_LIST; 1085 } 1086 1087 /** 1088 * Get service info 1089 * @param pageId Optional, the page id of the service. To get some basic info about the page. 1090 * @param serviceId The id of the service 1091 * @return a Map containing some info about the service (label, url..) 1092 */ 1093 @Callable 1094 public Map<String, Object> getServiceInfo(String pageId, String serviceId) 1095 { 1096 Map<String, Object> info = new HashMap<>(); 1097 1098 if (StringUtils.isNotEmpty(pageId)) 1099 { 1100 Page page = _resolver.resolveById(pageId); 1101 info.put("page-id", page.getId()); 1102 info.put("page-title", page.getTitle()); 1103 } 1104 1105 Service service = _serviceExtensionPoint.getExtension(serviceId); 1106 info.put("id", service.getId()); 1107 info.put("label", service.getLabel()); 1108 info.put("url", service.getURL()); 1109 info.put("smallIcon", service.getSmallIcon()); 1110 info.put("iconGlyph", service.getIconGlyph()); 1111 info.put("iconDecorator", service.getIconDecorator()); 1112 return info; 1113 } 1114 1115 /** 1116 * Rename a page 1117 * @param pageId The id of page to rename 1118 * @param title The page's title 1119 * @param longTitle The page's long title. 1120 * @param updatePath true to update page's path 1121 * @param createAlias true to create a alias 1122 * @return the result map 1123 */ 1124 @Callable (right = "Web_Rights_Page_Rename", rightContext = PageRightAssignmentContext.ID, paramIndex = 0) 1125 public Map<String, Object> renamePage (String pageId, String title, String longTitle, boolean updatePath, boolean createAlias) 1126 { 1127 Map<String, Object> result = new HashMap<>(); 1128 1129 Page page = _resolver.resolveById(pageId); 1130 1131 if (!(page instanceof ModifiablePage)) 1132 { 1133 throw new IllegalArgumentException("Can not rename a non-modifiable page '/" + page.getSitemapName() + "/" + page.getPathInSitemap() + "'"); 1134 } 1135 1136 ModifiablePage mPage = (ModifiablePage) page; 1137 mPage.setTitle(title); 1138 mPage.setLongTitle(longTitle); 1139 1140 if (updatePath) 1141 { 1142 String oldPathInSitemap = page.getPathInSitemap(); 1143 String oldPath = "/" + page.getSitemapName() + "/" + page.getPathInSitemap() + ".html"; 1144 String oldPathForChild = "/" + page.getSitemapName() + "/" + page.getPathInSitemap() + "/**.html"; 1145 1146 String pageName = ""; 1147 try 1148 { 1149 pageName = FilterNameHelper.filterName(title); 1150 } 1151 catch (IllegalArgumentException e) 1152 { 1153 result.put("invalid-name", title); 1154 return result; 1155 } 1156 1157 if (!page.getName().equals(pageName)) 1158 { 1159 int index = 1; 1160 String initialPageName = pageName; 1161 PagesContainer parent = page.getParent(); 1162 while (parent.hasChild(pageName)) 1163 { 1164 pageName = initialPageName + "-" + (index++); 1165 } 1166 1167 mPage.rename(pageName); 1168 1169 if (createAlias) 1170 { 1171 ModifiableTraversableAmetysObject rootNode = AliasHelper.getRootNode(page.getSite()); 1172 1173 DefaultAlias alias = rootNode.createChild(AliasHelper.getAliasNextUniqueName(rootNode), "ametys:alias"); 1174 alias.setUrl(oldPath); 1175 alias.setTarget(page.getId()); 1176 alias.setType(TargetType.PAGE); 1177 alias.setCreationDate(new Date()); 1178 1179 // Alias for child pages 1180 alias = rootNode.createChild(AliasHelper.getAliasNextUniqueName(rootNode), "ametys:alias"); 1181 alias.setUrl(oldPathForChild); 1182 alias.setTarget("/" + page.getSitemapName() + "/" + page.getPathInSitemap() + "/{1}.html"); 1183 alias.setType(TargetType.URL); 1184 alias.setCreationDate(new Date()); 1185 1186 rootNode.saveChanges(); 1187 } 1188 1189 // Notify observers that the page has been renamed 1190 Map<String, Object> eventParams = new HashMap<>(); 1191 eventParams.put(ObservationConstants.ARGS_PAGE, page); 1192 eventParams.put("path.old.path", oldPathInSitemap); 1193 eventParams.put(ObservationConstants.ARGS_PAGE_PATH, page.getPathInSitemap()); 1194 _observationManager.notify(new Event(ObservationConstants.EVENT_PAGE_RENAMED, _currentUserProvider.getUser(), eventParams)); 1195 1196 } 1197 else 1198 { 1199 // Notify observers that the page's title has been modified 1200 Map<String, Object> eventParams = new HashMap<>(); 1201 eventParams.put(ObservationConstants.ARGS_PAGE, page); 1202 _observationManager.notify(new Event(ObservationConstants.EVENT_PAGE_UPDATED, _currentUserProvider.getUser(), eventParams)); 1203 } 1204 1205 } 1206 else 1207 { 1208 // Notify observers that the page's title has been modified 1209 Map<String, Object> eventParams = new HashMap<>(); 1210 eventParams.put(ObservationConstants.ARGS_PAGE, page); 1211 _observationManager.notify(new Event(ObservationConstants.EVENT_PAGE_UPDATED, _currentUserProvider.getUser(), eventParams)); 1212 } 1213 1214 Sitemap sitemap = page.getSitemap(); 1215 if (sitemap.needsSave()) 1216 { 1217 sitemap.saveChanges(); 1218 } 1219 1220 result.put("id", page.getId()); 1221 result.put("path", page.getPath()); 1222 result.put("title", page.getTitle()); 1223 1224 return result; 1225 } 1226 1227 /** 1228 * Delete a page and its sub-pages. 1229 * The contents that belong only to the deleted page will be deleted if 'deleteBelongingContents' is set to true. 1230 * The newly created contents are deleted whatever the value if 'deleteBelongingContents'. 1231 * @param pageId the id of page to delete 1232 * @param deleteBelongingContents true to delete the contents that belong to the page and its sub-pages only 1233 * @return The id of deleted pages 1234 */ 1235 @Callable 1236 public Map<String, Object> deletePage(String pageId, boolean deleteBelongingContents) 1237 { 1238 ModifiablePage page = _resolver.resolveById(pageId); 1239 1240 // Check rights on parent 1241 if (!_canDelete(page)) 1242 { 1243 throw new IllegalStateException("You do not have the rights to delete the page '/" + page.getSitemapName() + "/" + page.getPathInSitemap() + "'"); 1244 } 1245 1246 return deletePage(page, deleteBelongingContents); 1247 } 1248 1249 /** 1250 * Delete a page and its sub-pages. 1251 * The contents that belong only to the deleted page will be deleted if 'deleteBelongingContents' is set to true. 1252 * The newly created contents are deleted whatever the value if 'deleteBelongingContents'. 1253 * @param page the page to delete 1254 * @param deleteBelongingContents true to delete the contents that belong to the page and its sub-pages only 1255 * @return The id of deleted pages 1256 */ 1257 public Map<String, Object> deletePage(ModifiablePage page, boolean deleteBelongingContents) 1258 { 1259 Map<String, Object> result = new HashMap<>(); 1260 1261 List<String> contentToDelete = getDeleteablePageContentIds(page.getId(), !deleteBelongingContents); 1262 1263 Sitemap sitemap = page.getSitemap(); 1264 PagesContainer parent = page.getParent(); 1265 String pagePathInSitemap = page.getPathInSitemap(); 1266 1267 List<String> childPagesIds = _getChildrenPageIds(page); 1268 1269 Map<String, Object> eventParams = new HashMap<>(); 1270 eventParams.put(ObservationConstants.ARGS_PAGE_ID, page.getId()); 1271 eventParams.put(ObservationConstants.ARGS_PAGE_PARENT, parent); 1272 eventParams.put(ObservationConstants.ARGS_PAGE_PATH, pagePathInSitemap); 1273 eventParams.put(ObservationConstants.ARGS_SITEMAP, sitemap); 1274 _observationManager.notify(new Event(ObservationConstants.EVENT_PAGE_DELETING, _currentUserProvider.getUser(), eventParams)); 1275 1276 // FIXME API test if this is not modifiable 1277 page.getParent(); 1278 page.remove(); 1279 ((ModifiableAmetysObject) parent).saveChanges(); 1280 1281 _observationManager.notify(new Event(ObservationConstants.EVENT_PAGE_DELETED, _currentUserProvider.getUser(), eventParams)); 1282 1283 result.put("id", page.getId()); 1284 result.put("childPages", childPagesIds); 1285 1286 result.putAll(_contentDAO.deleteContents(contentToDelete, true)); 1287 return result; 1288 } 1289 1290 /** 1291 * Set pages as blank page 1292 * @param pageIds the id of pages to modify 1293 * @return the id of pages which succeeded or failed. 1294 */ 1295 @Callable 1296 public Map<String, Object> setBlank (List<String> pageIds) 1297 { 1298 Map<String, Object> result = new HashMap<>(); 1299 1300 List<String> successes = new ArrayList<>(); 1301 List<Map<String, Object>> failures = new ArrayList<>(); 1302 1303 for (String pageId : pageIds) 1304 { 1305 try 1306 { 1307 Page page = _resolver.resolveById(pageId); 1308 if (!(page instanceof ModifiablePage)) 1309 { 1310 throw new IllegalArgumentException("Can not set page as blank a non-modifiable page " + pageId); 1311 } 1312 1313 ModifiablePage mPage = (ModifiablePage) page; 1314 1315 if (page.getType().equals(PageType.CONTAINER)) 1316 { 1317 // Remove zones 1318 for (ModifiableZone zone : mPage.getZones()) 1319 { 1320 zone.remove(); 1321 } 1322 } 1323 1324 mPage.setType(PageType.NODE); 1325 mPage.getSitemap().saveChanges(); 1326 1327 successes.add(pageId); 1328 1329 Map<String, Object> eventParams = new HashMap<>(); 1330 eventParams.put(ObservationConstants.ARGS_PAGE, page); 1331 _observationManager.notify(new Event(ObservationConstants.EVENT_PAGE_CHANGED, _currentUserProvider.getUser(), eventParams)); 1332 } 1333 catch (Exception e) 1334 { 1335 getLogger().error("Cannot set the page '" + pageId + "' as blank page", e); 1336 1337 Map<String, Object> failure = new HashMap<>(); 1338 failure.put("id", pageId); 1339 failure.put("error", e.toString()); 1340 failures.add(failure); 1341 } 1342 } 1343 1344 result.put("success", successes); 1345 result.put("failure", failures); 1346 1347 return result; 1348 } 1349 1350 /** 1351 * Set a template to pages 1352 * @param pageIds the id of pages to update 1353 * @param templateName The template name 1354 * @return the id of pages which succeeded 1355 */ 1356 @Callable 1357 public Map<String, Object> setTemplate (List<String> pageIds, String templateName) 1358 { 1359 return setTemplate(pageIds, templateName, true); 1360 } 1361 1362 /** 1363 * Set a template to pages 1364 * @param pageIds the id of pages to update 1365 * @param templateName The template name 1366 * @param checkAvailableTemplate true if you want to check available template 1367 * @return the id of pages which succeeded 1368 */ 1369 public Map<String, Object> setTemplate (List<String> pageIds, String templateName, boolean checkAvailableTemplate) 1370 { 1371 Map<String, Object> result = new HashMap<>(); 1372 1373 List<String> successes = new ArrayList<>(); 1374 1375 String defaultZoneName = null; 1376 1377 for (String pageId : pageIds) 1378 { 1379 Page page = _resolver.resolveById(pageId); 1380 if (!(page instanceof ModifiablePage)) 1381 { 1382 throw new IllegalArgumentException("Can not set template a non-modifiable page " + pageId); 1383 } 1384 1385 ModifiablePage mPage = (ModifiablePage) page; 1386 1387 if (defaultZoneName == null) 1388 { 1389 String skinId = page.getSite().getSkinId(); 1390 SkinTemplate tpl = _skinsManager.getSkin(skinId).getTemplate(templateName); 1391 if (tpl == null) 1392 { 1393 throw new IllegalStateException("Template '" + templateName + "' does not exist on skin '" + skinId + "'"); 1394 } 1395 1396 defaultZoneName = tpl.getDefaultZoneId(); 1397 } 1398 1399 if (checkAvailableTemplate && !_templatesHandler.getAvailablesTemplates(mPage).contains(templateName)) 1400 { 1401 throw new IllegalStateException("Template '" + templateName + "' is not available for page '" + pageId + "'"); 1402 } 1403 1404 if (page.getType().equals(PageType.CONTAINER)) 1405 { 1406 _removeOldZones (mPage, templateName); 1407 } 1408 1409 mPage.setTemplate(templateName); 1410 mPage.setType(PageType.CONTAINER); 1411 mPage.getSitemap().saveChanges(); 1412 1413 successes.add(pageId); 1414 1415 Map<String, Object> eventParams = new HashMap<>(); 1416 eventParams.put(ObservationConstants.ARGS_PAGE, page); 1417 _observationManager.notify(new Event(ObservationConstants.EVENT_PAGE_CHANGED, _currentUserProvider.getUser(), eventParams)); 1418 } 1419 1420 if (defaultZoneName != null) 1421 { 1422 result.put("zonename", defaultZoneName); 1423 } 1424 1425 result.put("success", successes); 1426 1427 return result; 1428 } 1429 1430 /** 1431 * Get the script class name to execute to add or update this service 1432 * @param serviceId the service id 1433 * @return the script class name. Can be empty 1434 */ 1435 @Callable 1436 public String getServiceParametersAction (String serviceId) 1437 { 1438 try 1439 { 1440 Service service = _serviceExtensionPoint.getExtension(serviceId); 1441 return service.getParametersScript().getScriptClassname(); 1442 } 1443 catch (IllegalArgumentException e) 1444 { 1445 throw new IllegalArgumentException("Service with id '" + serviceId + "' does not exist", e); 1446 } 1447 } 1448 1449 private void _removeOldZones (ModifiablePage page, String templateName) 1450 { 1451 String skinId = page.getSite().getSkinId(); 1452 1453 SkinTemplate oldTemplate = _skinsManager.getSkin(skinId).getTemplate(templateName); 1454 1455 Map<String, SkinTemplateZone> templateZones = oldTemplate.getZones(); 1456 1457 for (ModifiableZone zone : page.getZones()) 1458 { 1459 if (!templateZones.containsKey(zone.getName())) 1460 { 1461 zone.remove(); 1462 } 1463 } 1464 } 1465 1466 /** 1467 * Get the tags from the pages 1468 * @param pageIds The ids of the pages 1469 * @return the tags of the pages 1470 */ 1471 @Callable 1472 public Set<String> getTags (List<String> pageIds) 1473 { 1474 Set<String> tags = new HashSet<>(); 1475 1476 for (String pageId : pageIds) 1477 { 1478 Page page = _resolver.resolveById(pageId); 1479 tags.addAll(page.getTags()); 1480 } 1481 1482 return tags; 1483 } 1484 1485 /** 1486 * Tag a list of pages 1487 * @param pageIds The ids of pages to tag 1488 * @param tagNames The tags 1489 * @param mode The mode for updating tags: 'REPLACE' to replace tags, 'INSERT' to add tags or 'REMOVE' to remove tags. 1490 * @param contextualParameters Contextual parameters. Must contain the site name 1491 * @return the result 1492 */ 1493 @Callable 1494 public Map<String, Object> tag (List<String> pageIds, List<String> tagNames, String mode, Map<String, Object> contextualParameters) 1495 { 1496 Map<String, Object> result = new HashMap<>(); 1497 1498 result.put("nomodifiable-pages", new ArrayList<Map<String, Object>>()); 1499 result.put("invalid-tags", new ArrayList<String>()); 1500 result.put("allright-pages", new ArrayList<Map<String, Object>>()); 1501 1502 for (String pageId : pageIds) 1503 { 1504 Page page = _resolver.resolveById(pageId); 1505 1506 Map<String, Object> page2json = new HashMap<>(); 1507 page2json.put("id", page.getId()); 1508 page2json.put("title", page.getTitle()); 1509 1510 if (page instanceof ModifiablePage) 1511 { 1512 ModifiablePage mPage = (ModifiablePage) page; 1513 1514 TagMode tagMode = TagMode.valueOf(mode); 1515 1516 Set<String> oldTags = mPage.getTags(); 1517 if (TagMode.REPLACE.equals(tagMode)) 1518 { 1519 // First delete old tags 1520 for (String tagName : oldTags) 1521 { 1522 mPage.untag(tagName); 1523 } 1524 } 1525 1526 1527 // Then set new tags 1528 for (String tagName : tagNames) 1529 { 1530 if (_isTagValid(page, tagName)) 1531 { 1532 if (TagMode.REMOVE.equals(tagMode)) 1533 { 1534 mPage.untag(tagName); 1535 } 1536 else if (TagMode.REPLACE.equals(tagMode) || !oldTags.contains(tagName)) 1537 { 1538 mPage.tag(tagName); 1539 } 1540 } 1541 else 1542 { 1543 @SuppressWarnings("unchecked") 1544 List<String> invalidTags = (List<String>) result.get("invalid-tags"); 1545 invalidTags.add(tagName); 1546 } 1547 } 1548 1549 mPage.saveChanges(); 1550 1551 page2json.put("tags", page.getTags()); 1552 @SuppressWarnings("unchecked") 1553 List<Map<String, Object>> allRightPages = (List<Map<String, Object>>) result.get("allright-pages"); 1554 allRightPages.add(page2json); 1555 1556 if (!oldTags.equals(page.getTags())) 1557 { 1558 // Notify observers that the content has been tagged 1559 Map<String, Object> eventParams = new HashMap<>(); 1560 eventParams.put(ObservationConstants.ARGS_PAGE, page); 1561 eventParams.put("page.tags", page.getTags()); 1562 eventParams.put("page.old.tags", page); 1563 _observationManager.notify(new Event(ObservationConstants.EVENT_PAGE_UPDATED, _currentUserProvider.getUser(), eventParams)); 1564 } 1565 } 1566 else 1567 { 1568 @SuppressWarnings("unchecked") 1569 List<Map<String, Object>> nomodifiablePages = (List<Map<String, Object>>) result.get("nomodifiable-pages"); 1570 nomodifiablePages.add(page2json); 1571 } 1572 } 1573 1574 return result; 1575 } 1576 1577 /** 1578 * Tag a list of contents with the given tags 1579 * @param pageIds The ids of pages to tag 1580 * @param contentIds The ids of contents to tag 1581 * @param tagNames The tags 1582 * @param contextualParameters The contextual parameters 1583 * @return the result map 1584 */ 1585 @Callable 1586 public Map<String, Object> tag (List<String> pageIds, List<String> contentIds, List<String> tagNames, Map<String, Object> contextualParameters) 1587 { 1588 return tag(pageIds, contentIds, tagNames, TagMode.REPLACE.toString(), contextualParameters); 1589 } 1590 1591 /** 1592 * Tag a list of contents and/org pages 1593 * @param pageIds The ids of pages to tag 1594 * @param contentIds The ids of contents to tag 1595 * @param tagNames The tags 1596 * @param mode The mode for updating tags: 'REPLACE' to replace tags, 'INSERT' to add tags or 'REMOVE' to remove tags. 1597 * @param contextualParameters The contextual parameters 1598 * @return the result 1599 */ 1600 @Callable 1601 public Map<String, Object> tag (List<String> pageIds, List<String> contentIds, List<String> tagNames, String mode, Map<String, Object> contextualParameters) 1602 { 1603 // Tag pages 1604 Map<String, Object> result = tag(pageIds, tagNames, mode, contextualParameters); 1605 1606 // Tag contents 1607 result.putAll(_contentDAO.tag(contentIds, tagNames, mode, contextualParameters)); 1608 1609 // Invalid tags are ignored 1610 result.remove("invalid-tags"); 1611 return result; 1612 } 1613 1614 /** 1615 * Test if a tag is valid for a specific page 1616 * @param page The page 1617 * @param tagName The tag name 1618 * @return True if the tag is valid 1619 */ 1620 public boolean _isTagValid (Page page, String tagName) 1621 { 1622 Map<String, Object> params = new HashMap<>(); 1623 params.put("siteName", page.getSiteName()); 1624 CMSTag tag = _tagProvider.getTag(tagName, params); 1625 1626 return tag.getTarget().getName().equals("PAGE"); 1627 } 1628 1629 /** 1630 * Set the visible of pages 1631 * @param pageIds The id of pages 1632 * @param visible <code>true</code> to set pages as visible, <code>false</code> otherwise 1633 * @return The result map 1634 */ 1635 @Callable 1636 public Map<String, Object> setVisibility (List<String> pageIds, boolean visible) 1637 { 1638 Map<String, Object> result = new HashMap<>(); 1639 1640 result.put("nomodifiable-pages", new ArrayList<Map<String, Object>>()); 1641 result.put("allright-pages", new ArrayList<Map<String, Object>>()); 1642 1643 for (String id : pageIds) 1644 { 1645 Page page = _resolver.resolveById(id); 1646 1647 Map<String, Object> page2json = new HashMap<>(); 1648 page2json.put("id", page.getId()); 1649 page2json.put("title", page.getTitle()); 1650 1651 if (page instanceof ModifiablePage) 1652 { 1653 ModifiablePage mPage = (ModifiablePage) page; 1654 mPage.setVisible(visible); 1655 mPage.saveChanges(); 1656 1657 Map<String, Object> eventParams = new HashMap<>(); 1658 eventParams.put(ObservationConstants.ARGS_PAGE, page); 1659 eventParams.put(ObservationConstants.ARGS_PAGE_ID, page.getId()); 1660 _observationManager.notify(new Event(ObservationConstants.EVENT_PAGE_UPDATED, _currentUserProvider.getUser(), eventParams)); 1661 1662 @SuppressWarnings("unchecked") 1663 List<Map<String, Object>> allRightPages = (List<Map<String, Object>>) result.get("allright-pages"); 1664 allRightPages.add(page2json); 1665 } 1666 else 1667 { 1668 @SuppressWarnings("unchecked") 1669 List<Map<String, Object>> nomodifiablePages = (List<Map<String, Object>>) result.get("nomodifiable-pages"); 1670 nomodifiablePages.add(page2json); 1671 } 1672 } 1673 1674 return result; 1675 } 1676 1677 /** 1678 * Get the contents of a {@link Page} and its subpages which can be deleted. 1679 * A content is deleteable if user has right, the content is not locked and not referenced by other pages. 1680 * @param id The id of page 1681 * @return The list of deletable contents 1682 */ 1683 @Callable 1684 public Map<String, Object> getDeleteablePageContents (String id) 1685 { 1686 Map<String, Object> results = new HashMap<>(); 1687 1688 results.put("deleteable-contents", new ArrayList<Map<String, Object>>()); 1689 results.put("referenced-contents", new ArrayList<Map<String, Object>>()); 1690 results.put("unauthorized-contents", new ArrayList<Map<String, Object>>()); 1691 results.put("locked-contents", new ArrayList<Map<String, Object>>()); 1692 1693 Page page = _resolver.resolveById(id); 1694 List<Content> contents = getPageContents(page, true); 1695 1696 for (Content content : contents) 1697 { 1698 Map<String, Object> contentParams = new HashMap<>(); 1699 contentParams.put("id", content.getId()); 1700 contentParams.put("title", content.getTitle(new Locale(page.getSitemapName()))); 1701 contentParams.put("name", content.getName()); 1702 1703 if (_isReferenced(content)) 1704 { 1705 // Content is referenced by at least another page 1706 @SuppressWarnings("unchecked") 1707 List<Map<String, Object>> referencedContents = (List<Map<String, Object>>) results.get("referenced-contents"); 1708 referencedContents.add(contentParams); 1709 } 1710 else if (!_contentDAO.canDelete(content)) 1711 { 1712 @SuppressWarnings("unchecked") 1713 List<Map<String, Object>> unauthorizedContents = (List<Map<String, Object>>) results.get("unauthorized-contents"); 1714 unauthorizedContents.add(contentParams); 1715 } 1716 else if (_isLocked(content)) 1717 { 1718 // If the content is locked by other 1719 @SuppressWarnings("unchecked") 1720 List<Map<String, Object>> lockedContents = (List<Map<String, Object>>) results.get("locked-contents"); 1721 lockedContents.add(contentParams); 1722 } 1723 else 1724 { 1725 Map<String, Object> content2json = new HashMap<>(); 1726 content2json.put("id", content.getId()); 1727 content2json.put("name", content.getName()); 1728 content2json.put("title", content.getTitle(new Locale(page.getSitemapName()))); 1729 content2json.put("isNew", _isNew(content)); 1730 content2json.put("isShared", content instanceof SharedContent); 1731 content2json.put("hasShared", _sharedContentManager.hasSharedContents(content)); 1732 1733 @SuppressWarnings("unchecked") 1734 List<Map<String, Object>> allrightContents = (List<Map<String, Object>>) results.get("deleteable-contents"); 1735 allrightContents.add(content2json); 1736 } 1737 } 1738 1739 return results; 1740 } 1741 1742 /** 1743 * Get the contents that belong to the {@link Page} and its sub-pages and that can be deleted. 1744 * A content is deleteable if user has right, the content is not locked and it's not referenced by other pages. 1745 * If 'onlyNewlyCreatedContents' is set to 'true', only newly created contents will be returned 1746 * @param pageId The id of page 1747 * @param onlyNewlyCreatedContents true to return only the newly created contents 1748 * @return The ids of deleteable contents 1749 */ 1750 public List<String> getDeleteablePageContentIds (String pageId, boolean onlyNewlyCreatedContents) 1751 { 1752 List<String> contentsId = new ArrayList<>(); 1753 1754 Page page = _resolver.resolveById(pageId); 1755 1756 List<Content> contents = getPageContents(page, true); 1757 1758 for (Content content : contents) 1759 { 1760 if (_contentDAO.canDelete(content) && !_isLocked(content) && !_isReferenced(content)) 1761 { 1762 if (!onlyNewlyCreatedContents || _isNew(content)) 1763 { 1764 contentsId.add(content.getId()); 1765 } 1766 } 1767 } 1768 1769 return contentsId; 1770 } 1771 1772 /** 1773 * Get the unreferenced contents of a {@link Page} or a {@link ZoneItem} 1774 * @param id The id of page or zone item 1775 * @return The list of unreferenced contents 1776 */ 1777 @Callable 1778 public List<Map<String, Object>> getUnreferencedContents (String id) 1779 { 1780 List<Map<String, Object>> unreferencedContents = new ArrayList<>(); 1781 1782 Page page = _resolver.resolveById(id); 1783 List<Content> contents = getPageContents(page, true); 1784 1785 for (Content content : contents) 1786 { 1787 if (!_isReferenced(content)) 1788 { 1789 Map<String, Object> content2json = new HashMap<>(); 1790 content2json.put("id", content.getId()); 1791 content2json.put("name", content.getName()); 1792 content2json.put("title", content.getTitle(new Locale(page.getSitemapName()))); 1793 content2json.put("isNew", _isNew(content)); 1794 content2json.put("isShared", content instanceof SharedContent); 1795 content2json.put("hasShared", _sharedContentManager.hasSharedContents(content)); 1796 1797 unreferencedContents.add(content2json); 1798 } 1799 } 1800 1801 return unreferencedContents; 1802 } 1803 1804 /** 1805 * Returns the page's attachments root node 1806 * @param id the page's id 1807 * @return The attachments' root node informations 1808 */ 1809 @Callable 1810 public Map<String, Object> getAttachmentsRootNode (String id) 1811 { 1812 Map<String, Object> result = new HashMap<>(); 1813 1814 Page page = _resolver.resolveById(id); 1815 1816 result.put("title", page.getTitle()); 1817 result.put("contentId", page.getId()); 1818 1819 TraversableAmetysObject attachments = page.getRootAttachments(); 1820 1821 if (attachments != null) 1822 { 1823 result.put("id", attachments.getId()); 1824 if (attachments instanceof ModifiableAmetysObject) 1825 { 1826 result.put("isModifiable", true); 1827 } 1828 if (attachments instanceof ModifiableResourceCollection) 1829 { 1830 result.put("canCreateChild", true); 1831 } 1832 1833 boolean hasChildNodes = false; 1834 boolean hasResources = false; 1835 1836 for (AmetysObject child : attachments.getChildren()) 1837 { 1838 if (child instanceof Resource) 1839 { 1840 hasResources = true; 1841 } 1842 else if (child instanceof ExplorerNode) 1843 { 1844 hasChildNodes = true; 1845 } 1846 } 1847 1848 if (hasChildNodes) 1849 { 1850 result.put("hasChildNodes", true); 1851 } 1852 1853 if (hasResources) 1854 { 1855 result.put("hasResources", true); 1856 } 1857 1858 return result; 1859 } 1860 1861 throw new IllegalArgumentException("Page with id '" + id + "' does not support attachments."); 1862 } 1863 /** 1864 * Returns the page's parents ids 1865 * @param id the page's id 1866 * @return The attachments' root node informations 1867 */ 1868 @Callable 1869 public Map<String, Object> getPageParents (String id) 1870 { 1871 Map<String, Object> result = new HashMap<>(); 1872 List<Map<String, Object>> pages = new ArrayList<>(); 1873 Page page = _resolver.resolveById(id); 1874 pages.add(_page2Json(page)); 1875 while (page.getParent() != null && page.getParent() instanceof Page) 1876 { 1877 page = page.getParent(); 1878 pages.add(_page2Json(page)); 1879 } 1880 result.put("parents", pages); 1881 return result; 1882 } 1883 1884 /** 1885 * Get the contents of a page and its child pages 1886 * @param page The page 1887 * @return The list of contents 1888 */ 1889 public List<Content> getPageContents (Page page) 1890 { 1891 return getPageContents(page, false); 1892 } 1893 1894 /** 1895 * Get the contents of a page and its child pages 1896 * @param page The page 1897 * @param ignoreContentsOfNonRemovablePage true to ignore contents of non-removable pages (virtual pages) 1898 * @return The list of contents 1899 */ 1900 public List<Content> getPageContents (Page page, boolean ignoreContentsOfNonRemovablePage) 1901 { 1902 List<Content> contents = new ArrayList<>(); 1903 1904 if ((!ignoreContentsOfNonRemovablePage || page instanceof RemovableAmetysObject) && page.getType() == Page.PageType.CONTAINER) 1905 { 1906 for (Zone zone : page.getZones()) 1907 { 1908 for (ZoneItem zoneItem : zone.getZoneItems()) 1909 { 1910 if (zoneItem.getType() == ZoneItem.ZoneType.CONTENT) 1911 { 1912 contents.add(zoneItem.getContent()); 1913 } 1914 } 1915 } 1916 } 1917 1918 AmetysObjectIterable< ? extends Page> childrenPages = page.getChildrenPages(); 1919 for (Page childPage : childrenPages) 1920 { 1921 contents.addAll(getPageContents(childPage, ignoreContentsOfNonRemovablePage)); 1922 } 1923 1924 return contents; 1925 } 1926 1927 /** 1928 * Get the user rights on page container (page or sitemap) 1929 * @param pagesCt The pages container 1930 * @return The user's rights 1931 */ 1932 protected Set<String> getUserRights (PagesContainer pagesCt) 1933 { 1934 UserIdentity user = _currentUserProvider.getUser(); 1935 1936 Set<String> userRights = _rightManager.getUserRights(user, pagesCt); 1937 1938 // Do some specific stuff here, because the right 'Web_Rights_Page_Delete' is a right to delete child pages and not the page itself. 1939 // So the right should be checked on parent context. 1940 if (pagesCt instanceof Page) 1941 { 1942 PagesContainer parent = pagesCt.getParent(); 1943 boolean canDelete = _rightManager.hasRight(user, "Web_Rights_Page_Delete", parent) == RightResult.RIGHT_ALLOW; 1944 if (!canDelete) 1945 { 1946 // No right on parent page, so remove the right if exists. 1947 userRights.remove("Web_Rights_Page_Delete"); 1948 } 1949 } 1950 else 1951 { 1952 // There is no right of deletion on the sitemap 1953 userRights.remove("Web_Rights_Page_Delete"); 1954 } 1955 1956 return userRights; 1957 } 1958 1959 private boolean _isReferenced (Content content) 1960 { 1961 return content instanceof WebContent && ((WebContent) content).getReferencingPages().size() > 1; 1962 } 1963 1964 private boolean _isLocked (Content content) 1965 { 1966 if (content instanceof LockableAmetysObject) 1967 { 1968 LockableAmetysObject lockableContent = (LockableAmetysObject) content; 1969 if (lockableContent.isLocked()) 1970 { 1971 boolean canUnlockAll = _rightManager.hasRight(_currentUserProvider.getUser(), "CMS_Rights_UnlockAll", "/cms") == RightResult.RIGHT_ALLOW; 1972 if (!LockHelper.isLockOwner(lockableContent, _currentUserProvider.getUser()) && !canUnlockAll) 1973 { 1974 return true; 1975 } 1976 } 1977 } 1978 1979 return false; 1980 } 1981 1982 private boolean _isNew (Content content) 1983 { 1984 boolean isNew = false; 1985 if (content instanceof WorkflowAwareContent) 1986 { 1987 WorkflowAwareContent waContent = (WorkflowAwareContent) content; 1988 long workflowId = waContent.getWorkflowId(); 1989 1990 AmetysObjectWorkflow workflow = _workflowProvider.getAmetysObjectWorkflow(waContent); 1991 isNew = workflow.getHistorySteps(workflowId).isEmpty(); 1992 } 1993 return isNew; 1994 } 1995 1996 private Map<String, Object> _page2Json (Page page) 1997 { 1998 Map<String, Object> page2json = new HashMap<>(); 1999 page2json.put("id", page.getId()); 2000 page2json.put("title", page.getTitle()); 2001 page2json.put("siteName", page.getSiteName()); 2002 page2json.put("path", page.getPathInSitemap()); 2003 return page2json; 2004 } 2005 2006 private Map<String, Object> _content2Json (Content content, Locale locale) 2007 { 2008 Map<String, Object> content2json = new HashMap<>(); 2009 content2json.put("id", content.getId()); 2010 content2json.put("title", content.getTitle(locale)); 2011 content2json.put("name", content.getName()); 2012 2013 List<Map<String, Object>> pages = new ArrayList<>(); 2014 if (content instanceof WebContent) 2015 { 2016 content2json.put("siteName", ((WebContent) content).getSiteName()); 2017 Collection<Page> refPages = ((WebContent) content).getReferencingPages(); 2018 for (Page refPage : refPages) 2019 { 2020 pages.add(_page2Json(refPage)); 2021 } 2022 content2json.put("pages", pages); 2023 } 2024 2025 return content2json; 2026 } 2027 2028 private Map<String, Object> _publication2Json (Page page) 2029 { 2030 Map<String, Object> pub2json = new HashMap<>(); 2031 @SuppressWarnings("unchecked") 2032 ElementType<ZonedDateTime> dateType = (ElementType<ZonedDateTime>) _pageDataTypeExtensionPoint.getExtension(ModelItemTypeConstants.DATETIME_TYPE_ID); 2033 2034 ZonedDateTime startDate = page.getValue(DefaultPage.METADATA_PUBLICATION_START_DATE); 2035 if (startDate != null) 2036 { 2037 pub2json.put("startDate", dateType.valueToJSONForClient(startDate)); 2038 } 2039 2040 ZonedDateTime endDate = page.getValue(DefaultPage.METADATA_PUBLICATION_END_DATE); 2041 if (endDate != null) 2042 { 2043 pub2json.put("endDate", dateType.valueToJSONForClient(endDate)); 2044 } 2045 2046 return pub2json; 2047 2048 } 2049 2050 private AmetysObjectIterable<Content> _getIncomingContentReferences (String pageId) 2051 { 2052 String xpathQuery = "//element(*, ametys:content)[ametys-internal:consistency/@ametys-internal:link = 'page:" + pageId + "']"; 2053 return _resolver.query(xpathQuery); 2054 } 2055 2056 private AmetysObjectIterable<Page> _getIncomingPageReferences (String pageId) 2057 { 2058 String xpathQuery = "//element(*, ametys:page)[@ametys-internal:type = 'LINK' and @ametys-internal:url= '" + pageId + "']"; 2059 return _resolver.query(xpathQuery); 2060 } 2061 2062 private Map<String, Object> _zone2json (Zone zone) 2063 { 2064 Map<String, Object> jsonObject = new HashMap<>(); 2065 jsonObject.put("name", zone.getName()); 2066 jsonObject.put("isModifiable", zone instanceof ModifiableZone); 2067 2068 List<Map<String, Object>> zoneitems = new ArrayList<>(); 2069 for (ZoneItem zoneItem : zone.getZoneItems()) 2070 { 2071 zoneitems.add(_zoneitem2json(zoneItem)); 2072 } 2073 2074 jsonObject.put("zoneitems", zoneitems); 2075 2076 return jsonObject; 2077 } 2078 2079 private Map<String, Object> _zoneitem2json (ZoneItem zoneItem) 2080 { 2081 Map<String, Object> jsonObject = new HashMap<>(); 2082 jsonObject.put("name", zoneItem.getName()); 2083 jsonObject.put("id", zoneItem.getId()); 2084 jsonObject.put("isModifiable", zoneItem instanceof ModifiableZoneItem); 2085 2086 switch (zoneItem.getType()) 2087 { 2088 case CONTENT: 2089 try 2090 { 2091 jsonObject.put("metadataSetName", zoneItem.getMetadataSetName()); 2092 2093 Content content = zoneItem.getContent(); 2094 jsonObject.put("content", _contentDAO.getContentProperties(content)); 2095 } 2096 catch (AmetysRepositoryException e) 2097 { 2098 getLogger().error("Unable to get content property on zone item", e); 2099 } 2100 break; 2101 2102 case SERVICE: 2103 jsonObject.put("service", zoneItem.getServiceId()); 2104 break; 2105 default: 2106 break; 2107 } 2108 2109 return jsonObject; 2110 } 2111 2112 private void _updateContentsAfterCopy (Page createdPage) throws AmetysRepositoryException 2113 { 2114 for (Zone zone : createdPage.getZones()) 2115 { 2116 for (ZoneItem zoneItem : zone.getZoneItems()) 2117 { 2118 if (zoneItem.getType().equals(ZoneType.CONTENT)) 2119 { 2120 Content content = zoneItem.getContent(); 2121 2122 // Convert content language if necessary 2123 if (content instanceof ModifiableContent && content.getLanguage() != null && content.getLanguage() != createdPage.getSitemapName()) 2124 { 2125 ((ModifiableContent) content).setLanguage(createdPage.getSitemapName()); 2126 ((ModifiableContent) content).saveChanges(); 2127 } 2128 2129 // Create the first version 2130 if (content instanceof VersionableAmetysObject) 2131 { 2132 ((VersionableAmetysObject) content).checkpoint(); 2133 } 2134 } 2135 } 2136 } 2137 2138 // Browse child pages 2139 for (Page childPage : createdPage.getChildrenPages()) 2140 { 2141 _updateContentsAfterCopy (childPage); 2142 } 2143 } 2144 2145 private List<String> _getChildrenPageIds (Page page) 2146 { 2147 List<String> childIds = new ArrayList<>(); 2148 2149 for (Page childPage : page.getChildrenPages()) 2150 { 2151 childIds.add(childPage.getId()); 2152 childIds.addAll(_getChildrenPageIds(childPage)); 2153 } 2154 2155 return childIds; 2156 } 2157 2158 /** 2159 * Check each parent and return true if one of them is invisible 2160 * @param page page to check 2161 * @return true if at least one parent is invisible 2162 */ 2163 private boolean _isParentInvisible (Page page) 2164 { 2165 AmetysObject parent = page.getParent(); 2166 while (parent != null && parent instanceof Page) 2167 { 2168 boolean invisible = !((Page) parent).isVisible(); 2169 if (invisible) 2170 { 2171 return true; 2172 } 2173 parent = parent.getParent(); 2174 } 2175 return false; 2176 } 2177 2178 /* start of a group of methods for _isPreviewable */ 2179 /** 2180 * Determine if this page is previewable 2181 * @param page The page to look at 2182 * @return true if the page can be previewed 2183 */ 2184 private boolean _isPreviewable(Page page) 2185 { 2186 // Check for infinitive loop redirection 2187 ArrayList<String> pagesSequence = new ArrayList<>(); 2188 pagesSequence.add(page.getId()); 2189 if (_isInfiniteRedirection (page, pagesSequence)) 2190 { 2191 getLogger().error("An infinite loop redirection was detected for page '" + page.getPathInSitemap() + "'"); 2192 return false; 2193 } 2194 2195 if (page.getType() == PageType.LINK && LinkType.PAGE.equals(page.getURLType())) 2196 { 2197 return _isPageExist(page.getURL()) && _isPreviewable((Page) _resolver.resolveById(page.getURL())); 2198 } 2199 2200 if (page.getType() != PageType.NODE) 2201 { 2202 return true; 2203 } 2204 else 2205 { 2206 for (Page subPage : page.getChildrenPages()) 2207 { 2208 if (_isPreviewable(subPage)) 2209 { 2210 return true; 2211 } 2212 } 2213 } 2214 return false; 2215 } 2216 2217 private boolean _isPageExist (String id) 2218 { 2219 try 2220 { 2221 _resolver.resolveById(id); 2222 return true; 2223 } 2224 catch (UnknownAmetysObjectException e) 2225 { 2226 return false; 2227 } 2228 } 2229 2230 private boolean _isInfiniteRedirection (Page page, List<String> pagesSequence) 2231 { 2232 Page redirectPage = _getPageRedirection (page); 2233 if (redirectPage == null) 2234 { 2235 return false; 2236 } 2237 2238 if (pagesSequence.contains(redirectPage.getId())) 2239 { 2240 return true; 2241 } 2242 2243 pagesSequence.add(redirectPage.getId()); 2244 return _isInfiniteRedirection (redirectPage, pagesSequence); 2245 } 2246 2247 private Page _getPageRedirection (Page page) 2248 { 2249 if (PageType.LINK.equals(page.getType()) && LinkType.PAGE.equals(page.getURLType())) 2250 { 2251 try 2252 { 2253 String pageId = page.getURL(); 2254 return _resolver.resolveById(pageId); 2255 } 2256 catch (AmetysRepositoryException e) 2257 { 2258 return null; 2259 } 2260 } 2261 else if (PageType.NODE.equals(page.getType())) 2262 { 2263 AmetysObjectIterable<? extends Page> childPages = page.getChildrenPages(); 2264 Iterator<? extends Page> it = childPages.iterator(); 2265 if (it.hasNext()) 2266 { 2267 return it.next(); 2268 } 2269 } 2270 2271 return null; 2272 } 2273 /* end of a group of methods for _isPreviewable */ 2274}