001/*
002 *  Copyright 2016 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 */
016
017package org.ametys.plugins.userdirectory.observation;
018
019import java.util.Map;
020
021import org.apache.avalon.framework.context.ContextException;
022import org.apache.avalon.framework.context.Contextualizable;
023import org.apache.avalon.framework.service.ServiceException;
024import org.apache.avalon.framework.service.ServiceManager;
025import org.apache.avalon.framework.service.Serviceable;
026import org.apache.cocoon.environment.Context;
027import org.apache.commons.lang3.ArrayUtils;
028
029import org.ametys.cms.ObservationConstants;
030import org.ametys.cms.contenttype.ContentTypesHelper;
031import org.ametys.cms.repository.Content;
032import org.ametys.core.observation.Event;
033import org.ametys.core.observation.Observer;
034import org.ametys.plugins.repository.AmetysObjectIterable;
035import org.ametys.plugins.repository.AmetysObjectResolver;
036import org.ametys.plugins.repository.query.expression.Expression;
037import org.ametys.plugins.repository.query.expression.VirtualFactoryExpression;
038import org.ametys.plugins.userdirectory.UserDirectoryHelper;
039import org.ametys.plugins.userdirectory.UserDirectoryPageHandler;
040import org.ametys.plugins.userdirectory.page.OrgUnitPage;
041import org.ametys.plugins.userdirectory.page.OrganisationChartPageResolver;
042import org.ametys.plugins.userdirectory.page.UserDirectoryPageResolver;
043import org.ametys.plugins.userdirectory.page.UserPage;
044import org.ametys.plugins.userdirectory.page.VirtualOrganisationChartPageFactory;
045import org.ametys.plugins.userdirectory.page.VirtualUserDirectoryPageFactory;
046import org.ametys.runtime.plugin.component.AbstractLogEnabled;
047import org.ametys.web.cache.pageelement.PageElementCache;
048import org.ametys.web.repository.page.Page;
049import org.ametys.web.repository.page.PageQueryHelper;
050import org.ametys.web.repository.page.Zone;
051import org.ametys.web.repository.page.ZoneItem;
052import org.ametys.web.repository.page.ZoneItem.ZoneType;
053
054/**
055 * Abstract {@link Observer} for observing validation of User content.
056 */
057public abstract class AbstractContentObserver extends AbstractLogEnabled implements Observer, Serviceable, Contextualizable
058{
059    /** The context. */
060    protected org.apache.avalon.framework.context.Context _context;
061    /** Cocoon context. */
062    protected Context _cocoonContext;
063    /** Ametys object resolver. */
064    protected AmetysObjectResolver _resolver;
065    /** The content type helper */
066    protected ContentTypesHelper _contentTypeHelper;
067    /** The page element cache */
068    protected PageElementCache _zoneItemCache;
069    /** The resolver for user directory pages */
070    protected UserDirectoryPageResolver _userDirectoryPageResolver;
071    /** The resolver for ud orgunits pages */
072    protected OrganisationChartPageResolver _organisationChartPageResolver;
073
074    @Override
075    public void contextualize(org.apache.avalon.framework.context.Context context) throws ContextException
076    {
077        _context = context;
078        _cocoonContext = (Context) context.get(org.apache.cocoon.Constants.CONTEXT_ENVIRONMENT_CONTEXT);
079    }
080
081    @Override
082    public void service(ServiceManager manager) throws ServiceException
083    {
084        _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE);
085        _contentTypeHelper = (ContentTypesHelper) manager.lookup(ContentTypesHelper.ROLE);
086        _zoneItemCache = (PageElementCache) manager.lookup(PageElementCache.ROLE + "/zoneItem");
087        _userDirectoryPageResolver = (UserDirectoryPageResolver) manager.lookup(UserDirectoryPageResolver.ROLE);
088        _organisationChartPageResolver = (OrganisationChartPageResolver) manager.lookup(OrganisationChartPageResolver.ROLE);
089    }
090
091    @Override
092    public void observe(Event event, Map<String, Object> transientVars)
093    {
094        try
095        {
096            Content content = _getTarget(event);
097            if (_isUserContent(content))
098            {
099                AmetysObjectIterable<Page> rootPages = _getUserRootPages ();
100                if (!rootPages.iterator().hasNext())
101                {
102                    getLogger().debug("There's no user root page, nothing to invalidate");
103                    return;
104                }
105                
106                for (Page rootPage : rootPages)
107                {
108                    String cType = rootPage.getValue(UserDirectoryPageHandler.CONTENT_TYPE_DATA_NAME);
109                    if (ArrayUtils.contains(content.getTypes(), cType))
110                    {
111                        _internalObserve(event, rootPage, content);
112                    }
113                }
114            }
115            else if (_isOrgUnitContent(content))
116            {
117                AmetysObjectIterable<Page> rootPages = _getOrgUnitRootPages ();
118                if (!rootPages.iterator().hasNext())
119                {
120                    getLogger().debug("There's no orgUnit root page, nothing to invalidate");
121                    return;
122                }
123                
124                for (Page rootPage : rootPages)
125                {
126                    _internalObserve(event, rootPage, content);
127                }
128            }
129        }
130        catch (Exception e)
131        {
132            getLogger().error("Unable to observe event: " + event, e);
133        }
134    }
135    
136    /**
137     * Do the actual work.
138     * @param event the observation event.
139     * @param rootUsersPage the page holding the virtual user pages
140     * @param userContent a list containing all impacted user contents.
141     */
142    protected abstract void _internalObserve(Event event, Page rootUsersPage, Content userContent);
143    
144    /**
145     * Get the user root pages
146     * @return the user root pages
147     */
148    protected AmetysObjectIterable<Page> _getUserRootPages ()
149    {
150        Expression expression = new VirtualFactoryExpression(VirtualUserDirectoryPageFactory.class.getName());
151        String query = PageQueryHelper.getPageXPathQuery(null, null, null, expression, null);
152        
153        return _resolver.query(query);
154    }
155    
156    /**
157     * Get the orgUnit root pages
158     * @return the orgUnit root pages
159     */
160    protected AmetysObjectIterable<Page> _getOrgUnitRootPages ()
161    {
162        Expression expression = new VirtualFactoryExpression(VirtualOrganisationChartPageFactory.class.getName());
163        String query = PageQueryHelper.getPageXPathQuery(null, null, null, expression, null);
164        
165        return _resolver.query(query);
166    }
167    
168    /**
169     * Retrieve the target of the observer
170     * @param event The event
171     * @return The target
172     * @throws Exception if failed to get content
173     */
174    protected Content _getTarget(Event event) throws Exception
175    {
176        return (Content) event.getArguments().get(ObservationConstants.ARGS_CONTENT);
177    }
178    
179    /**
180     * Return true if the content is a user content
181     * @param content the content
182     * @return true if the content is a user content
183     */
184    protected boolean _isUserContent(Content content)
185    {
186        return content != null && _contentTypeHelper.isInstanceOf(content, UserDirectoryHelper.ABSTRACT_USER_CONTENT_TYPE);
187    }
188    
189    /**
190     * Return true if the content is a orgUnit content
191     * @param content the content
192     * @return true if the content is a orgUnit content
193     */
194    protected boolean _isOrgUnitContent(Content content)
195    {
196        return content != null && _contentTypeHelper.isInstanceOf(content, UserDirectoryHelper.ORGUNIT_CONTENT_TYPE);
197    }
198    
199    /**
200     * Remove zone item cache
201     * @param rootPage the root page
202     * @param content the content 
203     * @param workspace the workspace
204     */
205    protected void _removeZoneItemCache(Page rootPage, Content content, String workspace)
206    {
207        if (_isUserContent(content))
208        {
209            UserPage userPage = _userDirectoryPageResolver.getUserPage(rootPage, content);
210            if (userPage != null)
211            {
212                AmetysObjectIterable< ? extends Zone> zones = userPage.getZones();
213                for (Zone zone : zones)
214                {
215                    for (ZoneItem zoneItem : zone.getZoneItems())
216                    {
217                        if (zoneItem.getType().equals(ZoneType.CONTENT)) 
218                        {
219                            _zoneItemCache.removeItem(workspace, rootPage.getSiteName(), "CONTENT", zoneItem.getId());
220                        }
221                    }
222                }
223            }
224        }
225        else if (_isOrgUnitContent(content))
226        {
227            OrgUnitPage orgUnitPage = _organisationChartPageResolver.getOrgUnitPage(rootPage, content);
228            if (orgUnitPage != null)
229            {
230                AmetysObjectIterable< ? extends Zone> zones = orgUnitPage.getZones();
231                for (Zone zone : zones)
232                {
233                    for (ZoneItem zoneItem : zone.getZoneItems())
234                    {
235                        if (zoneItem.getType().equals(ZoneType.CONTENT)) 
236                        {
237                            _zoneItemCache.removeItem(workspace, rootPage.getSiteName(), "CONTENT", zoneItem.getId());
238                        }
239                    }
240                }
241            }
242        }
243    }
244}