001    /**
002     * Copyright (c) 2000-2013 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.portal.kernel.exception.PortalException;
018    import com.liferay.portal.kernel.exception.SystemException;
019    import com.liferay.portal.kernel.language.LanguageUtil;
020    import com.liferay.portal.kernel.security.pacl.DoPrivileged;
021    import com.liferay.portal.kernel.util.DateUtil;
022    import com.liferay.portal.kernel.util.GetterUtil;
023    import com.liferay.portal.kernel.util.LocaleUtil;
024    import com.liferay.portal.kernel.util.StringBundler;
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.workflow.WorkflowConstants;
030    import com.liferay.portal.kernel.xml.Document;
031    import com.liferay.portal.kernel.xml.Element;
032    import com.liferay.portal.kernel.xml.SAXReaderUtil;
033    import com.liferay.portal.model.Layout;
034    import com.liferay.portal.model.LayoutConstants;
035    import com.liferay.portal.service.GroupLocalServiceUtil;
036    import com.liferay.portal.service.LayoutLocalServiceUtil;
037    import com.liferay.portal.theme.ThemeDisplay;
038    import com.liferay.portal.util.PortalUtil;
039    import com.liferay.portal.util.PropsValues;
040    import com.liferay.portlet.journal.model.JournalArticle;
041    import com.liferay.portlet.journal.model.JournalArticleConstants;
042    import com.liferay.portlet.journal.service.JournalArticleServiceUtil;
043    
044    import java.text.DateFormat;
045    
046    import java.util.Date;
047    import java.util.HashSet;
048    import java.util.List;
049    import java.util.Locale;
050    import java.util.Set;
051    
052    /**
053     * @author Jorge Ferrer
054     * @author Vilmos Papp
055     */
056    @DoPrivileged
057    public class SitemapImpl implements Sitemap {
058    
059            public String encodeXML(String input) {
060                    return StringUtil.replace(
061                            input,
062                            new String[] {"&", "<", ">", "'", "\""},
063                            new String[] {"&amp;", "&lt;", "&gt;", "&apos;", "&quot;"});
064            }
065    
066            public String getSitemap(
067                            long groupId, boolean privateLayout, ThemeDisplay themeDisplay)
068                    throws PortalException, SystemException {
069    
070                    Document document = SAXReaderUtil.createDocument();
071    
072                    document.setXMLEncoding(StringPool.UTF8);
073    
074                    Element rootElement = document.addElement(
075                            "urlset", "http://www.google.com/schemas/sitemap/0.84");
076    
077                    List<Layout> layouts = LayoutLocalServiceUtil.getLayouts(
078                            groupId, privateLayout, LayoutConstants.DEFAULT_PARENT_LAYOUT_ID);
079    
080                    visitLayouts(rootElement, layouts, themeDisplay);
081    
082                    return document.asXML();
083            }
084    
085            protected void addURLElement(
086                    Element element, String url, UnicodeProperties typeSettingsProperties,
087                    Date modifiedDate) {
088    
089                    Element urlElement = element.addElement("url");
090    
091                    Element locElement = urlElement.addElement("loc");
092    
093                    locElement.addText(encodeXML(url));
094    
095                    if (typeSettingsProperties == null) {
096                            if (Validator.isNotNull(
097                                            PropsValues.SITES_SITEMAP_DEFAULT_CHANGE_FREQUENCY)) {
098    
099                                    Element changefreqElement = urlElement.addElement("changefreq");
100    
101                                    changefreqElement.addText(
102                                            PropsValues.SITES_SITEMAP_DEFAULT_CHANGE_FREQUENCY);
103                            }
104    
105                            if (Validator.isNotNull(
106                                            PropsValues.SITES_SITEMAP_DEFAULT_PRIORITY)) {
107    
108                                    Element priorityElement = urlElement.addElement("priority");
109    
110                                    priorityElement.addText(
111                                            PropsValues.SITES_SITEMAP_DEFAULT_PRIORITY);
112                            }
113                    }
114                    else {
115                            String changefreq = typeSettingsProperties.getProperty(
116                                    "sitemap-changefreq");
117    
118                            if (Validator.isNotNull(changefreq)) {
119                                    Element changefreqElement = urlElement.addElement("changefreq");
120    
121                                    changefreqElement.addText(changefreq);
122                            }
123                            else if (Validator.isNotNull(
124                                                    PropsValues.SITES_SITEMAP_DEFAULT_CHANGE_FREQUENCY)) {
125    
126                                    Element changefreqElement = urlElement.addElement("changefreq");
127    
128                                    changefreqElement.addText(
129                                            PropsValues.SITES_SITEMAP_DEFAULT_CHANGE_FREQUENCY);
130                            }
131    
132                            String priority = typeSettingsProperties.getProperty(
133                                    "sitemap-priority");
134    
135                            if (Validator.isNotNull(priority)) {
136                                    Element priorityElement = urlElement.addElement("priority");
137    
138                                    priorityElement.addText(priority);
139                            }
140                            else if (Validator.isNotNull(
141                                                    PropsValues.SITES_SITEMAP_DEFAULT_PRIORITY)) {
142    
143                                    Element priorityElement = urlElement.addElement("priority");
144    
145                                    priorityElement.addText(
146                                            PropsValues.SITES_SITEMAP_DEFAULT_PRIORITY);
147                            }
148                    }
149    
150                    if (modifiedDate != null) {
151                            Element modifiedDateElement = urlElement.addElement("lastmod");
152    
153                            DateFormat iso8601DateFormat = DateUtil.getISO8601Format();
154    
155                            modifiedDateElement.addText(iso8601DateFormat.format(modifiedDate));
156                    }
157            }
158    
159            protected void visitArticles(
160                            Element element, Layout layout, ThemeDisplay themeDisplay)
161                    throws PortalException, SystemException {
162    
163                    List<JournalArticle> journalArticles =
164                            JournalArticleServiceUtil.getArticlesByLayoutUuid(
165                                    layout.getGroupId(), layout.getUuid());
166    
167                    if (journalArticles.isEmpty()) {
168                            return;
169                    }
170    
171                    Set<String> processedArticleIds = new HashSet<String>();
172    
173                    for (JournalArticle journalArticle : journalArticles) {
174                            if (processedArticleIds.contains(
175                                            journalArticle.getArticleId()) ||
176                                    (journalArticle.getStatus() !=
177                                            WorkflowConstants.STATUS_APPROVED)) {
178    
179                                    continue;
180                            }
181    
182                            String portalURL = PortalUtil.getPortalURL(layout, themeDisplay);
183    
184                            String groupFriendlyURL = PortalUtil.getGroupFriendlyURL(
185                                    GroupLocalServiceUtil.getGroup(journalArticle.getGroupId()),
186                                    false, themeDisplay);
187    
188                            StringBundler sb = new StringBundler(4);
189    
190                            if (!groupFriendlyURL.startsWith(portalURL)) {
191                                    sb.append(portalURL);
192                            }
193    
194                            sb.append(groupFriendlyURL);
195                            sb.append(JournalArticleConstants.CANONICAL_URL_SEPARATOR);
196                            sb.append(journalArticle.getUrlTitle());
197    
198                            String articleURL = PortalUtil.getCanonicalURL(
199                                    sb.toString(), themeDisplay, layout);
200    
201                            addURLElement(
202                                    element, articleURL, null, journalArticle.getModifiedDate());
203    
204                            Locale[] availableLocales = LanguageUtil.getAvailableLocales();
205    
206                            if (availableLocales.length > 1) {
207                                    Locale defaultLocale = LocaleUtil.getDefault();
208    
209                                    for (Locale availableLocale : availableLocales) {
210                                            if (!availableLocale.equals(defaultLocale)) {
211                                                    String alternateURL = PortalUtil.getAlternateURL(
212                                                            articleURL, themeDisplay, availableLocale);
213    
214                                                    addURLElement(
215                                                            element, alternateURL, null,
216                                                            journalArticle.getModifiedDate());
217                                            }
218                                    }
219                            }
220    
221                            processedArticleIds.add(journalArticle.getArticleId());
222                    }
223            }
224    
225            protected void visitLayout(
226                            Element element, Layout layout, ThemeDisplay themeDisplay)
227                    throws PortalException, SystemException {
228    
229                    UnicodeProperties typeSettingsProperties =
230                            layout.getTypeSettingsProperties();
231    
232                    if (layout.isHidden() || !PortalUtil.isLayoutSitemapable(layout) ||
233                            !GetterUtil.getBoolean(
234                                    typeSettingsProperties.getProperty("sitemap-include"), true)) {
235    
236                            return;
237                    }
238    
239                    String layoutFullURL = PortalUtil.getLayoutFullURL(
240                            layout, themeDisplay);
241    
242                    layoutFullURL = PortalUtil.getCanonicalURL(
243                            layoutFullURL, themeDisplay, layout);
244    
245                    addURLElement(
246                            element, layoutFullURL, typeSettingsProperties,
247                            layout.getModifiedDate());
248    
249                    Locale[] availableLocales = LanguageUtil.getAvailableLocales();
250    
251                    if (availableLocales.length > 1) {
252                            Locale defaultLocale = LocaleUtil.getDefault();
253    
254                            for (Locale availableLocale : availableLocales) {
255                                    if (availableLocale.equals(defaultLocale)) {
256                                            continue;
257                                    }
258    
259                                    String alternateURL = PortalUtil.getAlternateURL(
260                                            layoutFullURL, themeDisplay, availableLocale);
261    
262                                    addURLElement(
263                                            element, alternateURL, typeSettingsProperties,
264                                            layout.getModifiedDate());
265                            }
266                    }
267    
268                    visitArticles(element, layout, themeDisplay);
269                    visitLayouts(element, layout.getChildren(), themeDisplay);
270            }
271    
272            protected void visitLayouts(
273                            Element element, List<Layout> layouts, ThemeDisplay themeDisplay)
274                    throws PortalException, SystemException {
275    
276                    for (Layout layout : layouts) {
277                            visitLayout(element, layout, themeDisplay);
278                    }
279            }
280    
281    }