001
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.HashMap;
048 import java.util.HashSet;
049 import java.util.List;
050 import java.util.Locale;
051 import java.util.Map;
052 import java.util.Set;
053
054
058 @DoPrivileged
059 public class SitemapImpl implements Sitemap {
060
061 @Override
062 public String encodeXML(String input) {
063 return StringUtil.replace(
064 input,
065 new String[] {"&", "<", ">", "'", "\""},
066 new String[] {"&", "<", ">", "'", """});
067 }
068
069 @Override
070 public String getSitemap(
071 long groupId, boolean privateLayout, ThemeDisplay themeDisplay)
072 throws PortalException, SystemException {
073
074 Document document = SAXReaderUtil.createDocument();
075
076 document.setXMLEncoding(StringPool.UTF8);
077
078 Element rootElement = document.addElement(
079 "urlset", "http:
080
081 rootElement.addAttribute("xmlns:xhtml", "http:
082
083 List<Layout> layouts = LayoutLocalServiceUtil.getLayouts(
084 groupId, privateLayout, LayoutConstants.DEFAULT_PARENT_LAYOUT_ID);
085
086 visitLayouts(rootElement, layouts, themeDisplay);
087
088 return document.asXML();
089 }
090
091 protected void addURLElement(
092 Element element, String url, UnicodeProperties typeSettingsProperties,
093 Date modifiedDate, String canonicalURL,
094 Map<Locale, String> alternateURLs) {
095
096 Element urlElement = element.addElement("url");
097
098 Element locElement = urlElement.addElement("loc");
099
100 locElement.addText(encodeXML(url));
101
102 if (typeSettingsProperties == null) {
103 if (Validator.isNotNull(
104 PropsValues.SITES_SITEMAP_DEFAULT_CHANGE_FREQUENCY)) {
105
106 Element changefreqElement = urlElement.addElement("changefreq");
107
108 changefreqElement.addText(
109 PropsValues.SITES_SITEMAP_DEFAULT_CHANGE_FREQUENCY);
110 }
111
112 if (Validator.isNotNull(
113 PropsValues.SITES_SITEMAP_DEFAULT_PRIORITY)) {
114
115 Element priorityElement = urlElement.addElement("priority");
116
117 priorityElement.addText(
118 PropsValues.SITES_SITEMAP_DEFAULT_PRIORITY);
119 }
120 }
121 else {
122 String changefreq = typeSettingsProperties.getProperty(
123 "sitemap-changefreq");
124
125 if (Validator.isNotNull(changefreq)) {
126 Element changefreqElement = urlElement.addElement("changefreq");
127
128 changefreqElement.addText(changefreq);
129 }
130 else if (Validator.isNotNull(
131 PropsValues.SITES_SITEMAP_DEFAULT_CHANGE_FREQUENCY)) {
132
133 Element changefreqElement = urlElement.addElement("changefreq");
134
135 changefreqElement.addText(
136 PropsValues.SITES_SITEMAP_DEFAULT_CHANGE_FREQUENCY);
137 }
138
139 String priority = typeSettingsProperties.getProperty(
140 "sitemap-priority");
141
142 if (Validator.isNotNull(priority)) {
143 Element priorityElement = urlElement.addElement("priority");
144
145 priorityElement.addText(priority);
146 }
147 else if (Validator.isNotNull(
148 PropsValues.SITES_SITEMAP_DEFAULT_PRIORITY)) {
149
150 Element priorityElement = urlElement.addElement("priority");
151
152 priorityElement.addText(
153 PropsValues.SITES_SITEMAP_DEFAULT_PRIORITY);
154 }
155 }
156
157 if (modifiedDate != null) {
158 Element modifiedDateElement = urlElement.addElement("lastmod");
159
160 DateFormat iso8601DateFormat = DateUtil.getISO8601Format();
161
162 modifiedDateElement.addText(iso8601DateFormat.format(modifiedDate));
163 }
164
165 if (alternateURLs != null) {
166 for (Map.Entry<Locale, String> entry : alternateURLs.entrySet()) {
167 Locale locale = entry.getKey();
168 String href = entry.getValue();
169
170 Element alternateURLElement = urlElement.addElement("link");
171
172 alternateURLElement.addAttribute("href", href);
173 alternateURLElement.addAttribute(
174 "hreflang", LocaleUtil.toW3cLanguageId(locale));
175 alternateURLElement.addAttribute("rel", "alternate");
176 }
177
178 Element alternateURLElement = urlElement.addElement("link");
179
180 alternateURLElement.addAttribute("rel", "alternate");
181 alternateURLElement.addAttribute("hreflang", "x-default");
182 alternateURLElement.addAttribute("href", canonicalURL);
183 }
184 }
185
186 protected Map<Locale, String> getAlternateURLs(
187 String canonicalURL, ThemeDisplay themeDisplay, Layout layout)
188 throws PortalException, SystemException {
189
190 Map<Locale, String> alternateURLs = new HashMap<Locale, String>();
191
192 Locale[] availableLocales = LanguageUtil.getAvailableLocales(
193 layout.getGroupId());
194
195 for (Locale availableLocale : availableLocales) {
196 String alternateURL = PortalUtil.getAlternateURL(
197 canonicalURL, themeDisplay, availableLocale, layout);
198
199 alternateURLs.put(availableLocale, alternateURL);
200 }
201
202 return alternateURLs;
203 }
204
205 protected void visitArticles(
206 Element element, Layout layout, ThemeDisplay themeDisplay)
207 throws PortalException, SystemException {
208
209 List<JournalArticle> journalArticles =
210 JournalArticleServiceUtil.getArticlesByLayoutUuid(
211 layout.getGroupId(), layout.getUuid());
212
213 if (journalArticles.isEmpty()) {
214 return;
215 }
216
217 Set<String> processedArticleIds = new HashSet<String>();
218
219 for (JournalArticle journalArticle : journalArticles) {
220 if (processedArticleIds.contains(
221 journalArticle.getArticleId()) ||
222 (journalArticle.getStatus() !=
223 WorkflowConstants.STATUS_APPROVED)) {
224
225 continue;
226 }
227
228 String portalURL = PortalUtil.getPortalURL(layout, themeDisplay);
229
230 String groupFriendlyURL = PortalUtil.getGroupFriendlyURL(
231 GroupLocalServiceUtil.getGroup(journalArticle.getGroupId()),
232 false, themeDisplay);
233
234 StringBundler sb = new StringBundler(4);
235
236 if (!groupFriendlyURL.startsWith(portalURL)) {
237 sb.append(portalURL);
238 }
239
240 sb.append(groupFriendlyURL);
241 sb.append(JournalArticleConstants.CANONICAL_URL_SEPARATOR);
242 sb.append(journalArticle.getUrlTitle());
243
244 String articleURL = PortalUtil.getCanonicalURL(
245 sb.toString(), themeDisplay, layout);
246
247 addURLElement(
248 element, articleURL, null, journalArticle.getModifiedDate(),
249 articleURL, getAlternateURLs(articleURL, themeDisplay, layout));
250
251 Locale[] availableLocales = LanguageUtil.getAvailableLocales(
252 layout.getGroupId());
253
254 if (availableLocales.length > 1) {
255 Locale defaultLocale = LocaleUtil.getSiteDefault();
256
257 for (Locale availableLocale : availableLocales) {
258 if (!availableLocale.equals(defaultLocale)) {
259 String alternateURL = PortalUtil.getAlternateURL(
260 articleURL, themeDisplay, availableLocale, layout);
261
262 addURLElement(
263 element, alternateURL, null,
264 journalArticle.getModifiedDate(), articleURL,
265 getAlternateURLs(articleURL, themeDisplay, layout));
266 }
267 }
268 }
269
270 processedArticleIds.add(journalArticle.getArticleId());
271 }
272 }
273
274 protected void visitLayout(
275 Element element, Layout layout, ThemeDisplay themeDisplay)
276 throws PortalException, SystemException {
277
278 UnicodeProperties typeSettingsProperties =
279 layout.getTypeSettingsProperties();
280
281 if (!PortalUtil.isLayoutSitemapable(layout) ||
282 !GetterUtil.getBoolean(
283 typeSettingsProperties.getProperty("sitemap-include"), true)) {
284
285 return;
286 }
287
288 String layoutFullURL = PortalUtil.getLayoutFullURL(
289 layout, themeDisplay);
290
291 layoutFullURL = PortalUtil.getCanonicalURL(
292 layoutFullURL, themeDisplay, layout);
293
294 addURLElement(
295 element, layoutFullURL, typeSettingsProperties,
296 layout.getModifiedDate(), layoutFullURL,
297 getAlternateURLs(layoutFullURL, themeDisplay, layout));
298
299 Locale[] availableLocales = LanguageUtil.getAvailableLocales(
300 layout.getGroupId());
301
302 if (availableLocales.length > 1) {
303 Locale defaultLocale = LocaleUtil.getSiteDefault();
304
305 for (Locale availableLocale : availableLocales) {
306 if (availableLocale.equals(defaultLocale)) {
307 continue;
308 }
309
310 String alternateURL = PortalUtil.getAlternateURL(
311 layoutFullURL, themeDisplay, availableLocale, layout);
312
313 addURLElement(
314 element, alternateURL, typeSettingsProperties,
315 layout.getModifiedDate(), layoutFullURL,
316 getAlternateURLs(layoutFullURL, themeDisplay, layout));
317 }
318 }
319 }
320
321 protected void visitLayouts(
322 Element element, List<Layout> layouts, ThemeDisplay themeDisplay)
323 throws PortalException, SystemException {
324
325 for (Layout layout : layouts) {
326 visitLayout(element, layout, themeDisplay);
327
328 visitArticles(element, layout, themeDisplay);
329
330 visitLayouts(element, layout.getChildren(), themeDisplay);
331 }
332 }
333
334 }