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