001/*
002 *  Copyright 2025 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.userdirectory.observation;
017
018import java.util.Map;
019import java.util.Objects;
020import java.util.Set;
021import java.util.stream.Collectors;
022
023import org.apache.avalon.framework.service.ServiceException;
024import org.apache.avalon.framework.service.ServiceManager;
025import org.apache.avalon.framework.service.Serviceable;
026
027import org.ametys.cms.ObservationConstants;
028import org.ametys.cms.contenttype.ContentTypesHelper;
029import org.ametys.cms.repository.Content;
030import org.ametys.core.observation.Event;
031import org.ametys.core.observation.Observer;
032import org.ametys.plugins.repository.AmetysObjectIterable;
033import org.ametys.plugins.repository.AmetysObjectResolver;
034import org.ametys.plugins.userdirectory.UserDirectoryHelper;
035import org.ametys.plugins.userdirectory.cachepolicy.InsertUserServiceCachePolicy;
036import org.ametys.runtime.plugin.component.AbstractLogEnabled;
037import org.ametys.web.cache.CacheInvalidationPolicy;
038import org.ametys.web.repository.page.Page;
039import org.ametys.web.repository.page.ZoneItem;
040import org.ametys.web.repository.site.Site;
041
042/**
043 * {@link Observer} for observing user content validation, for content that are displayed, in order to invalidate cache on front-office.
044 */
045public class InvalidateCacheOnDisplayedContentValidationObserver extends AbstractLogEnabled implements Observer, Serviceable
046{
047    /** Cache invalidation policy */
048    protected CacheInvalidationPolicy _cachePolicy;
049    /** The content types helper */
050    protected ContentTypesHelper _contentTypesHelper;
051    /** The Ametys object resolver */
052    protected AmetysObjectResolver _resolver;
053    
054    @Override
055    public void service(ServiceManager manager) throws ServiceException
056    {
057        _cachePolicy = (CacheInvalidationPolicy) manager.lookup(CacheInvalidationPolicy.ROLE);
058        _contentTypesHelper = (ContentTypesHelper) manager.lookup(ContentTypesHelper.ROLE);
059        _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE);
060    }
061    
062    @Override
063    public boolean supports(Event event)
064    {
065        String eventId = event.getId();
066        // If event is validation
067        if (eventId.equals(ObservationConstants.EVENT_CONTENT_VALIDATED) || eventId.equals(ObservationConstants.EVENT_CONTENT_DELETED))
068        {
069            // And if the event is about a user, then it is supported
070            Object object = event.getArguments().get(ObservationConstants.ARGS_CONTENT);
071            if (object != null && object instanceof Content content)
072            {
073                return _contentTypesHelper.isInstanceOf(content, UserDirectoryHelper.ABSTRACT_USER_CONTENT_TYPE);
074            }
075        }
076        
077        // If the event is not a validation or the content is not a user, the event is not supported
078        return false;
079    }
080    
081    /**
082     * Get the sites where the user is displayed
083     * @param contentId The Id of the user content
084     * @return The list of sites
085     */
086    protected Set<Site> _getSitesWhereUserContentIsDisplayed(String contentId)
087    {
088        // Retrieve the zone item that are displaying a user content
089        String xpathQuery = "//element(*, ametys:zoneItem)[@ametys-internal:service='" + InsertUserServiceCachePolicy.SERVICE_INSERT_USER_ID + "' and ametys:service_parameters/@ametys:contentId='" + contentId + "']";
090        
091        AmetysObjectIterable<ZoneItem> zoneItems = _resolver.query(xpathQuery);
092        
093        return zoneItems.stream()
094                        .filter(Objects::nonNull)
095                        .map(zoneItem -> zoneItem.getZone().getSitemapElement())
096                        .filter(sitemapElem -> sitemapElem instanceof Page)
097                        .map(page -> page.getSite())
098                        .collect(Collectors.toSet());
099    }
100    
101    @Override
102    public int getPriority()
103    {
104        // Will be processed after indexation
105        return MAX_PRIORITY + 4000;
106    }
107    
108    @Override
109    public void observe(Event event, Map<String, Object> transientVars)
110    {
111        String eventId = event.getId();
112        if (eventId.equals(ObservationConstants.EVENT_CONTENT_VALIDATED))
113        {
114            Content content = (Content) event.getArguments().get(ObservationConstants.ARGS_CONTENT);
115
116            Set<Site>  sites = _getSitesWhereUserContentIsDisplayed(content.getId());
117            for (Site site : sites)
118            {
119                try
120                {
121                    _cachePolicy.invalidateCacheOnContentModification(site, content);
122                }
123                catch (Exception e)
124                {
125                    getLogger().error("Unable to invalidate cache for displayed user content " + content, e);
126                }
127            }
128        }
129        else if (eventId.equals(ObservationConstants.EVENT_CONTENT_DELETED))
130        {
131            String contentId = (String) event.getArguments().get(ObservationConstants.ARGS_CONTENT_ID);
132            Set<Site>  sites = _getSitesWhereUserContentIsDisplayed(contentId);
133            for (Site site : sites)
134            {
135                try
136                {
137                    _cachePolicy.invalidateCacheOnContentDeletion(site, contentId);
138                }
139                catch (Exception e)
140                {
141                    getLogger().error("Unable to invalidate cache for displayed user content " + contentId, e);
142                }
143            }
144        }
145    }
146}