001    /**
002     * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portlet.layoutsadmin.util;
016    
017    import com.liferay.layouts.admin.kernel.util.Sitemap;
018    import com.liferay.layouts.admin.kernel.util.SitemapURLProvider;
019    import com.liferay.layouts.admin.kernel.util.SitemapURLProviderRegistryUtil;
020    import com.liferay.portal.kernel.exception.PortalException;
021    import com.liferay.portal.kernel.language.LanguageUtil;
022    import com.liferay.portal.kernel.security.pacl.DoPrivileged;
023    import com.liferay.portal.kernel.util.DateUtil;
024    import com.liferay.portal.kernel.util.LocaleUtil;
025    import com.liferay.portal.kernel.util.StringPool;
026    import com.liferay.portal.kernel.util.StringUtil;
027    import com.liferay.portal.kernel.util.UnicodeProperties;
028    import com.liferay.portal.kernel.util.Validator;
029    import com.liferay.portal.kernel.xml.Document;
030    import com.liferay.portal.kernel.xml.Element;
031    import com.liferay.portal.kernel.xml.SAXReaderUtil;
032    import com.liferay.portal.model.Layout;
033    import com.liferay.portal.model.LayoutConstants;
034    import com.liferay.portal.service.LayoutLocalServiceUtil;
035    import com.liferay.portal.theme.ThemeDisplay;
036    import com.liferay.portal.util.PortalUtil;
037    import com.liferay.portal.util.PropsValues;
038    
039    import java.text.DateFormat;
040    
041    import java.util.Date;
042    import java.util.HashMap;
043    import java.util.List;
044    import java.util.Locale;
045    import java.util.Map;
046    
047    /**
048     * @author Jorge Ferrer
049     * @author Vilmos Papp
050     */
051    @DoPrivileged
052    public class SitemapImpl implements Sitemap {
053    
054            @Override
055            public void addURLElement(
056                    Element element, String url, UnicodeProperties typeSettingsProperties,
057                    Date modifiedDate, String canonicalURL,
058                    Map<Locale, String> alternateURLs) {
059    
060                    Element urlElement = element.addElement("url");
061    
062                    Element locElement = urlElement.addElement("loc");
063    
064                    locElement.addText(encodeXML(url));
065    
066                    if (typeSettingsProperties == null) {
067                            if (Validator.isNotNull(
068                                            PropsValues.SITES_SITEMAP_DEFAULT_CHANGE_FREQUENCY)) {
069    
070                                    Element changefreqElement = urlElement.addElement("changefreq");
071    
072                                    changefreqElement.addText(
073                                            PropsValues.SITES_SITEMAP_DEFAULT_CHANGE_FREQUENCY);
074                            }
075    
076                            if (Validator.isNotNull(
077                                            PropsValues.SITES_SITEMAP_DEFAULT_PRIORITY)) {
078    
079                                    Element priorityElement = urlElement.addElement("priority");
080    
081                                    priorityElement.addText(
082                                            PropsValues.SITES_SITEMAP_DEFAULT_PRIORITY);
083                            }
084                    }
085                    else {
086                            String changefreq = typeSettingsProperties.getProperty(
087                                    "sitemap-changefreq");
088    
089                            if (Validator.isNotNull(changefreq)) {
090                                    Element changefreqElement = urlElement.addElement("changefreq");
091    
092                                    changefreqElement.addText(changefreq);
093                            }
094                            else if (Validator.isNotNull(
095                                                    PropsValues.SITES_SITEMAP_DEFAULT_CHANGE_FREQUENCY)) {
096    
097                                    Element changefreqElement = urlElement.addElement("changefreq");
098    
099                                    changefreqElement.addText(
100                                            PropsValues.SITES_SITEMAP_DEFAULT_CHANGE_FREQUENCY);
101                            }
102    
103                            String priority = typeSettingsProperties.getProperty(
104                                    "sitemap-priority");
105    
106                            if (Validator.isNotNull(priority)) {
107                                    Element priorityElement = urlElement.addElement("priority");
108    
109                                    priorityElement.addText(priority);
110                            }
111                            else if (Validator.isNotNull(
112                                                    PropsValues.SITES_SITEMAP_DEFAULT_PRIORITY)) {
113    
114                                    Element priorityElement = urlElement.addElement("priority");
115    
116                                    priorityElement.addText(
117                                            PropsValues.SITES_SITEMAP_DEFAULT_PRIORITY);
118                            }
119                    }
120    
121                    if (modifiedDate != null) {
122                            Element modifiedDateElement = urlElement.addElement("lastmod");
123    
124                            DateFormat iso8601DateFormat = DateUtil.getISO8601Format();
125    
126                            modifiedDateElement.addText(iso8601DateFormat.format(modifiedDate));
127                    }
128    
129                    if (alternateURLs != null) {
130                            for (Map.Entry<Locale, String> entry : alternateURLs.entrySet()) {
131                                    Locale locale = entry.getKey();
132                                    String href = entry.getValue();
133    
134                                    Element alternateURLElement = urlElement.addElement(
135                                            "xhtml:link", "http://www.w3.org/1999/xhtml");
136    
137                                    alternateURLElement.addAttribute("href", href);
138                                    alternateURLElement.addAttribute(
139                                            "hreflang", LocaleUtil.toW3cLanguageId(locale));
140                                    alternateURLElement.addAttribute("rel", "alternate");
141                            }
142    
143                            Element alternateURLElement = urlElement.addElement(
144                                    "xhtml:link", "http://www.w3.org/1999/xhtml");
145    
146                            alternateURLElement.addAttribute("rel", "alternate");
147                            alternateURLElement.addAttribute("hreflang", "x-default");
148                            alternateURLElement.addAttribute("href", canonicalURL);
149                    }
150            }
151    
152            @Override
153            public String encodeXML(String input) {
154                    return StringUtil.replace(
155                            input,
156                            new String[] {"&", "<", ">", "'", "\""},
157                            new String[] {"&amp;", "&lt;", "&gt;", "&apos;", "&quot;"});
158            }
159    
160            @Override
161            public Map<Locale, String> getAlternateURLs(
162                            String canonicalURL, ThemeDisplay themeDisplay, Layout layout)
163                    throws PortalException {
164    
165                    Map<Locale, String> alternateURLs = new HashMap<>();
166    
167                    for (Locale availableLocale : LanguageUtil.getAvailableLocales(
168                                    layout.getGroupId())) {
169    
170                            String alternateURL = PortalUtil.getAlternateURL(
171                                    canonicalURL, themeDisplay, availableLocale, layout);
172    
173                            alternateURLs.put(availableLocale, alternateURL);
174                    }
175    
176                    return alternateURLs;
177            }
178    
179            @Override
180            public String getSitemap(
181                            long groupId, boolean privateLayout, ThemeDisplay themeDisplay)
182                    throws PortalException {
183    
184                    Document document = SAXReaderUtil.createDocument();
185    
186                    document.setXMLEncoding(StringPool.UTF8);
187    
188                    Element rootElement = document.addElement(
189                            "urlset", "http://www.sitemaps.org/schemas/sitemap/0.9");
190    
191                    rootElement.addAttribute("xmlns:xhtml", "http://www.w3.org/1999/xhtml");
192    
193                    List<Layout> layouts = LayoutLocalServiceUtil.getLayouts(
194                            groupId, privateLayout, LayoutConstants.DEFAULT_PARENT_LAYOUT_ID);
195    
196                    List<SitemapURLProvider> sitemapURLProviders =
197                            SitemapURLProviderRegistryUtil.getSitemapURLProviders();
198    
199                    visitLayouts(rootElement, layouts, sitemapURLProviders, themeDisplay);
200    
201                    return document.asXML();
202            }
203    
204            protected void visitLayouts(
205                            Element element, List<Layout> layouts,
206                            List<SitemapURLProvider> sitemapURLProviders,
207                            ThemeDisplay themeDisplay)
208                    throws PortalException {
209    
210                    for (Layout layout : layouts) {
211                            for (SitemapURLProvider sitemapURLProvider : sitemapURLProviders) {
212                                    sitemapURLProvider.visitLayout(element, layout, themeDisplay);
213                            }
214    
215                            visitLayouts(
216                                    element, layout.getChildren(), sitemapURLProviders,
217                                    themeDisplay);
218                    }
219            }
220    
221    }