001/*
002 *  Copyright 2010 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.site;
017
018import java.io.IOException;
019import java.util.ArrayList;
020import java.util.List;
021import java.util.Set;
022import java.util.regex.Matcher;
023import java.util.regex.Pattern;
024
025import org.apache.avalon.framework.service.ServiceException;
026import org.apache.avalon.framework.service.ServiceManager;
027import org.apache.cocoon.ProcessingException;
028import org.apache.cocoon.generation.ServiceableGenerator;
029import org.apache.cocoon.xml.AttributesImpl;
030import org.apache.cocoon.xml.XMLUtils;
031import org.xml.sax.SAXException;
032
033import org.ametys.cms.transformation.xslt.ResolveURIComponent;
034import org.ametys.core.user.directory.UserDirectory;
035import org.ametys.core.user.population.PopulationContextHelper;
036import org.ametys.plugins.repository.AmetysObjectIterable;
037import org.ametys.web.renderingcontext.RenderingContext;
038import org.ametys.web.renderingcontext.RenderingContextHandler;
039import org.ametys.web.repository.page.Page;
040import org.ametys.web.repository.page.Zone;
041import org.ametys.web.repository.page.ZoneItem;
042import org.ametys.web.repository.page.ZoneItem.ZoneType;
043import org.ametys.web.repository.site.Site;
044import org.ametys.web.repository.site.SiteManager;
045import org.ametys.web.repository.sitemap.Sitemap;
046import org.ametys.web.usermanagement.UserSignupManager;
047
048/**
049 * SAXes sites front-office configuration.
050 */
051public class SitesGenerator extends ServiceableGenerator
052{
053    /** Pattern for sites URL. */
054    public static final Pattern URL_PATTERN = Pattern.compile("^(https?)://([a-zA-Z0-9.\\-]+)(:([0-9]+))?((/[a-zA-Z0-9\\-_.]+)*)/?$");
055    
056    private SiteManager _siteManager;
057
058    private PopulationContextHelper _populationContextHelper;
059    
060    private UserSignupManager _userSignupManager;
061    
062    private RenderingContextHandler _renderingContextHandler;
063
064    private SiteConfigurationManager _siteConfigurationManager;
065    
066    @Override
067    public void service(ServiceManager sManager) throws ServiceException
068    {
069        super.service(sManager);
070        _siteManager = (SiteManager) sManager.lookup(SiteManager.ROLE);
071        _populationContextHelper = (PopulationContextHelper) sManager.lookup(PopulationContextHelper.ROLE);
072        _userSignupManager = (UserSignupManager) sManager.lookup(UserSignupManager.ROLE);
073        _renderingContextHandler = (RenderingContextHandler) sManager.lookup(RenderingContextHandler.ROLE);
074        _siteConfigurationManager = (SiteConfigurationManager) sManager.lookup(SiteConfigurationManager.ROLE);
075    }
076    
077    @Override
078    public void generate() throws IOException, SAXException, ProcessingException
079    {
080        AmetysObjectIterable<Site> sites = _siteManager.getRootSites();
081        
082        contentHandler.startDocument();
083        XMLUtils.startElement(contentHandler, "sites");
084        
085        for (Site site : sites)
086        {
087            _saxSite(site);
088        }
089        
090        XMLUtils.endElement(contentHandler, "sites");
091        contentHandler.endDocument();
092    }
093    
094    private void _saxSite(Site site)
095    {
096        try
097        {
098            if (!_siteConfigurationManager.isSiteConfigurationValid(site))
099            {
100                 // site is invalid, ignore it
101                return;
102            }
103            
104            // check all urls BEFORE starting sax
105            for (String url : site.getUrlAliases())
106            {
107                Matcher matcher = URL_PATTERN.matcher(url);
108                
109                if (!matcher.matches())
110                {
111                    getLogger().error("The site url '" + url + "' is not valid for site " + site.getName());
112                    return;
113                }
114            }
115            
116            // Start sax
117            AttributesImpl atts = new AttributesImpl();
118            atts.addCDATAAttribute("name", site.getName());
119            atts.addCDATAAttribute("title", site.getTitle());
120            XMLUtils.startElement(contentHandler, "site", atts);
121            
122            // Sax populations
123            XMLUtils.startElement(contentHandler, "populations");
124            
125            List<String> contexts = new ArrayList<>();
126            contexts.add("/sites/" + site.getName());
127            contexts.add("/sites-fo/" + site.getName());
128            
129            Set<String> userPopulationsOnContext = _populationContextHelper.getUserPopulationsOnContexts(contexts, false, false);
130            for (String populationId : userPopulationsOnContext)
131            {
132                XMLUtils.createElement(contentHandler, "population", populationId);
133            }
134            XMLUtils.endElement(contentHandler, "populations");
135            
136            // Sax urls
137            for (String url : site.getUrlAliases())
138            {
139                Matcher matcher = URL_PATTERN.matcher(url);
140                
141                matcher.matches();
142                
143                String protocol = matcher.group(1);
144                String serverName = matcher.group(2);
145                String port = matcher.group(4);
146                String path = matcher.group(5);
147            
148                port = (port != null) ? port : ("https".equals(protocol) ? "443" : "80");
149                path = (path != null) ? path : "";
150                
151                atts = new AttributesImpl();
152                atts.addCDATAAttribute("serverName", serverName);
153                atts.addCDATAAttribute("serverPort", port);
154                atts.addCDATAAttribute("serverPath", path);
155                
156                XMLUtils.createElement(contentHandler, "url", atts);
157            }
158            
159            // Sax languages
160            XMLUtils.startElement(contentHandler, "languages");
161            for (Sitemap sitemap : site.getSitemaps())
162            {
163                XMLUtils.createElement(contentHandler, sitemap.getName());
164            }
165            XMLUtils.endElement(contentHandler, "languages");
166            
167            // Sax sign-up pages
168            _generateSignupPages(site);
169            
170            // SAX weak password urls
171            _generatePasswordWeakUrls(site);
172            
173            // Get children site
174            AmetysObjectIterable<Site> childrenSites = site.getChildrenSites();
175            for (Site child : childrenSites)
176            {
177                _saxSite(child);
178            }
179            
180            XMLUtils.endElement(contentHandler, "site");
181        }
182        catch (Exception ex)
183        {
184            throw new RuntimeException("An error occured while retrieving informations for site: " + site.getName(), ex);
185        }
186    }
187    
188    private void _generateSignupPages(Site site) throws SAXException
189    {
190        boolean publicSignupAllowed = _userSignupManager.isPublicSignupAllowed(site.getName());
191
192        AttributesImpl attr = new AttributesImpl();
193        attr.addCDATAAttribute("publicSignupAllowed", String.valueOf(publicSignupAllowed));
194        XMLUtils.startElement(contentHandler, "signupPages", attr);
195        
196        // If public sign-up is allowed
197        if (publicSignupAllowed) 
198        {
199            // For every language of the site
200            for (Sitemap sitemap : site.getSitemaps())
201            {
202                String sitemapName = sitemap.getName();
203                // Get the sign-up pages for this language and this site
204                List<Page> signupPages = _userSignupManager.getSignupPages(site.getName(), sitemapName);
205                
206                XMLUtils.startElement(contentHandler, sitemapName);
207                
208                // For every sign-up page, get the page information
209                for (Page page : signupPages)
210                {
211                    RenderingContext currentCtx = _renderingContextHandler.getRenderingContext();
212                    String pageURL;
213                    
214                    try
215                    {
216                        _renderingContextHandler.setRenderingContext(RenderingContext.FRONT);
217                        pageURL = ResolveURIComponent.resolve("page", page.getId());
218                    }
219                    finally
220                    {
221                        // Restore current context
222                        _renderingContextHandler.setRenderingContext(currentCtx);
223                    }
224                    
225                    List<UserDirectory> userDirectories = _getUserDirectory(page);
226                    if (!userDirectories.isEmpty())
227                    {
228                        XMLUtils.startElement(contentHandler, "page");
229                        
230                        XMLUtils.createElement(contentHandler, "url", pageURL);
231                        
232                        for (UserDirectory userDirectory : userDirectories)
233                        {
234                            AttributesImpl atts = new AttributesImpl();
235                            atts.addCDATAAttribute("populationId", userDirectory.getPopulationId());
236                            atts.addCDATAAttribute("id", userDirectory.getId());
237                            
238                            XMLUtils.createElement(contentHandler, "userDirectory", atts);
239                        }
240                        
241                        XMLUtils.endElement(contentHandler, "page");
242                    }
243                }
244                
245                XMLUtils.endElement(contentHandler, sitemapName);
246            }
247        }
248        
249        XMLUtils.endElement(contentHandler, "signupPages");
250    }
251    
252    private void _generatePasswordWeakUrls(Site site) throws SAXException
253    {
254        XMLUtils.startElement(contentHandler, "weakPasswordUrls");
255        for (Sitemap sitemap : site.getSitemaps())
256        {
257            String lang = sitemap.getName();
258            Page pwdChangePage = _userSignupManager.getPwdChangePage(site.getName(), lang);
259            
260            if (pwdChangePage != null)
261            {
262                // A change password page exists, it can be used in case of weak password
263                String weakPasswordUrl = "cocoon://_generate/plugins/web/frontoffice-formbasedauthentication/weak-password/" + site.getName() + "?pageId=" + pwdChangePage.getId();
264                XMLUtils.createElement(contentHandler, lang, weakPasswordUrl);
265            }
266        }
267        XMLUtils.endElement(contentHandler, "weakPasswordUrls");
268    }
269    
270    /**
271     * Get the user directories for a sign-up page
272     * @param page signup page
273     * @return the user directories
274     */
275    private List<UserDirectory> _getUserDirectory(Page page) 
276    {
277        List<UserDirectory> userDirectories = new ArrayList<>();
278        for (Zone zone : page.getZones())
279        {
280            try (AmetysObjectIterable< ? extends ZoneItem> zoneItemIt = zone.getZoneItems())
281            {
282                for (ZoneItem zoneItem : zoneItemIt) 
283                {
284                    if (zoneItem.getType() == ZoneType.SERVICE && UserSignupManager.SIGNUP_PAGE_SERVICE_ID.equals(zoneItem.getServiceId()))
285                    {
286                        UserDirectory userDirectory = _userSignupManager.getUserDirectory(zoneItem);
287                        if (userDirectory != null)
288                        {
289                            userDirectories.add(userDirectory);
290                        }
291                    }
292                }
293            }
294        }
295        return userDirectories;
296    }
297}