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.cache;
017
018import java.util.ArrayList;
019import java.util.Collection;
020import java.util.Iterator;
021import java.util.List;
022
023import org.apache.avalon.framework.component.Component;
024import org.apache.avalon.framework.logger.AbstractLogEnabled;
025import org.apache.avalon.framework.service.ServiceException;
026import org.apache.avalon.framework.service.ServiceManager;
027import org.apache.avalon.framework.service.Serviceable;
028
029import org.ametys.cms.repository.Content;
030import org.ametys.core.right.RightManager;
031import org.ametys.plugins.repository.AmetysObjectIterable;
032import org.ametys.web.inputdata.InputData;
033import org.ametys.web.inputdata.InputDataExtensionPoint;
034import org.ametys.web.repository.page.Page;
035import org.ametys.web.repository.page.Page.PageType;
036import org.ametys.web.repository.page.Zone;
037import org.ametys.web.repository.page.ZoneItem;
038import org.ametys.web.repository.page.ZoneItem.ZoneType;
039import org.ametys.web.repository.site.Site;
040import org.ametys.web.service.Service;
041import org.ametys.web.service.ServiceExtensionPoint;
042
043/**
044 * Helper component providing status of pages.
045 */
046public class PageHelper extends AbstractLogEnabled implements Component, Serviceable
047{
048    /** The component role. */
049    public static final String ROLE = PageHelper.class.getName();
050    
051    private ServiceExtensionPoint _serviceExtPt;
052    private RightManager _rightManager;
053    private InputDataExtensionPoint _inputDataExtensionPoint;
054    
055    @Override
056    public void service(ServiceManager serviceManager) throws ServiceException
057    {
058        _serviceExtPt = (ServiceExtensionPoint) serviceManager.lookup(ServiceExtensionPoint.ROLE);
059        _rightManager = (RightManager) serviceManager.lookup(RightManager.ROLE);
060        _inputDataExtensionPoint = (InputDataExtensionPoint) serviceManager.lookup(InputDataExtensionPoint.ROLE);
061    }
062    
063    /**
064     * Tests if a page is cacheable, i.e:<ul>
065     * <li>its access is not restricted</li>
066     * <li>all its page elements are cacheable</li>
067     * </ul>
068     * @param page The page to test.
069     * @return true if the page is cacheable, false otherwise.
070     */
071    public boolean isCacheable(Page page)
072    {
073        String debugStringPrefix = "";
074        if (getLogger().isDebugEnabled())
075        {
076            debugStringPrefix = "Page " + page.getSiteName() + "/" + page.getSitemapName() + "/" + page .getTitle() + " (" +  page.getId() + ") is ";
077        }
078        
079        if (!_rightManager.hasAnonymousReadAccess(page))
080        {
081            // never cache a private page
082            getLogger().debug(debugStringPrefix + "not cacheable: access is limited");
083            return false;
084        }
085        
086        if (page.getType() != PageType.CONTAINER)
087        {
088            // makes no sense to cache a Node or link page
089            getLogger().debug(debugStringPrefix + "not cacheable: not a container page");
090            return false;
091        }
092        
093        AmetysObjectIterable<? extends Zone> zones = page.getZones();
094        Iterator<? extends Zone> itZ = zones.iterator();
095        
096        // test each service of the page
097        while (itZ.hasNext())
098        {
099            Zone zone = itZ.next();
100            AmetysObjectIterable<? extends ZoneItem> zoneItems = zone.getZoneItems();
101            Iterator<? extends ZoneItem> itZI = zoneItems.iterator();
102            
103            while (itZI.hasNext())
104            {
105                ZoneItem zoneItem = itZI.next();
106                if (zoneItem.getType() == ZoneType.SERVICE)
107                {
108                    Service service = _serviceExtPt.getExtension(zoneItem.getServiceId());
109                    try
110                    {
111                        // null service is cacheable
112                        if (service != null && !service.isCacheable(page, zoneItem))
113                        {
114                            getLogger().debug(debugStringPrefix + "not cacheable: due to service " + zoneItem.getServiceId() + " (" + zoneItem.getId() + ")");
115                            return false;
116                        }
117                    }
118                    catch (Exception e)
119                    {
120                        // Ignore
121                    }
122                }
123            }
124        }
125        
126        // Test each inputdata
127        Collection<String> ids = _inputDataExtensionPoint.getExtensionsIds();
128        Iterator<String> it = ids.iterator();
129        while (it.hasNext())
130        {
131            String id = it.next();
132            InputData inputData = _inputDataExtensionPoint.getExtension(id);
133            if (!inputData.isCacheable(page.getSite(), page))
134            {
135                getLogger().debug(debugStringPrefix + "not cacheable: due to inputdata " + id);
136                return false;
137            }
138        }
139
140        getLogger().debug(debugStringPrefix + "cacheable");
141        return true;
142    }
143    
144    /**
145     * Determines if a captcha is required on forms of the page
146     * @param page The page to test
147     * @return  true if a captcha is required
148     */
149    public boolean isCaptchaRequired (Page page)
150    {
151        Site site = page.getSite();
152        
153        String captchaPolicy = site.getValue("display-captcha-policy");
154        
155        if (captchaPolicy == null || "restricted".equals(captchaPolicy)) 
156        {
157            // Display captcha on public page only
158            return _rightManager.hasAnonymousReadAccess(page); 
159        }
160        
161        return "always".equals(captchaPolicy);
162    }
163
164    /**
165     * Get all the contents inside a page
166     * @param page page to search
167     * @return list of contents in this page
168     */
169    public List<Content> getAllContents (Page page)
170    {
171        List<Content> result = new ArrayList<>();
172        AmetysObjectIterable< ? extends Zone> zones = page.getZones();
173        for (Zone zone : zones)
174        {
175            AmetysObjectIterable< ? extends ZoneItem> zoneItems = zone.getZoneItems();
176            for (ZoneItem zoneItem : zoneItems)
177            {
178                if (ZoneType.CONTENT.equals(zoneItem.getType()))
179                {
180                    Content content = zoneItem.getContent();
181                    result.add(content);
182                }
183            }
184        }
185        return result;
186    }
187
188}