001/* 002 * Copyright 2011 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.plugins.blog; 017 018import java.time.ZonedDateTime; 019import java.util.ArrayList; 020import java.util.Calendar; 021import java.util.Collection; 022import java.util.Collections; 023import java.util.Comparator; 024import java.util.Date; 025import java.util.HashMap; 026import java.util.HashSet; 027import java.util.Iterator; 028import java.util.LinkedHashMap; 029import java.util.List; 030import java.util.Map; 031import java.util.Map.Entry; 032import java.util.Set; 033import java.util.concurrent.TimeUnit; 034 035import org.apache.avalon.framework.activity.Initializable; 036import org.apache.avalon.framework.component.Component; 037import org.apache.avalon.framework.logger.AbstractLogEnabled; 038import org.apache.avalon.framework.service.ServiceException; 039import org.apache.avalon.framework.service.ServiceManager; 040import org.apache.avalon.framework.service.Serviceable; 041 042import org.ametys.cms.ObservationConstants; 043import org.ametys.cms.repository.Content; 044import org.ametys.cms.repository.ContentTypeExpression; 045import org.ametys.cms.repository.LanguageExpression; 046import org.ametys.cms.tag.Tag; 047import org.ametys.cms.tag.TagHelper; 048import org.ametys.cms.tag.TagProviderExtensionPoint; 049import org.ametys.core.cache.AbstractCacheManager; 050import org.ametys.core.cache.Cache; 051import org.ametys.core.observation.Event; 052import org.ametys.core.observation.ObservationManager; 053import org.ametys.core.user.UserIdentity; 054import org.ametys.plugins.blog.posts.PostContentType; 055import org.ametys.plugins.core.impl.cache.AbstractCacheKey; 056import org.ametys.plugins.repository.AmetysObjectIterable; 057import org.ametys.plugins.repository.AmetysObjectResolver; 058import org.ametys.plugins.repository.provider.WorkspaceSelector; 059import org.ametys.plugins.repository.query.QueryHelper; 060import org.ametys.plugins.repository.query.SortCriteria; 061import org.ametys.plugins.repository.query.expression.AndExpression; 062import org.ametys.plugins.repository.query.expression.Expression; 063import org.ametys.plugins.repository.query.expression.Expression.Operator; 064import org.ametys.plugins.repository.query.expression.MetadataExpression; 065import org.ametys.plugins.repository.query.expression.StringExpression; 066import org.ametys.runtime.i18n.I18nizableText; 067import org.ametys.web.WebConstants; 068import org.ametys.web.repository.SiteAwareAmetysObject; 069import org.ametys.web.repository.site.Site; 070import org.ametys.web.repository.site.SiteManager; 071import org.ametys.web.site.SiteConfigurationExtensionPoint; 072 073import com.google.common.collect.Multimap; 074import com.google.common.collect.TreeMultimap; 075 076/** 077 * Blog cache manager. 078 */ 079public class BlogCacheManager extends AbstractLogEnabled implements Component, Serviceable, Initializable 080{ 081 /** The name of the date attribute of post */ 082 public static final String POST_DATE_ATTRIBUTE = "date"; 083 084 /** The name of the tags attribute of post */ 085 public static final String POST_TAGS_ATTRIBUTE = "tags"; 086 087 /** The avalon role. */ 088 public static final String ROLE = BlogCacheManager.class.getName(); 089 090 private static final String __LAST_UPDATE_FUTURE_POST_CACHE = ROLE + "$lastUpdate"; 091 092 private static final String __POST_BY_DATE_CACHE = ROLE + "$postsByDate"; 093 094 private static final String __POST_BY_TAG_CACHE = ROLE + "$postsByTag"; 095 096 private static final String __ALL_POSTS_CACHE = ROLE + "$allPosts"; 097 098 private static final String __FUTURE_POSTS_CACHE = ROLE + "$futurePosts"; 099 100 /** The ametys object resolver. */ 101 protected AmetysObjectResolver _ametysResolver; 102 103 /** Th tag provider */ 104 protected TagProviderExtensionPoint _tagProviderEP; 105 106 /** The Site manager. */ 107 protected SiteManager _siteManager; 108 109 /** The site configuration extension point */ 110 protected SiteConfigurationExtensionPoint _siteConf; 111 112 /** The blog root page handler. */ 113 protected BlogPageHandler _blogRootHandler; 114 115 /** The workspace selector. */ 116 protected WorkspaceSelector _workspaceSelector; 117 118 /** The Ametys resolver */ 119 protected AmetysObjectResolver _resolver; 120 121 /** The observation manager */ 122 protected ObservationManager _observationManager; 123 124 /** CacheManager used to create and get cache */ 125 protected AbstractCacheManager _cacheManager; 126 127 @Override 128 public void service(ServiceManager serviceManager) throws ServiceException 129 { 130 _ametysResolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE); 131 _siteManager = (SiteManager) serviceManager.lookup(SiteManager.ROLE); 132 _siteConf = (SiteConfigurationExtensionPoint) serviceManager.lookup(SiteConfigurationExtensionPoint.ROLE); 133 _blogRootHandler = (BlogPageHandler) serviceManager.lookup(BlogPageHandler.ROLE); 134 _workspaceSelector = (WorkspaceSelector) serviceManager.lookup(WorkspaceSelector.ROLE); 135 _tagProviderEP = (TagProviderExtensionPoint) serviceManager.lookup(TagProviderExtensionPoint.ROLE); 136 _resolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE); 137 _observationManager = (ObservationManager) serviceManager.lookup(ObservationManager.ROLE); 138 _cacheManager = (AbstractCacheManager) serviceManager.lookup(AbstractCacheManager.ROLE); 139 } 140 141 @Override 142 public void initialize() throws Exception 143 { 144 _cacheManager.createMemoryCache(__LAST_UPDATE_FUTURE_POST_CACHE, 145 new I18nizableText("plugin.blog", "PLUGINS_CORE_LAST_UPDATE_FUTURE_POST_CACHE_LABEL"), 146 new I18nizableText("plugin.blog", "PLUGINS_CORE_LAST_UPDATE_FUTURE_POST_CACHE_DESCRIPTION"), 147 true, 148 null); 149 _cacheManager.createMemoryCache(__POST_BY_DATE_CACHE, 150 new I18nizableText("plugin.blog", "PLUGINS_CORE_CACHE_POST_BY_DATE_LABEL"), 151 new I18nizableText("plugin.blog", "PLUGINS_CORE_CACHE_POST_BY_DATE_DESCRIPTION"), 152 true, 153 null); 154 _cacheManager.createMemoryCache(__POST_BY_TAG_CACHE, 155 new I18nizableText("plugin.blog", "PLUGINS_CORE_CACHE_POST_BY_TAG_LABEL"), 156 new I18nizableText("plugin.blog", "PLUGINS_CORE_CACHE_POST_BY_TAG_DESCRIPTION"), 157 true, 158 null); 159 _cacheManager.createMemoryCache(__ALL_POSTS_CACHE, 160 new I18nizableText("plugin.blog", "PLUGINS_CORE_CACHE_ALL_POSTS_LABEL"), 161 new I18nizableText("plugin.blog", "PLUGINS_CORE_CACHE_ALL_POSTS_DESCRIPTION"), 162 true, 163 null); 164 _cacheManager.createMemoryCache(__FUTURE_POSTS_CACHE, 165 new I18nizableText("plugin.blog", "PLUGINS_CORE_CACHE_FUTURE_POSTS_LABEL"), 166 new I18nizableText("plugin.blog", "PLUGINS_CORE_CACHE_FUTURE_POSTS_DESCRIPTION"), 167 true, 168 null); 169 } 170 171 /** 172 * Get the years with posts. 173 * @param siteName the site name. 174 * @param language the language. 175 * @param year the year 176 * @return the years with posts as a Set of Integer. 177 */ 178 public boolean hasYear(String siteName, String language, int year) 179 { 180 PostDateCache cache = getPostDateCache(siteName, language); 181 182 return cache.hasYear(year); 183 } 184 185 /** 186 * Get the years with posts. 187 * @param siteName the site name. 188 * @param language the language. 189 * @return the years with posts as a Set of Integer. 190 */ 191 public Set<Integer> getYears(String siteName, String language) 192 { 193 PostDateCache cache = getPostDateCache(siteName, language); 194 195 return cache.getYears(); 196 } 197 198 /** 199 * Get the years with posts. 200 * @param siteName the site name. 201 * @param language the language. 202 * @param year the year 203 * @param month the month 204 * @return the years with posts as a Set of Integer. 205 */ 206 public boolean hasMonth(String siteName, String language, int year, int month) 207 { 208 PostDateCache cache = getPostDateCache(siteName, language); 209 210 return cache.hasMonth(year, month); 211 } 212 213 /** 214 * Get the months with posts for a specified year. 215 * @param siteName the site name. 216 * @param language the language. 217 * @param year the year. 218 * @return the months with posts in the specified year as a Set of Integer. 219 */ 220 public Set<Integer> getMonths(String siteName, String language, int year) 221 { 222 PostDateCache cache = getPostDateCache(siteName, language); 223 224 return cache.getMonths(year); 225 } 226 227 /** 228 * Get the posts for a year-month couple. 229 * @param siteName the site name. 230 * @param language the language. 231 * @param year the year. 232 * @param month the month. 233 * @return the posts in the specified year and month. 234 */ 235 public Map<String, Post> getPostsByMonth(String siteName, String language, int year, int month) 236 { 237 PostDateCache cache = getPostDateCache(siteName, language); 238 239 return cache.getPosts(year, month); 240 } 241 242 /** 243 * Get the posts for a year-month couple. 244 * @param siteName the site name. 245 * @param language the language. 246 * @param year the year. 247 * @param month the month. 248 * @return the posts in the specified year and month. 249 */ 250 public Collection<Post> getSortedPostsByMonth(String siteName, String language, int year, int month) 251 { 252 PostDateCache cache = getPostDateCache(siteName, language); 253 254 List<Post> sortedPosts = new ArrayList<>(cache.getPosts(year, month).values()); 255 256 Collections.sort(sortedPosts, new ReverseDatePostComparator()); 257 258 return sortedPosts; 259 } 260 261 /** 262 * Get the posts for a year. 263 * @param siteName the site name. 264 * @param language the language. 265 * @param year the year. 266 * @return the posts in the specified year. 267 */ 268 public Map<String, Post> getPostsByYear(String siteName, String language, int year) 269 { 270 PostDateCache cache = getPostDateCache(siteName, language); 271 272 return cache.getPosts(year); 273 } 274 275 /** 276 * Get the posts for a year. 277 * @param siteName the site name. 278 * @param language the language. 279 * @param year the year. 280 * @return the posts in the specified year. 281 */ 282 public Collection<Post> getSortedPostsByYear(String siteName, String language, int year) 283 { 284 PostDateCache cache = getPostDateCache(siteName, language); 285 286 List<Post> sortedPosts = new ArrayList<>(cache.getPosts(year).values()); 287 288 Collections.sort(sortedPosts, new ReverseDatePostComparator()); 289 290 return sortedPosts; 291 } 292 293 /** 294 * Get the posts for a year-month couple. 295 * @param siteName the site name. 296 * @param language the language. 297 * @param year the year. 298 * @param month the month. 299 * @param name the name 300 * @return the posts in the specified year and month. 301 */ 302 public Post getPostByName(String siteName, String language, int year, int month, String name) 303 { 304 PostDateCache cache = getPostDateCache(siteName, language); 305 306 return cache.getPostByName(year, month, name); 307 } 308 309 /** 310 * Get the posts for a year-month couple. 311 * @param siteName the site name. 312 * @param language the language. 313 * @param year the year. 314 * @param month the month. 315 * @param id the id of post 316 * @return the posts in the specified year and month. 317 */ 318 public boolean hasPost(String siteName, String language, int year, int month, String id) 319 { 320 PostDateCache cache = getPostDateCache(siteName, language); 321 322 return cache.hasPost(year, month, id); 323 } 324 325 /** 326 * Get the posts for a year-month couple. 327 * @param siteName the site name. 328 * @param language the language. 329 * @param year the year. 330 * @param month the month. 331 * @param name the name of post 332 * @return the posts in the specified year and month. 333 */ 334 public boolean hasPostByName(String siteName, String language, int year, int month, String name) 335 { 336 PostDateCache cache = getPostDateCache(siteName, language); 337 338 return cache.hasPostByName(year, month, name); 339 } 340 341 ///////////////////////////////////////////////// 342 /// POSTS BY TAG METHODS /// 343 ///////////////////////////////////////////////// 344 345 /** 346 * Get the tags for a site and language. 347 * @param siteName The site name 348 * @param language The language 349 * @return the tag list. 350 */ 351 public Set<String> getTags(String siteName, String language) 352 { 353 PostTagCache cache = getPostTagCache(siteName, language); 354 355 return cache.getTags(); 356 } 357 358 /** 359 * Test if a tag exists in the given site and language. 360 * @param siteName The site name 361 * @param language The language 362 * @param tagName The tag name 363 * @return true if the tag exists, false otherwise. 364 */ 365 public boolean hasTag(String siteName, String language, String tagName) 366 { 367 PostTagCache cache = getPostTagCache(siteName, language); 368 369 Map<String, Object> params = new HashMap<>(); 370 params.put("siteName", siteName); 371 Tag tag = _tagProviderEP.getTag(tagName, params); 372 Set<String> descendantNamesAndItSelf = TagHelper.getDescendantNames(tag, true); 373 374 for (String descendantName : descendantNamesAndItSelf) 375 { 376 if (cache.hasTag(descendantName)) 377 { 378 return true; 379 } 380 } 381 382 return false; 383 } 384 385 /** 386 * Get the posts for a given tag. 387 * @param siteName The site name 388 * @param language The language 389 * @param tagName The tag name 390 * @return the tag posts, indexed by ID. 391 */ 392 public Map<String, Post> getPostsByTag(String siteName, String language, String tagName) 393 { 394 PostTagCache cache = getPostTagCache(siteName, language); 395 396 return cache.getPosts(tagName); 397 } 398 399 /** 400 * Get the posts for a given tag. 401 * @param siteName The site name 402 * @param language The language 403 * @param tagName The tag name 404 * @return the tag posts, indexed by ID. 405 */ 406 public Collection<Post> getSortedPostsByTag(String siteName, String language, String tagName) 407 { 408 return getSortedPostsByTag (siteName, language, tagName, false); 409 } 410 411 /** 412 * Get the posts for a given tag. 413 * @param siteName The site name 414 * @param language the language 415 * @param tagName the tag name 416 * @param deepSearch true to perform deep search 417 * @return the tag posts, indexed by ID. 418 */ 419 public Collection<Post> getSortedPostsByTag(String siteName, String language, String tagName, boolean deepSearch) 420 { 421 PostTagCache cache = getPostTagCache(siteName, language); 422 423 List<Post> sortedPosts = new ArrayList<>(cache.getPosts(tagName).values()); 424 425 if (deepSearch) 426 { 427 Map<String, Object> contextualParams = new HashMap<>(); 428 contextualParams.put("siteName", siteName); 429 430 Tag tag = _tagProviderEP.getTag(tagName, contextualParams); 431 if (tag != null) 432 { 433 Set<String> descendantsNames = TagHelper.getDescendantNames(tag, false); 434 for (String descendantsName : descendantsNames) 435 { 436 sortedPosts.addAll(cache.getPosts(descendantsName).values()); 437 } 438 } 439 } 440 441 Collections.sort(sortedPosts, new ReverseDatePostComparator()); 442 443 return sortedPosts; 444 } 445 446 /** 447 * Test if a given post has a tag, by its ID. 448 * @param siteName The site name 449 * @param language The language 450 * @param tagName The tag name 451 * @param postId The id of post 452 * @return true if the given post has the given tag. 453 */ 454 public boolean hasPostByTag(String siteName, String language, String tagName, String postId) 455 { 456 PostTagCache cache = getPostTagCache(siteName, language); 457 458 return cache.hasPost(tagName, postId); 459 } 460 461 /** 462 * Get the posts for a year-month couple. 463 * @param siteName The site name 464 * @param language The language 465 * @param tagName The tag name 466 * @param id The id of post 467 * @return the posts in the specified year and month. 468 */ 469 public Post getPost(String siteName, String language, String tagName, String id) 470 { 471 PostTagCache cache = getPostTagCache(siteName, language); 472 473 return cache.getPost(tagName, id); 474 } 475 476 /** 477 * Get the posts for a year-month couple. 478 * @param siteName The site name 479 * @param language The language 480 * @param tagName The tag name 481 * @param name the name of post 482 * @return the posts in the specified year and month. 483 */ 484 public Post getPostByName(String siteName, String language, String tagName, String name) 485 { 486 PostTagCache cache = getPostTagCache(siteName, language); 487 488 return cache.getPostByName(tagName, name); 489 } 490 491 /** 492 * Get the posts for a year-month couple. 493 * @param siteName The site name 494 * @param language The language 495 * @param tagName The tag name 496 * @param id The id of post 497 * @return the posts in the specified year and month. 498 */ 499 public boolean hasPost(String siteName, String language, String tagName, String id) 500 { 501 PostTagCache cache = getPostTagCache(siteName, language); 502 503 return cache.hasPost(tagName, id); 504 } 505 506 /** 507 * Get the posts for a year-month couple. 508 * @param siteName The site name 509 * @param language The language 510 * @param tagName The tag name 511 * @param name the name of post 512 * @return the posts in the specified year and month. 513 */ 514 public boolean hasPostByName(String siteName, String language, String tagName, String name) 515 { 516 PostTagCache cache = getPostTagCache(siteName, language); 517 518 return cache.hasPostByName(tagName, name); 519 } 520 521 ///////////////////////////////////////////////// 522 /// ALL POSTS METHODS /// 523 ///////////////////////////////////////////////// 524 525 /** 526 * Get a list of all the posts' IDs. 527 * @param siteName the site name. 528 * @param language the language. 529 * @return all the posts' IDs as a Set. 530 */ 531 public Set<String> getPostIds(String siteName, String language) 532 { 533 AllPostCache cache = getAllPostCache(siteName, language); 534 535 return cache.getPostIds(); 536 } 537 538 /** 539 * Get the exhaustive list of Posts. 540 * @param siteName the site name. 541 * @param language the language. 542 * @return the exhaustive list of Posts, indexed by their ID. 543 */ 544 public Map<String, Post> getPosts(String siteName, String language) 545 { 546 AllPostCache cache = getAllPostCache(siteName, language); 547 548 return cache.getPosts(); 549 } 550 551 /** 552 * Get the exhaustive list of Posts. 553 * @param siteName the site name. 554 * @param language the language. 555 * @return the exhaustive list of Posts, indexed by their ID. 556 */ 557 public Collection<Post> getSortedPosts(String siteName, String language) 558 { 559 AllPostCache cache = getAllPostCache(siteName, language); 560 561 List<Post> sortedPosts = new ArrayList<>(cache.getPosts().values()); 562 563 Collections.sort(sortedPosts, new ReverseDatePostComparator()); 564 565 return sortedPosts; 566 } 567 568 /** 569 * Test if a post exists, provided its ID. 570 * @param id the post content ID. 571 * @param siteName the site name. 572 * @param language the language. 573 * @return the Post. 574 */ 575 public Post getPost(String siteName, String language, String id) 576 { 577 AllPostCache cache = getAllPostCache(siteName, language); 578 579 return cache.getPost(id); 580 } 581 582 /** 583 * Test if a post exists, provided its content name. 584 * @param name the post content name. 585 * @param siteName the site name. 586 * @param language the language. 587 * @return true if the post exists. 588 */ 589 public Post getPostByName(String siteName, String language, String name) 590 { 591 AllPostCache cache = getAllPostCache(siteName, language); 592 593 return cache.getPostByName(name); 594 } 595 596 597 /** 598 * Test if a post exists, provided its ID. 599 * @param id the post content ID. 600 * @param siteName the site name. 601 * @param language the language. 602 * @return true if the post exists. 603 */ 604 public boolean hasPost(String siteName, String language, String id) 605 { 606 AllPostCache cache = getAllPostCache(siteName, language); 607 608 return cache.hasPost(id); 609 } 610 611 /** 612 * Test if a post exists, provided its content name. 613 * @param name the post content name. 614 * @param siteName the site name. 615 * @param language the language. 616 * @return true if the post exists. 617 */ 618 public boolean hasPostByName(String siteName, String language, String name) 619 { 620 AllPostCache cache = getAllPostCache(siteName, language); 621 622 return cache.hasPostByName(name); 623 } 624 625 /** 626 * Add a post content to the cache. 627 * @param siteName The site name 628 * @param language The language 629 * @param postContent The content 630 */ 631 public void addPost(String siteName, String language, Content postContent) 632 { 633 PostDateCache dateCache = getPostDateCache(siteName, language); 634 PostTagCache tagCache = getPostTagCache(siteName, language); 635 AllPostCache allCache = getAllPostCache(siteName, language); 636 FuturePostCache futureCache = getFuturePostCache(siteName, language); 637 638 boolean displayFuturePosts = _getDisplayFuturePost(siteName); 639 640 boolean isFuturePost = _isFuturePost(postContent); 641 if (displayFuturePosts || !isFuturePost) 642 { 643 dispatchPost(dateCache, postContent); 644 dispatchPost(tagCache, postContent); 645 dispatchPost(allCache, postContent); 646 } 647 else // => !displayFuturePosts && isFuturePost 648 { 649 dispatchPost(futureCache, postContent); 650 } 651 } 652 653 /** 654 * Modify a post content in the cache. 655 * @param siteName The site name 656 * @param language The language 657 * @param postContent The content 658 */ 659 public void modifyPost(String siteName, String language, Content postContent) 660 { 661 PostDateCache dateCache = getPostDateCache(siteName, language); 662 PostTagCache tagCache = getPostTagCache(siteName, language); 663 AllPostCache allCache = getAllPostCache(siteName, language); 664 FuturePostCache futureCache = getFuturePostCache(siteName, language); 665 666 Post post = allCache.getPost(postContent.getId()); 667 Post futurePost = futureCache.getPost(postContent.getId()); 668 669 boolean displayFuturePosts = _getDisplayFuturePost(siteName); 670 boolean isFuturePost = _isFuturePost(postContent); 671 672 if (displayFuturePosts || !isFuturePost) 673 { 674 if (futurePost != null) 675 { 676 removePost(futureCache, futurePost); 677 } 678 679 if (post == null) 680 { 681 dispatchPost(dateCache, postContent); 682 dispatchPost(tagCache, postContent); 683 dispatchPost(allCache, postContent); 684 } 685 else 686 { 687 modifyPost(dateCache, post, postContent); 688 modifyPost(tagCache, post, postContent); 689 modifyPost(allCache, post, postContent); 690 } 691 } 692 else // => !displayFuturePosts && isFuturePost 693 { 694 if (futurePost == null) 695 { 696 dispatchPost(futureCache, postContent); 697 } 698 else 699 { 700 modifyPost(futureCache, futurePost, postContent); 701 } 702 703 if (post != null) 704 { 705 removePost(dateCache, post); 706 removePost(tagCache, post); 707 removePost(allCache, post); 708 } 709 } 710 } 711 712 /** 713 * Validate a post content. 714 * @param siteName The site name 715 * @param language The language 716 * @param postContent The content 717 */ 718 public void validatePost(String siteName, String language, Content postContent) 719 { 720 boolean displayFuturePosts = _getDisplayFuturePost(siteName); 721 722 // Get the "live" caches. 723 PostDateCache dateCache = _getPostByDateCache().get(BlogPageElementKey.of(WebConstants.LIVE_WORKSPACE, siteName, language), k-> new PostDateCache()); 724 PostTagCache tagCache = _getPostByTagCache().get(BlogPageElementKey.of(WebConstants.LIVE_WORKSPACE, siteName, language), k-> new PostTagCache()); 725 AllPostCache allCache = _getAllPosts().get(BlogPageElementKey.of(WebConstants.LIVE_WORKSPACE, siteName, language), k-> new AllPostCache()); 726 FuturePostCache futureCache = _getFuturePosts().get(BlogPageElementKey.of(WebConstants.LIVE_WORKSPACE, siteName, language), k-> new FuturePostCache()); 727 728 if (displayFuturePosts || !_isFuturePost(postContent)) 729 { 730 // Dispatch the post in the live caches. 731 removePost(futureCache, postContent); 732 733 dispatchPost(dateCache, postContent); 734 dispatchPost(tagCache, postContent); 735 dispatchPost(allCache, postContent); 736 } 737 else // => !displayFuturePosts && isFuturePost 738 { 739 dispatchPost(futureCache, postContent); 740 741 removePost(dateCache, postContent); 742 removePost(tagCache, postContent); 743 removePost(allCache, postContent); 744 } 745 } 746 747 /** 748 * Remove a post content from the cache. 749 * @param siteName The site name 750 * @param language The language 751 * @param postContent The content 752 */ 753 public void removePost(String siteName, String language, Content postContent) 754 { 755 PostDateCache dateCache = getPostDateCache(siteName, language); 756 PostTagCache tagCache = getPostTagCache(siteName, language); 757 AllPostCache allCache = getAllPostCache(siteName, language); 758 FuturePostCache futureCache = getFuturePostCache(siteName, language); 759 760 removePost(dateCache, postContent); 761 removePost(tagCache, postContent); 762 removePost(allCache, postContent); 763 removePost(futureCache, postContent); 764 } 765 766 /** 767 * Remove a post content from the cache. 768 * @param siteName the site name. 769 * @param postId the post ID. 770 */ 771 public void removePostById(String siteName, String postId) 772 { 773 PostSiteLang postWithSiteLang = getPost(siteName, postId); 774 PostSiteLang futurePostWithSiteLang = getFuturePost(siteName, postId); 775 776 if (postWithSiteLang != null) 777 { 778 Post post = postWithSiteLang.getPost(); 779 String language = postWithSiteLang.getLanguage(); 780 781 // Get cache from current workspace 782 PostDateCache dateCache = getPostDateCache(siteName, language); 783 PostTagCache tagCache = getPostTagCache(siteName, language); 784 AllPostCache allCache = getAllPostCache(siteName, language); 785 786 // Get cache from live workspace 787 PostDateCache workspaceDateCache = _getPostByDateCache().get(BlogPageElementKey.of(WebConstants.LIVE_WORKSPACE, siteName, language), k-> new PostDateCache()); 788 PostTagCache workspaceTagCache = _getPostByTagCache().get(BlogPageElementKey.of(WebConstants.LIVE_WORKSPACE, siteName, language), k-> new PostTagCache()); 789 AllPostCache workspaceAllCache = _getAllPosts().get(BlogPageElementKey.of(WebConstants.LIVE_WORKSPACE, siteName, language), k-> new AllPostCache()); 790 791 // Update the cache 792 removePost(dateCache, post); 793 removePost(tagCache, post); 794 removePost(allCache, post); 795 796 removePost(workspaceDateCache, post); 797 removePost(workspaceTagCache, post); 798 removePost(workspaceAllCache, post); 799 } 800 801 if (futurePostWithSiteLang != null) 802 { 803 Post post = futurePostWithSiteLang.getPost(); 804 String language = futurePostWithSiteLang.getLanguage(); 805 806 // Get cache from current workspace 807 FuturePostCache futureCache = getFuturePostCache(siteName, language); 808 809 // Get cache from live workspace 810 FuturePostCache workspaceFutureCache = _getFuturePosts().get(BlogPageElementKey.of(WebConstants.LIVE_WORKSPACE, siteName, language), k-> new FuturePostCache()); 811 812 // Update the cache 813 removePost(futureCache, post); 814 removePost(workspaceFutureCache, post); 815 } 816 } 817 818 /** 819 * Clear the caches. 820 */ 821 public void clearCaches() 822 { 823 _getPostByDateCache().invalidateAll(); 824 _getPostByTagCache().invalidateAll(); 825 _getAllPosts().invalidateAll(); 826 _getFuturePosts().invalidateAll(); 827 } 828 829 /** 830 * Clear the caches for a given siteName and language. 831 * @param siteName The site name 832 * @param language The language 833 */ 834 public void clearCaches(String siteName, String language) 835 { 836 for (BlogPageElementKey key : _getPostByDateCache().asMap().keySet()) 837 { 838 PostDateCache cache = _getPostByDateCache().get(BlogPageElementKey.of((String) key.getFields().get(0), siteName, language)); 839 cache.clear(); 840 } 841 842 for (BlogPageElementKey key : _getPostByTagCache().asMap().keySet()) 843 { 844 PostTagCache cache = _getPostByTagCache().get(BlogPageElementKey.of((String) key.getFields().get(0), siteName, language)); 845 cache.clear(); 846 } 847 848 for (BlogPageElementKey key : _getAllPosts().asMap().keySet()) 849 { 850 AllPostCache cache = _getAllPosts().get(BlogPageElementKey.of((String) key.getFields().get(0), siteName, language)); 851 cache.clear(); 852 } 853 854 for (BlogPageElementKey key : _getFuturePosts().asMap().keySet()) 855 { 856 FuturePostCache cache = _getFuturePosts().get(BlogPageElementKey.of((String) key.getFields().get(0), siteName, language)); 857 cache.clear(); 858 } 859 } 860 861 /** 862 * Get the post date cache. 863 * @param siteName The site name 864 * @param language The language 865 * @return the post date cache for the given site and language. 866 */ 867 protected PostDateCache getPostDateCache(String siteName, String language) 868 { 869 // Get the workspace name. 870 String workspaceName = _workspaceSelector.getWorkspace(); 871 872 // Ensure the cache is created and initialized for the site and language. 873 if (!_getPostByDateCache().hasKey(BlogPageElementKey.of(workspaceName, siteName, language))) 874 { 875 initializeCaches(workspaceName, siteName, language); 876 } 877 878 // Update the future posts cache. 879 _updateFuturePosts(workspaceName, siteName, language); 880 return _getPostByDateCache().get(BlogPageElementKey.of(workspaceName, siteName, language)); 881 } 882 883 /** 884 * Get the post tag cache. 885 * @param siteName The site name 886 * @param language The language 887 * @return the post tag cache for the given site and language. 888 */ 889 protected PostTagCache getPostTagCache(String siteName, String language) 890 { 891 // Get the workspace name. 892 String workspaceName = _workspaceSelector.getWorkspace(); 893 894 // Ensure the cache is created and initialized for the site and language. 895 if (!_getPostByTagCache().hasKey(BlogPageElementKey.of(workspaceName, siteName, language))) 896 { 897 initializeCaches(workspaceName, siteName, language); 898 } 899 900 // Update the future posts cache. 901 _updateFuturePosts(workspaceName, siteName, language); 902 903 return _getPostByTagCache().get(BlogPageElementKey.of(workspaceName, siteName, language)); 904 } 905 906 /** 907 * Get the exhaustive post cache. 908 * @param siteName The site name 909 * @param language The language 910 * @return the post date cache for the given site and language. 911 */ 912 protected AllPostCache getAllPostCache(String siteName, String language) 913 { 914 // Get the workspace name. 915 String workspaceName = _workspaceSelector.getWorkspace(); 916 917 // Ensure the cache is created and initialized for the site and language. 918 if (!_getAllPosts().hasKey(BlogPageElementKey.of(workspaceName, siteName, language))) 919 { 920 initializeCaches(workspaceName, siteName, language); 921 } 922 923 // Update the future posts cache. 924 _updateFuturePosts(workspaceName, siteName, language); 925 return _getAllPosts().get(BlogPageElementKey.of(workspaceName, siteName, language)); 926 } 927 928 929 /** 930 * Get the future post cache. 931 * @param siteName the name of the site 932 * @param language the language 933 * @return the post date cache for the given site and language. 934 */ 935 protected FuturePostCache getFuturePostCache(String siteName, String language) 936 { 937 // Get the workspace name. 938 String workspaceName = _workspaceSelector.getWorkspace(); 939 940 // Ensure the cache is created and initialized for the site and language. 941 if (!_getFuturePosts().hasKey(BlogPageElementKey.of(workspaceName, siteName, language))) 942 { 943 initializeCaches(workspaceName, siteName, language); 944 } 945 946 return _getFuturePosts().get(BlogPageElementKey.of(workspaceName, siteName, language)); 947 } 948 949 /** 950 * Initialize all the caches. 951 * @param workspaceName The workspace name 952 * @param siteName The site name 953 * @param language The language 954 */ 955 protected void initializeCaches(String workspaceName, String siteName, String language) 956 { 957 PostDateCache dateCache = _getPostByDateCache().get(BlogPageElementKey.of(workspaceName, siteName, language), k-> new PostDateCache()); 958 PostTagCache tagCache = _getPostByTagCache().get(BlogPageElementKey.of(workspaceName, siteName, language), k-> new PostTagCache()); 959 AllPostCache allCache = _getAllPosts().get(BlogPageElementKey.of(workspaceName, siteName, language), k-> new AllPostCache()); 960 FuturePostCache futureCache = _getFuturePosts().get(BlogPageElementKey.of(workspaceName, siteName, language), k-> new FuturePostCache()); 961 962 ContentTypeExpression cTypeExpr = new ContentTypeExpression(Operator.EQ, PostContentType.CONTENT_TYPE); 963 Expression siteExpr = new StringExpression(SiteAwareAmetysObject.METADATA_SITE, Operator.EQ, siteName); 964 Expression languageExpr = new LanguageExpression(Operator.EQ, language); 965 MetadataExpression dateExpr = new MetadataExpression(POST_DATE_ATTRIBUTE); 966 967 Expression expression = new AndExpression(cTypeExpr, siteExpr, languageExpr, dateExpr); 968 969 boolean displayFuturePosts = _getDisplayFuturePost(siteName); 970 971 SortCriteria sort = new SortCriteria(); 972 sort.addCriterion(POST_DATE_ATTRIBUTE, false, false); 973 sort.addJCRPropertyCriterion("ametys:lastModified", false, false); 974 975 String query = QueryHelper.getXPathQuery(null, "ametys:content", expression, sort); 976 977 try (AmetysObjectIterable<Content> posts = _ametysResolver.query(query);) 978 { 979 for (Content postContent : posts) 980 { 981 if (displayFuturePosts || !_isFuturePost(postContent)) 982 { 983 dispatchPost(dateCache, postContent); 984 dispatchPost(tagCache, postContent); 985 dispatchPost(allCache, postContent); 986 } 987 else 988 { 989 dispatchPost(futureCache, postContent); 990 } 991 } 992 } 993 } 994 995 private boolean _getDisplayFuturePost(String siteName) 996 { 997 boolean displayFuturePosts = true; 998 if (siteName != null) 999 { 1000 Site site = _siteManager.getSite(siteName); 1001 displayFuturePosts = site.getValue("display-future-posts", false, false); 1002 } 1003 return displayFuturePosts; 1004 } 1005 1006 private void _updateFuturePosts(String workspaceName, String siteName, String language) 1007 { 1008 Date now = new Date(); 1009 1010 Calendar calendar = Calendar.getInstance(); 1011 calendar.setTime(now); 1012 1013 int minutes = calendar.get(Calendar.MINUTE); 1014 1015 int mod = minutes % 15; 1016 calendar.add(Calendar.MINUTE, -mod); 1017 calendar.set(Calendar.SECOND, 0); 1018 calendar.set(Calendar.MILLISECOND, 0); 1019 1020 Date roundedNow = calendar.getTime(); 1021 1022 if (_needUpdateFuturePosts(workspaceName, siteName, language, roundedNow)) 1023 { 1024 // Update the last update time 1025 _getLastUpdateFuturePosts().put(BlogPageElementKey.of(workspaceName, siteName, language), roundedNow); 1026 1027 // Remove past "future posts" 1028 FuturePostCache futureCache = _getFuturePosts().get(BlogPageElementKey.of(workspaceName, siteName, language), k-> new FuturePostCache()); 1029 Collection<Post> pastPosts = futureCache.removePastPosts(); 1030 1031 // Notify that the posts are pasts, and must be visible now. 1032 for (Post pastPost : pastPosts) 1033 { 1034 if (_resolver.hasAmetysObjectForId(pastPost.getId())) 1035 { 1036 Content content = _resolver.resolveById(pastPost.getId()); 1037 1038 Map<String, Object> eventParams = new HashMap<>(); 1039 eventParams.put(ObservationConstants.ARGS_CONTENT, content); 1040 _observationManager.notify(new Event(BlogObservationConstants.EVENT_FUTURE_CONTENT_VISIBLE, new UserIdentity("admin", "admin"), eventParams)); 1041 } 1042 } 1043 } 1044 } 1045 1046 private boolean _needUpdateFuturePosts(String workspaceName, String siteName, String language, Date roundedNow) 1047 { 1048 Date lastUpdate = _getLastUpdateFuturePosts().get(BlogPageElementKey.of(workspaceName, siteName, language)); 1049 1050 if (lastUpdate != null) 1051 { 1052 long duration = roundedNow.getTime() - lastUpdate.getTime(); 1053 long diffInMinutes = TimeUnit.MILLISECONDS.toMinutes(duration); 1054 1055 return diffInMinutes >= 15; 1056 } 1057 1058 return true; 1059 } 1060 1061 private boolean _isFuturePost (Content post) 1062 { 1063 ZonedDateTime postDate = post.getValue(POST_DATE_ATTRIBUTE); 1064 return postDate != null && postDate.isAfter(ZonedDateTime.now()); 1065 } 1066 1067 /** 1068 * Get a post by its site name and id. 1069 * @param siteName the site name. 1070 * @param postId the post ID. 1071 * @return the Post if it exists in the cache, or null if it doesn't. 1072 */ 1073 protected PostSiteLang getPost(String siteName, String postId) 1074 { 1075 String workspaceName = _workspaceSelector.getWorkspace(); 1076 1077 for (Entry<BlogPageElementKey, AllPostCache> langPosts : _getAllPosts().getAll(BlogPageElementKey.of(workspaceName, siteName)).entrySet()) 1078 { 1079 BlogPageElementKey blogPageElementKey = langPosts.getKey(); 1080 List<Object> fields = blogPageElementKey.getFields(); 1081 if (langPosts.getValue().hasPost(postId)) 1082 { 1083 String language = (String) fields.get(2); 1084 Post post = langPosts.getValue().getPost(postId); 1085 1086 return new PostSiteLang(post, siteName, language); 1087 } 1088 } 1089 1090 return null; 1091 } 1092 1093 /** 1094 * Get a future post by its site name and id. 1095 * @param siteName the site name. 1096 * @param postId the post ID. 1097 * @return the Post if it exists in the cache, or null if it doesn't. 1098 */ 1099 protected PostSiteLang getFuturePost(String siteName, String postId) 1100 { 1101 1102 String workspaceName = _workspaceSelector.getWorkspace(); 1103 1104 1105 for (Entry<BlogPageElementKey, FuturePostCache> langPosts : _getFuturePosts().getAll(BlogPageElementKey.of(workspaceName, siteName)).entrySet()) 1106 { 1107 BlogPageElementKey blogPageElementKey = langPosts.getKey(); 1108 List<Object> fields = blogPageElementKey.getFields(); 1109 if (langPosts.getValue().hasPost(postId)) 1110 { 1111 String language = (String) fields.get(2); 1112 Post post = langPosts.getValue().getPost(postId); 1113 1114 return new PostSiteLang(post, siteName, language); 1115 } 1116 1117 } 1118 1119 return null; 1120 } 1121 1122 /** 1123 * Dispatch a post content in the post date cache. 1124 * @param dateCache the post date cache. 1125 * @param content the Post content. 1126 */ 1127 protected void dispatchPost(PostDateCache dateCache, Content content) 1128 { 1129 ZonedDateTime postDate = content.getValue(POST_DATE_ATTRIBUTE); 1130 1131 // Dispatch by date. 1132 if (postDate != null) 1133 { 1134 int month = postDate.getMonthValue(); 1135 int year = postDate.getYear(); 1136 1137 dateCache.addPost(content, year, month); 1138 } 1139 } 1140 1141 /** 1142 * Add a post in the post date cache. 1143 * @param dateCache the post date cache. 1144 * @param post the Post to add. 1145 */ 1146 protected void dispatchPost(PostDateCache dateCache, Post post) 1147 { 1148 ZonedDateTime postDate = post.getDate(); 1149 1150 // Dispatch by date. 1151 if (postDate != null) 1152 { 1153 int month = postDate.getMonthValue(); 1154 int year = postDate.getYear(); 1155 1156 dateCache.addPost(post, year, month); 1157 } 1158 } 1159 1160 /** 1161 * Dispatch a post content in the post tag cache. 1162 * @param tagCache the post tag cache. 1163 * @param content the Post content. 1164 */ 1165 protected void dispatchPost(PostTagCache tagCache, Content content) 1166 { 1167 String[] postTags = content.getValue(POST_TAGS_ATTRIBUTE, false, new String[0]); 1168 1169 for (String tagName : postTags) 1170 { 1171 tagCache.addPost(content, tagName); 1172 } 1173 } 1174 1175 /** 1176 * Dispatch a post in the post tag cache. 1177 * @param tagCache the post tag cache. 1178 * @param post the Post to add. 1179 */ 1180 protected void dispatchPost(PostTagCache tagCache, Post post) 1181 { 1182 String[] postTags = post.getTags(); 1183 1184 for (String tagName : postTags) 1185 { 1186 tagCache.addPost(post, tagName); 1187 } 1188 } 1189 1190 /** 1191 * Dispatch a post content in the post cache. 1192 * @param cache the post cache. 1193 * @param content the Post content. 1194 */ 1195 protected void dispatchPost(AllPostCache cache, Content content) 1196 { 1197 cache.addPost(content); 1198 } 1199 1200 /** 1201 * Dispatch a post in the post cache. 1202 * @param cache the post cache. 1203 * @param post the Post content. 1204 */ 1205 protected void dispatchPost(AllPostCache cache, Post post) 1206 { 1207 cache.addPost(post); 1208 } 1209 1210 /** 1211 * Dispatch a post content in the future post cache. 1212 * @param cache the future post cache. 1213 * @param content the Post content. 1214 */ 1215 protected void dispatchPost(FuturePostCache cache, Content content) 1216 { 1217 cache.addPost(content); 1218 } 1219 1220 /** 1221 * Remove a post content. 1222 * @param dateCache the post date cache. 1223 * @param content the Post content. 1224 */ 1225 protected void removePost(PostDateCache dateCache, Content content) 1226 { 1227 ZonedDateTime postDate = content.getValue(POST_DATE_ATTRIBUTE); 1228 1229 // Dispatch by date. 1230 if (postDate != null) 1231 { 1232 int month = postDate.getMonthValue(); 1233 int year = postDate.getYear(); 1234 1235 dateCache.removePost(content.getId(), year, month); 1236 } 1237 } 1238 1239 /** 1240 * Remove a post content. 1241 * @param dateCache the post date cache. 1242 * @param post the Post object to remove. 1243 */ 1244 protected void removePost(PostDateCache dateCache, Post post) 1245 { 1246 ZonedDateTime postDate = post.getDate(); 1247 1248 // Dispatch by date. 1249 if (postDate != null) 1250 { 1251 int month = postDate.getMonthValue(); 1252 int year = postDate.getYear(); 1253 1254 dateCache.removePost(post.getId(), year, month); 1255 } 1256 } 1257 1258 /** 1259 * Remove a post content. 1260 * @param cache the post tag cache. 1261 * @param content the Post content. 1262 */ 1263 protected void removePost(PostTagCache cache, Content content) 1264 { 1265 String[] postTags = content.getValue(POST_TAGS_ATTRIBUTE, false, new String[0]); 1266 1267 for (String tagName : postTags) 1268 { 1269 cache.removePost(content.getId(), tagName); 1270 } 1271 } 1272 1273 /** 1274 * Remove a post content. 1275 * @param cache the post tag cache. 1276 * @param post the Post to remove. 1277 */ 1278 protected void removePost(PostTagCache cache, Post post) 1279 { 1280 String[] postTags = post.getTags(); 1281 1282 for (String tagName : postTags) 1283 { 1284 cache.removePost(post.getId(), tagName); 1285 } 1286 } 1287 1288 /** 1289 * Remove a post content. 1290 * @param cache the post cache. 1291 * @param content the Post content. 1292 */ 1293 protected void removePost(AllPostCache cache, Content content) 1294 { 1295 cache.removePost(content); 1296 } 1297 1298 /** 1299 * Remove a post content. 1300 * @param cache the post cache. 1301 * @param post the Post to remove. 1302 */ 1303 protected void removePost(AllPostCache cache, Post post) 1304 { 1305 cache.removePost(post.getId(), post.getName()); 1306 } 1307 1308 /** 1309 * Remove a future post content. 1310 * @param cache the future post cache. 1311 * @param content the Post content to remove. 1312 */ 1313 protected void removePost(FuturePostCache cache, Content content) 1314 { 1315 cache.removePost(content); 1316 } 1317 1318 /** 1319 * Remove a future post content. 1320 * @param cache the future post cache. 1321 * @param post the Post to remove. 1322 */ 1323 protected void removePost(FuturePostCache cache, Post post) 1324 { 1325 cache.removePost(post); 1326 } 1327 1328 /** 1329 * Remove a post content. 1330 * @param dateCache the post date cache. 1331 * @param post the post content 1332 * @param postContent the post Content. 1333 */ 1334 protected void modifyPost(PostDateCache dateCache, Post post, Content postContent) 1335 { 1336 ZonedDateTime oldDate = post.getDate(); 1337 ZonedDateTime newDate = postContent.getValue(POST_DATE_ATTRIBUTE); 1338 1339 if (!newDate.equals(oldDate)) 1340 { 1341 if (oldDate != null) 1342 { 1343 int month = oldDate.getMonthValue(); 1344 int year = oldDate.getYear(); 1345 dateCache.removePost(postContent.getId(), year, month); 1346 } 1347 1348 int month = newDate.getMonthValue(); 1349 int year = newDate.getYear(); 1350 dateCache.addPost(postContent, year, month); 1351 } 1352 } 1353 1354 /** 1355 * Remove a post content. 1356 * @param cache the post tag cache. 1357 * @param post the post content 1358 * @param postContent the post Content. 1359 */ 1360 protected void modifyPost(PostTagCache cache, Post post, Content postContent) 1361 { 1362 String[] oldPostTags = post.getTags(); 1363 String[] newPostTags = postContent.getValue(POST_TAGS_ATTRIBUTE, false, new String[0]); 1364 1365 for (String oldTagName : oldPostTags) 1366 { 1367 cache.removePost(postContent.getId(), oldTagName); 1368 } 1369 1370 for (String newTagName : newPostTags) 1371 { 1372 cache.addPost(postContent, newTagName); 1373 } 1374 } 1375 1376 /** 1377 * Modify a post content. 1378 * @param cache the post cache. 1379 * @param post the post content 1380 * @param postContent the post Content. 1381 */ 1382 protected void modifyPost(AllPostCache cache, Post post, Content postContent) 1383 { 1384 cache.removePost(postContent); 1385 cache.addPost(postContent); 1386 } 1387 1388 /** 1389 * Modify a future post content. 1390 * @param cache the post cache. 1391 * @param post the post 1392 * @param postContent the post Content. 1393 */ 1394 protected void modifyPost(FuturePostCache cache, Post post, Content postContent) 1395 { 1396 cache.removePost(postContent); 1397 cache.addPost(postContent); 1398 } 1399 1400 /** 1401 * Class representing a Post in the cache. 1402 */ 1403 public static class Post 1404 { 1405 1406 /** The post content id. */ 1407 protected String _id; 1408 1409 /** The post content name. */ 1410 protected String _name; 1411 1412 /** The post date. */ 1413 protected ZonedDateTime _date; 1414 1415 /** The post tags. */ 1416 protected String[] _tags; 1417 1418 /** 1419 * Get the id. 1420 * @return the id 1421 */ 1422 public String getId() 1423 { 1424 return _id; 1425 } 1426 1427 /** 1428 * Set the id. 1429 * @param id the id to set 1430 */ 1431 public void setId(String id) 1432 { 1433 this._id = id; 1434 } 1435 1436 /** 1437 * Get the name. 1438 * @return the name 1439 */ 1440 public String getName() 1441 { 1442 return _name; 1443 } 1444 1445 /** 1446 * Set the name. 1447 * @param name the name to set 1448 */ 1449 public void setName(String name) 1450 { 1451 this._name = name; 1452 } 1453 1454 /** 1455 * Get the tags. 1456 * @return the tags 1457 */ 1458 public String[] getTags() 1459 { 1460 return _tags; 1461 } 1462 1463 /** 1464 * Set the tags. 1465 * @param tags the tags to set 1466 */ 1467 public void setTags(String[] tags) 1468 { 1469 this._tags = new String[tags.length]; 1470 System.arraycopy(tags, 0, _tags, 0, tags.length); 1471 } 1472 1473 /** 1474 * Get the date. 1475 * @return the date 1476 */ 1477 public ZonedDateTime getDate() 1478 { 1479 return _date; 1480 } 1481 1482 /** 1483 * Set the date. 1484 * @param date the date to set 1485 */ 1486 public void setDate(ZonedDateTime date) 1487 { 1488 this._date = date; 1489 } 1490 1491 @Override 1492 public int hashCode() 1493 { 1494 return _id.hashCode(); 1495 } 1496 1497 @Override 1498 public boolean equals(Object obj) 1499 { 1500 if (this == obj) 1501 { 1502 return true; 1503 } 1504 if (obj == null) 1505 { 1506 return false; 1507 } 1508 if (getClass() != obj.getClass()) 1509 { 1510 return false; 1511 } 1512 Post other = (Post) obj; 1513 if (_id == null) 1514 { 1515 if (other._id != null) 1516 { 1517 return false; 1518 } 1519 } 1520 else if (!_id.equals(other._id)) 1521 { 1522 return false; 1523 } 1524 return true; 1525 } 1526 1527 } 1528 1529 /** 1530 * A Post with site name and language. 1531 */ 1532 protected static class PostSiteLang extends Post 1533 { 1534 1535 /** The post. */ 1536 protected Post _post; 1537 1538 /** The post site name. */ 1539 protected String _siteName; 1540 1541 /** The post language. */ 1542 protected String _language; 1543 1544 /** 1545 * Default constructor. 1546 */ 1547 public PostSiteLang() 1548 { 1549 // Nothing to do. 1550 } 1551 1552 /** 1553 * Constructor with all parameters. 1554 * @param post the Post. 1555 * @param siteName the site name. 1556 * @param language the language. 1557 */ 1558 public PostSiteLang(Post post, String siteName, String language) 1559 { 1560 super(); 1561 this._post = post; 1562 this._siteName = siteName; 1563 this._language = language; 1564 } 1565 1566 /** 1567 * Get the post. 1568 * @return the post 1569 */ 1570 public Post getPost() 1571 { 1572 return _post; 1573 } 1574 1575 /** 1576 * Set the post. 1577 * @param post the post to set 1578 */ 1579 public void setPost(Post post) 1580 { 1581 this._post = post; 1582 } 1583 1584 /** 1585 * Get the siteName. 1586 * @return the siteName 1587 */ 1588 public String getSiteName() 1589 { 1590 return _siteName; 1591 } 1592 1593 /** 1594 * Set the siteName. 1595 * @param siteName the siteName to set 1596 */ 1597 public void setSiteName(String siteName) 1598 { 1599 this._siteName = siteName; 1600 } 1601 1602 /** 1603 * Get the language. 1604 * @return the language 1605 */ 1606 public String getLanguage() 1607 { 1608 return _language; 1609 } 1610 1611 /** 1612 * Set the language. 1613 * @param language the language to set 1614 */ 1615 public void setLanguage(String language) 1616 { 1617 this._language = language; 1618 } 1619 1620 } 1621 1622 /** 1623 * Post cache by date. 1624 */ 1625 protected static class PostDateCache 1626 { 1627 /** The posts, indexed by year, month and ID. */ 1628 private Map<Integer, Map<Integer, Map<String, Post>>> _posts; 1629 1630 /** 1631 * Construct a PostDateCache instance. 1632 */ 1633 public PostDateCache() 1634 { 1635 _posts = new HashMap<>(); 1636 } 1637 1638 /** 1639 * Determines if the year page exists 1640 * @param year the year 1641 * @return true if there are posts for this year 1642 */ 1643 public boolean hasYear(int year) 1644 { 1645 return _posts.containsKey(year); 1646 } 1647 1648 /** 1649 * Get the years with posts. 1650 * @return the years with posts as a Set of Integer. 1651 */ 1652 public Set<Integer> getYears() 1653 { 1654 return _posts.keySet(); 1655 } 1656 1657 /** 1658 * Determines if the page month exists in given year 1659 * @param year The year 1660 * @param month The month 1661 * @return true if there are posts for this year/month couple 1662 */ 1663 public boolean hasMonth(int year, int month) 1664 { 1665 boolean hasMonth = false; 1666 1667 if (_posts.containsKey(year)) 1668 { 1669 hasMonth = _posts.get(year).containsKey(month); 1670 } 1671 1672 return hasMonth; 1673 } 1674 1675 /** 1676 * Get the months with posts for a specified year. 1677 * @param year the year. 1678 * @return the months with posts in the specified year as a Set of Integer. 1679 */ 1680 public Set<Integer> getMonths(int year) 1681 { 1682 Set<Integer> months = new HashSet<>(); 1683 1684 if (_posts.containsKey(year)) 1685 { 1686 months = _posts.get(year).keySet(); 1687 } 1688 1689 return months; 1690 } 1691 1692 /** 1693 * Get the posts for a year couple. 1694 * @param year the year. 1695 * @return the posts in the specified year. 1696 */ 1697 public Map<String, Post> getPosts(int year) 1698 { 1699 Map<String, Post> posts = new HashMap<>(); 1700 1701 if (_posts.containsKey(year)) 1702 { 1703 Map<Integer, Map<String, Post>> months = _posts.get(year); 1704 1705 for (Map<String, Post> month : months.values()) 1706 { 1707 posts.putAll(month); 1708 } 1709 } 1710 1711 return posts; 1712 } 1713 1714 /** 1715 * Get the posts for a year-month couple. 1716 * @param year the year. 1717 * @param month the month. 1718 * @return the posts in the specified year and month. 1719 */ 1720 public Map<String, Post> getPosts(int year, int month) 1721 { 1722 Map<String, Post> posts = new HashMap<>(); 1723 1724 if (_posts.containsKey(year)) 1725 { 1726 Map<Integer, Map<String, Post>> months = _posts.get(year); 1727 1728 if (months.containsKey(month)) 1729 { 1730 posts = months.get(month); 1731 } 1732 } 1733 1734 return posts; 1735 } 1736 1737 /** 1738 * Add a post to the cache. 1739 * @param content The content 1740 * @param year the year 1741 * @param month the month 1742 */ 1743 public void addPost(Content content, int year, int month) 1744 { 1745 Map<String, Post> posts = getOrCreatePostMap(year, month); 1746 1747 Post post = new Post(); 1748 post.setId(content.getId()); 1749 post.setName(content.getName()); 1750 post.setDate(content.getValue(POST_DATE_ATTRIBUTE)); 1751 post.setTags(content.getValue(POST_TAGS_ATTRIBUTE, false, new String[0])); 1752 1753 posts.put(post.getId(), post); 1754 } 1755 1756 /** 1757 * Add a post to the cache. 1758 * @param post the post 1759 * @param year the year of the post 1760 * @param month the month of the post 1761 */ 1762 public void addPost(Post post, int year, int month) 1763 { 1764 Map<String, Post> posts = getOrCreatePostMap(year, month); 1765 posts.put(post.getId(), post); 1766 } 1767 1768 /** 1769 * Remove a post from the cache. 1770 * @param contentId The content id 1771 * @param year the year 1772 * @param month the month 1773 */ 1774 public void removePost(String contentId, int year, int month) 1775 { 1776 if (_posts.containsKey(year)) 1777 { 1778 Map<Integer, Map<String, Post>> months = _posts.get(year); 1779 1780 if (months.containsKey(month)) 1781 { 1782 Map<String, Post> posts = months.get(month); 1783 1784 posts.remove(contentId); 1785 if (posts.isEmpty()) 1786 { 1787 months.remove(month); 1788 } 1789 } 1790 1791 if (months.isEmpty()) 1792 { 1793 _posts.remove(year); 1794 } 1795 } 1796 } 1797 1798 /** 1799 * Test if the post with the given ID exists for this year and month. 1800 * @param year the year. 1801 * @param month the month. 1802 * @param id the id of post 1803 * @return the posts in the specified year and month. 1804 */ 1805 public boolean hasPost(int year, int month, String id) 1806 { 1807 return getPosts(year, month).containsKey(id); 1808 } 1809 1810 /** 1811 * Get the posts for a year-month couple. 1812 * @param year the year. 1813 * @param month the month. 1814 * @param name the name of post 1815 * @return the posts in the specified year and month. 1816 */ 1817 public Post getPostByName(int year, int month, String name) 1818 { 1819 Post post = null; 1820 1821 Iterator<Post> posts = getPosts(year, month).values().iterator(); 1822 while (posts.hasNext() && post == null) 1823 { 1824 Post currentPost = posts.next(); 1825 if (currentPost.getName().equals(name)) 1826 { 1827 post = currentPost; 1828 } 1829 } 1830 1831 return post; 1832 } 1833 1834 /** 1835 * Get the posts for a year-month couple. 1836 * @param year the year. 1837 * @param month the month. 1838 * @param name the name of post 1839 * @return the posts in the specified year and month. 1840 */ 1841 public boolean hasPostByName(int year, int month, String name) 1842 { 1843 boolean hasPost = false; 1844 1845 Iterator<Post> posts = getPosts(year, month).values().iterator(); 1846 while (posts.hasNext() && !hasPost) 1847 { 1848 hasPost = posts.next().getName().equals(name); 1849 } 1850 1851 return hasPost; 1852 } 1853 1854 /** 1855 * Clear the cache. 1856 */ 1857 public void clear() 1858 { 1859 _posts.clear(); 1860 } 1861 1862 /** 1863 * Get or create the post set for the specified year and month. 1864 * @param year the year. 1865 * @param month the month. 1866 * @return the post set. 1867 */ 1868 protected Map<String, Post> getOrCreatePostMap(int year, int month) 1869 { 1870 Map<String, Post> posts; 1871 1872 Map<Integer, Map<String, Post>> months; 1873 1874 if (_posts.containsKey(year)) 1875 { 1876 months = _posts.get(year); 1877 } 1878 else 1879 { 1880 months = new HashMap<>(); 1881 _posts.put(year, months); 1882 } 1883 1884 if (months.containsKey(month)) 1885 { 1886 posts = months.get(month); 1887 } 1888 else 1889 { 1890 posts = new LinkedHashMap<>(); 1891 months.put(month, posts); 1892 } 1893 1894 return posts; 1895 } 1896 1897 } 1898 1899 /** 1900 * Post cache by tag. 1901 */ 1902 protected static class PostTagCache 1903 { 1904 1905 /** The posts, indexed by tag and ID. */ 1906 private Map<String, Map<String, Post>> _posts; 1907 1908 /** 1909 * Construct a PostTagCache instance. 1910 */ 1911 public PostTagCache() 1912 { 1913 _posts = new HashMap<>(); 1914 } 1915 1916 /** 1917 * Get the years with posts. 1918 * @param tagId The id of tag 1919 * @return the years with posts as a Set of Integer. 1920 */ 1921 public boolean hasTag(String tagId) 1922 { 1923 return _posts.containsKey(tagId); 1924 } 1925 1926 /** 1927 * Get the years with posts. 1928 * @return the years with posts as a Set of Integer. 1929 */ 1930 public Set<String> getTags() 1931 { 1932 return _posts.keySet(); 1933 } 1934 1935 /** 1936 * Get the posts for a year-month couple. 1937 * @param tagId the year. 1938 * @return the posts in the specified year and month. 1939 */ 1940 public Map<String, Post> getPosts(String tagId) 1941 { 1942 Map<String, Post> posts = new HashMap<>(); 1943 1944 if (_posts.containsKey(tagId)) 1945 { 1946 posts = _posts.get(tagId); 1947 } 1948 1949 return posts; 1950 } 1951 1952 /** 1953 * Add a post to the cache. 1954 * @param content The content 1955 * @param tagId the tag ID. 1956 */ 1957 public void addPost(Content content, String tagId) 1958 { 1959 Map<String, Post> posts = getOrCreatePostMap(tagId); 1960 1961 Post post = new Post(); 1962 post.setId(content.getId()); 1963 post.setName(content.getName()); 1964 post.setDate(content.getValue(POST_DATE_ATTRIBUTE)); 1965 post.setTags(content.getValue(POST_TAGS_ATTRIBUTE, false, new String[0])); 1966 1967 posts.put(post.getId(), post); 1968 } 1969 1970 /** 1971 * Add a post to the cache. 1972 * @param post the post 1973 * @param tagId the tag ID. 1974 */ 1975 public void addPost(Post post, String tagId) 1976 { 1977 Map<String, Post> posts = getOrCreatePostMap(tagId); 1978 posts.put(post.getId(), post); 1979 } 1980 1981 /** 1982 * Remove a post from the cache. 1983 * @param contentId The id of content 1984 * @param tagId the id of tag 1985 */ 1986 public void removePost(String contentId, String tagId) 1987 { 1988 if (_posts.containsKey(tagId)) 1989 { 1990 Map<String, Post> posts = _posts.get(tagId); 1991 1992 posts.remove(contentId); 1993 if (posts.isEmpty()) 1994 { 1995 _posts.remove(tagId); 1996 } 1997 } 1998 } 1999 2000 /** 2001 * Test if the post with the given ID exists for this year and month. 2002 * @param tagId the tag id. 2003 * @param id the post ID. 2004 * @return the posts in the specified year and month. 2005 */ 2006 public Post getPost(String tagId, String id) 2007 { 2008 return getPosts(tagId).get(id); 2009 } 2010 2011 /** 2012 * Test if the post with the given ID exists for this year and month. 2013 * @param tagId the tag id. 2014 * @param id the post ID. 2015 * @return the posts in the specified year and month. 2016 */ 2017 public boolean hasPost(String tagId, String id) 2018 { 2019 return getPosts(tagId).containsKey(id); 2020 } 2021 2022 /** 2023 * Get the posts for a year-month couple. 2024 * @param tagId the year. 2025 * @param name The name of post 2026 * @return the posts in the specified year and month. 2027 */ 2028 public Post getPostByName(String tagId, String name) 2029 { 2030 Post post = null; 2031 2032 Iterator<Post> posts = getPosts(tagId).values().iterator(); 2033 while (posts.hasNext() && post == null) 2034 { 2035 Post currentPost = posts.next(); 2036 if (currentPost.getName().equals(name)) 2037 { 2038 post = currentPost; 2039 } 2040 } 2041 2042 return post; 2043 } 2044 2045 /** 2046 * Get the posts for a year-month couple. 2047 * @param tagId the year. 2048 * @param name the name of post 2049 * @return the posts in the specified year and month. 2050 */ 2051 public boolean hasPostByName(String tagId, String name) 2052 { 2053 boolean hasPost = false; 2054 2055 Iterator<Post> posts = getPosts(tagId).values().iterator(); 2056 while (posts.hasNext() && !hasPost) 2057 { 2058 hasPost = posts.next().getName().equals(name); 2059 } 2060 2061 return hasPost; 2062 } 2063 2064 /** 2065 * Clear the cache. 2066 */ 2067 public void clear() 2068 { 2069 _posts.clear(); 2070 } 2071 2072 /** 2073 * Get or create the post set for the specified year and month. 2074 * @param tagId the year. 2075 * @return the post set, indexed by id. 2076 */ 2077 protected Map<String, Post> getOrCreatePostMap(String tagId) 2078 { 2079 Map<String, Post> posts; 2080 2081 if (_posts.containsKey(tagId)) 2082 { 2083 posts = _posts.get(tagId); 2084 } 2085 else 2086 { 2087 posts = new LinkedHashMap<>(); 2088 _posts.put(tagId, posts); 2089 } 2090 2091 return posts; 2092 } 2093 2094 } 2095 2096 /** 2097 * Cache of all the posts. 2098 */ 2099 protected static class AllPostCache 2100 { 2101 2102 /** The posts, indexed by ID. */ 2103 private Map<String, Post> _posts; 2104 2105 /** The posts, indexed by name. */ 2106 private Map<String, Post> _postsByName; 2107 2108 /** 2109 * Construct a AllPostCache instance. 2110 */ 2111 public AllPostCache() 2112 { 2113 _posts = new LinkedHashMap<>(); 2114 _postsByName = new LinkedHashMap<>(); 2115 } 2116 2117 /** 2118 * Get a list of all the posts' IDs. 2119 * @return all the posts' IDs as a Set. 2120 */ 2121 public Set<String> getPostIds() 2122 { 2123 return Collections.unmodifiableSet(_posts.keySet()); 2124 } 2125 2126 /** 2127 * Get the exhaustive list of Posts. 2128 * @return the exhaustive list of Posts. 2129 */ 2130 public Map<String, Post> getPosts() 2131 { 2132 return Collections.unmodifiableMap(_posts); 2133 } 2134 2135 /** 2136 * Test if a post exists, provided its ID. 2137 * @param id the post content ID. 2138 * @return true if the post exists. 2139 */ 2140 public Post getPost(String id) 2141 { 2142 return _posts.get(id); 2143 } 2144 2145 /** 2146 * Test if a post exists, provided its ID. 2147 * @param id the post content ID. 2148 * @return true if the post exists. 2149 */ 2150 public boolean hasPost(String id) 2151 { 2152 return _posts.containsKey(id); 2153 } 2154 2155 /** 2156 * Test if a post exists, provided its content name. 2157 * @param name the post content name. 2158 * @return true if the post exists. 2159 */ 2160 public Post getPostByName(String name) 2161 { 2162 return _postsByName.get(name); 2163 } 2164 2165 /** 2166 * Test if a post exists, provided its content name. 2167 * @param name the post content name. 2168 * @return true if the post exists. 2169 */ 2170 public boolean hasPostByName(String name) 2171 { 2172 return _postsByName.containsKey(name); 2173 } 2174 2175 /** 2176 * Add a post to the cache. 2177 * @param content the post content. 2178 */ 2179 public void addPost(Content content) 2180 { 2181 Post post = new Post(); 2182 post.setId(content.getId()); 2183 post.setName(content.getName()); 2184 if (content.hasValue(POST_DATE_ATTRIBUTE)) 2185 { 2186 post.setDate(content.getValue(POST_DATE_ATTRIBUTE)); 2187 } 2188 post.setTags(content.getValue(POST_TAGS_ATTRIBUTE, false, new String[0])); 2189 2190 _posts.put(post.getId(), post); 2191 _postsByName.put(post.getName(), post); 2192 } 2193 2194 /** 2195 * Add a post to the cache. 2196 * @param post The post 2197 */ 2198 public void addPost(Post post) 2199 { 2200 _posts.put(post.getId(), post); 2201 _postsByName.put(post.getName(), post); 2202 } 2203 2204 /** 2205 * Remove a post from the cache. 2206 * @param content the post content to remove from the cache. 2207 */ 2208 public void removePost(Content content) 2209 { 2210 removePost(content.getId(), content.getName()); 2211 } 2212 2213 /** 2214 * Remove a post from the cache. 2215 * @param id The id of post 2216 * @param name The name of post 2217 */ 2218 public void removePost(String id, String name) 2219 { 2220 _posts.remove(id); 2221 _postsByName.remove(name); 2222 } 2223 2224 /** 2225 * Clear the cache. 2226 */ 2227 public void clear() 2228 { 2229 _posts.clear(); 2230 _postsByName.clear(); 2231 } 2232 } 2233 2234 /** 2235 * Cache of identifier of the future posts. 2236 */ 2237 protected static class FuturePostCache 2238 { 2239 /** The posts, indexed by ID. */ 2240 private Map<String, Post> _posts; 2241 2242 /** The posts id, indexed by date. */ 2243 private Multimap<ZonedDateTime, String> _sortedPostIds; 2244 2245 /** 2246 * Construct a AllPostCache instance. 2247 */ 2248 public FuturePostCache() 2249 { 2250 _posts = new HashMap<>(); 2251 _sortedPostIds = TreeMultimap.create(); 2252 } 2253 2254 /** 2255 * Remove the post that are not future anymore (given the current date) 2256 * @return The collection of past posts 2257 */ 2258 public Collection<Post> removePastPosts() 2259 { 2260 Set<Post> pastPosts = new HashSet<>(); 2261 2262 Set<ZonedDateTime> sortedDates = _sortedPostIds.keySet(); 2263 Iterator<ZonedDateTime> iterator = sortedDates.iterator(); 2264 boolean isFuture = false; 2265 ZonedDateTime now = ZonedDateTime.now(); 2266 2267 while (iterator.hasNext() && !isFuture) 2268 { 2269 ZonedDateTime date = iterator.next(); 2270 isFuture = date.isAfter(now); 2271 2272 if (!isFuture) 2273 { 2274 Collection<String> pastPostIds = _sortedPostIds.get(date); 2275 for (String id : pastPostIds) 2276 { 2277 Post pastPost = _posts.get(id); 2278 if (pastPost != null) 2279 { 2280 pastPosts.add(pastPost); 2281 } 2282 } 2283 } 2284 } 2285 2286 for (Post pastPost : pastPosts) 2287 { 2288 removePost(pastPost); 2289 } 2290 2291 return pastPosts; 2292 } 2293 2294 /** 2295 * Test if a post exists, provided its ID. 2296 * @param id the post content ID. 2297 * @return true if the post exists. 2298 */ 2299 public Post getPost(String id) 2300 { 2301 return _posts.get(id); 2302 } 2303 2304 /** 2305 * Test if a post exists, provided its ID. 2306 * @param id the post content ID. 2307 * @return true if the post exists. 2308 */ 2309 public boolean hasPost(String id) 2310 { 2311 return _posts.containsKey(id); 2312 } 2313 2314 /** 2315 * Add a post to the cache. 2316 * @param content the post content. 2317 */ 2318 public void addPost(Content content) 2319 { 2320 String id = content.getId(); 2321 2322 if (_posts.containsKey(id)) 2323 { 2324 removePost(content); 2325 } 2326 2327 ZonedDateTime date = content.getValue(POST_DATE_ATTRIBUTE); 2328 2329 Post post = new Post(); 2330 post.setId(id); 2331 post.setName(content.getName()); 2332 post.setDate(date); 2333 post.setTags(content.getValue(POST_TAGS_ATTRIBUTE, false, new String[0])); 2334 2335 _posts.put(post.getId(), post); 2336 _sortedPostIds.put(date, id); 2337 } 2338 2339 /** 2340 * Remove a post from the cache. 2341 * @param content the post content to remove from the cache. 2342 */ 2343 public void removePost(Content content) 2344 { 2345 Post post = _posts.remove(content.getId()); 2346 if (post != null) 2347 { 2348 _sortedPostIds.remove(post.getDate(), post.getId()); 2349 } 2350 } 2351 2352 /** 2353 * Remove a post from the cache. 2354 * @param post the post content to remove from the cache. 2355 */ 2356 public void removePost(Post post) 2357 { 2358 _posts.remove(post.getId()); 2359 _sortedPostIds.remove(post.getDate(), post.getId()); 2360 } 2361 2362 /** 2363 * Clear the cache. 2364 */ 2365 public void clear() 2366 { 2367 _posts.clear(); 2368 _sortedPostIds.clear(); 2369 } 2370 } 2371 2372 /** 2373 * Reverse date post comparator. 2374 */ 2375 public class ReverseDatePostComparator implements Comparator<Post> 2376 { 2377 @Override 2378 public int compare(Post p1, Post p2) 2379 { 2380 if (p1.getDate() == null) 2381 { 2382 return 1; 2383 } 2384 if (p2.getDate() == null) 2385 { 2386 return -1; 2387 } 2388 return p2.getDate().compareTo(p1.getDate()); 2389 } 2390 } 2391 2392 static class BlogPageElementKey extends AbstractCacheKey 2393 { 2394 BlogPageElementKey(String workspace, String siteName, String sitemap) 2395 { 2396 super(workspace, siteName, sitemap); 2397 } 2398 2399 static BlogPageElementKey of(String workspace, String site, String sitemap) 2400 { 2401 return new BlogPageElementKey(workspace, site, sitemap); 2402 } 2403 2404 static BlogPageElementKey of(String workspace, String site) 2405 { 2406 return new BlogPageElementKey(workspace, site, null); 2407 } 2408 2409 static BlogPageElementKey of(String workspace) 2410 { 2411 return of(workspace, null); 2412 } 2413 } 2414 2415 private Cache<BlogPageElementKey, PostDateCache> _getPostByDateCache() 2416 { 2417 return this._cacheManager.get(__POST_BY_DATE_CACHE); 2418 } 2419 2420 private Cache<BlogPageElementKey, PostTagCache> _getPostByTagCache() 2421 { 2422 return this._cacheManager.get(__POST_BY_TAG_CACHE); 2423 } 2424 2425 private Cache<BlogPageElementKey, AllPostCache> _getAllPosts() 2426 { 2427 return this._cacheManager.get(__ALL_POSTS_CACHE); 2428 } 2429 2430 private Cache<BlogPageElementKey, FuturePostCache> _getFuturePosts() 2431 { 2432 return this._cacheManager.get(__FUTURE_POSTS_CACHE); 2433 } 2434 2435 private Cache<BlogPageElementKey, Date> _getLastUpdateFuturePosts() 2436 { 2437 return this._cacheManager.get(__LAST_UPDATE_FUTURE_POST_CACHE); 2438 } 2439}