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