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