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}