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.UnicodeProperties;
027 import com.liferay.portal.kernel.util.Validator;
028 import com.liferay.portal.kernel.workflow.WorkflowConstants;
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.LayoutSet;
034 import com.liferay.portal.service.GroupLocalServiceUtil;
035 import com.liferay.portal.service.LayoutLocalServiceUtil;
036 import com.liferay.portal.service.LayoutSetLocalServiceUtil;
037 import com.liferay.portal.theme.ThemeDisplay;
038 import com.liferay.portal.util.LayoutSettings;
039 import com.liferay.portal.util.PortalUtil;
040 import com.liferay.portal.util.PropsValues;
041 import com.liferay.portlet.journal.model.JournalArticle;
042 import com.liferay.portlet.journal.model.JournalArticleConstants;
043 import com.liferay.portlet.journal.service.JournalArticleServiceUtil;
044
045 import java.text.DateFormat;
046
047 import java.util.Date;
048 import java.util.HashMap;
049 import java.util.HashSet;
050 import java.util.List;
051 import java.util.Locale;
052 import java.util.Map;
053 import java.util.Set;
054
055
059 @DoPrivileged
060 public class SitemapImpl implements Sitemap {
061
062 @Override
063 public String encodeXML(String input) {
064 StringBundler sb = null;
065
066 int lastReplacementIndex = 0;
067
068 for (int i = 0; i < input.length(); i++) {
069 char c = input.charAt(i);
070
071 String html = null;
072
073 if (c == '&') {
074 html = "&";
075 }
076 else if (c == '<') {
077 html = "<";
078 }
079 else if (c == '>') {
080 html = ">";
081 }
082 else if (c == '\'') {
083 html = "'";
084 }
085 else if (c == '"') {
086 html = """;
087 }
088
089 if (html != null) {
090 if (sb == null) {
091 sb = new StringBundler();
092 }
093
094 if (i > lastReplacementIndex) {
095 sb.append(input.substring(lastReplacementIndex, i));
096 }
097
098 sb.append(html);
099
100 lastReplacementIndex = i + 1;
101 }
102 }
103
104 if (sb == null) {
105 return input;
106 }
107
108 if (lastReplacementIndex < input.length()) {
109 sb.append(input.substring(lastReplacementIndex));
110 }
111
112 return sb.toString();
113 }
114
115 @Override
116 public String getSitemap(
117 long groupId, boolean privateLayout, ThemeDisplay themeDisplay)
118 throws PortalException, SystemException {
119
120 Document document = SAXReaderUtil.createDocument();
121
122 document.setXMLEncoding(StringPool.UTF8);
123
124 Element rootElement = document.addElement(
125 "urlset", "http:
126
127 rootElement.addAttribute("xmlns:xhtml", "http:
128
129 LayoutSet layoutSet = LayoutSetLocalServiceUtil.getLayoutSet(
130 groupId, privateLayout);
131
132 visitLayoutSet(rootElement, layoutSet, themeDisplay);
133
134 visitArticles(rootElement, layoutSet, themeDisplay);
135
136 return document.asXML();
137 }
138
139 protected void addURLElement(
140 Element element, String url, UnicodeProperties typeSettingsProperties,
141 Date modifiedDate, String canonicalURL,
142 Map<Locale, String> alternateURLs) {
143
144 Element urlElement = element.addElement("url");
145
146 Element locElement = urlElement.addElement("loc");
147
148 locElement.addText(encodeXML(url));
149
150 if (typeSettingsProperties == null) {
151 if (Validator.isNotNull(
152 PropsValues.SITES_SITEMAP_DEFAULT_CHANGE_FREQUENCY)) {
153
154 Element changefreqElement = urlElement.addElement("changefreq");
155
156 changefreqElement.addText(
157 PropsValues.SITES_SITEMAP_DEFAULT_CHANGE_FREQUENCY);
158 }
159
160 if (Validator.isNotNull(
161 PropsValues.SITES_SITEMAP_DEFAULT_PRIORITY)) {
162
163 Element priorityElement = urlElement.addElement("priority");
164
165 priorityElement.addText(
166 PropsValues.SITES_SITEMAP_DEFAULT_PRIORITY);
167 }
168 }
169 else {
170 String changefreq = typeSettingsProperties.getProperty(
171 "sitemap-changefreq");
172
173 if (Validator.isNotNull(changefreq)) {
174 Element changefreqElement = urlElement.addElement("changefreq");
175
176 changefreqElement.addText(changefreq);
177 }
178 else if (Validator.isNotNull(
179 PropsValues.SITES_SITEMAP_DEFAULT_CHANGE_FREQUENCY)) {
180
181 Element changefreqElement = urlElement.addElement("changefreq");
182
183 changefreqElement.addText(
184 PropsValues.SITES_SITEMAP_DEFAULT_CHANGE_FREQUENCY);
185 }
186
187 String priority = typeSettingsProperties.getProperty(
188 "sitemap-priority");
189
190 if (Validator.isNotNull(priority)) {
191 Element priorityElement = urlElement.addElement("priority");
192
193 priorityElement.addText(priority);
194 }
195 else if (Validator.isNotNull(
196 PropsValues.SITES_SITEMAP_DEFAULT_PRIORITY)) {
197
198 Element priorityElement = urlElement.addElement("priority");
199
200 priorityElement.addText(
201 PropsValues.SITES_SITEMAP_DEFAULT_PRIORITY);
202 }
203 }
204
205 if (modifiedDate != null) {
206 Element modifiedDateElement = urlElement.addElement("lastmod");
207
208 DateFormat iso8601DateFormat = DateUtil.getISO8601Format();
209
210 modifiedDateElement.addText(iso8601DateFormat.format(modifiedDate));
211 }
212
213 if (alternateURLs != null) {
214 for (Map.Entry<Locale, String> entry : alternateURLs.entrySet()) {
215 Locale locale = entry.getKey();
216 String href = entry.getValue();
217
218 Element alternateURLElement = urlElement.addElement(
219 "xhtml:link", "http:
220
221 alternateURLElement.addAttribute("href", href);
222 alternateURLElement.addAttribute(
223 "hreflang", LocaleUtil.toW3cLanguageId(locale));
224 alternateURLElement.addAttribute("rel", "alternate");
225 }
226
227 Element alternateURLElement = urlElement.addElement(
228 "xhtml:link", "http:
229
230 alternateURLElement.addAttribute("rel", "alternate");
231 alternateURLElement.addAttribute("hreflang", "x-default");
232 alternateURLElement.addAttribute("href", canonicalURL);
233 }
234 }
235
236 protected Map<Locale, String> getAlternateURLs(
237 String canonicalURL, ThemeDisplay themeDisplay, Layout layout)
238 throws PortalException, SystemException {
239
240 Map<Locale, String> alternateURLs = new HashMap<Locale, String>();
241
242 Locale[] availableLocales = LanguageUtil.getAvailableLocales(
243 layout.getGroupId());
244
245 for (Locale availableLocale : availableLocales) {
246 String alternateURL = PortalUtil.getAlternateURL(
247 canonicalURL, themeDisplay, availableLocale, layout);
248
249 alternateURLs.put(availableLocale, alternateURL);
250 }
251
252 return alternateURLs;
253 }
254
255 protected void visitArticles(
256 Element element, LayoutSet layoutSet, ThemeDisplay themeDisplay)
257 throws PortalException, SystemException {
258
259 List<JournalArticle> journalArticles =
260 JournalArticleServiceUtil.getLayoutArticles(layoutSet.getGroupId());
261
262 if (journalArticles.isEmpty()) {
263 return;
264 }
265
266 Set<String> processedArticleIds = new HashSet<String>();
267
268 String portalURL = PortalUtil.getPortalURL(layoutSet, themeDisplay);
269
270 for (JournalArticle journalArticle : journalArticles) {
271 if (processedArticleIds.contains(
272 journalArticle.getArticleId()) ||
273 (journalArticle.getStatus() !=
274 WorkflowConstants.STATUS_APPROVED)) {
275
276 continue;
277 }
278
279 String groupFriendlyURL = PortalUtil.getGroupFriendlyURL(
280 GroupLocalServiceUtil.getGroup(journalArticle.getGroupId()),
281 false, themeDisplay);
282
283 StringBundler sb = new StringBundler(4);
284
285 if (!groupFriendlyURL.startsWith(portalURL)) {
286 sb.append(portalURL);
287 }
288
289 sb.append(groupFriendlyURL);
290 sb.append(JournalArticleConstants.CANONICAL_URL_SEPARATOR);
291 sb.append(journalArticle.getUrlTitle());
292
293 Layout layout = LayoutLocalServiceUtil.getLayoutByUuidAndGroupId(
294 journalArticle.getLayoutUuid(), layoutSet.getGroupId(),
295 layoutSet.getPrivateLayout());
296
297 String articleURL = PortalUtil.getCanonicalURL(
298 sb.toString(), themeDisplay, layout);
299
300 Map<Locale, String> alternateURLs = getAlternateURLs(
301 articleURL, themeDisplay, layout);
302
303 addURLElement(
304 element, articleURL, null, journalArticle.getModifiedDate(),
305 articleURL, alternateURLs);
306
307 if (alternateURLs.size() > 1) {
308 Locale defaultLocale = LocaleUtil.getSiteDefault();
309
310 for (Map.Entry<Locale, String> entry :
311 alternateURLs.entrySet()) {
312
313 Locale availableLocale = entry.getKey();
314 String alternateURL = entry.getValue();
315
316 if (!availableLocale.equals(defaultLocale)) {
317 addURLElement(
318 element, alternateURL, null,
319 journalArticle.getModifiedDate(), articleURL,
320 alternateURLs);
321 }
322 }
323 }
324
325 processedArticleIds.add(journalArticle.getArticleId());
326 }
327 }
328
329 protected void visitLayoutSet(
330 Element element, LayoutSet layoutSet, ThemeDisplay themeDisplay)
331 throws PortalException, SystemException {
332
333 if (layoutSet.isPrivateLayout()) {
334 return;
335 }
336
337 Map<String, LayoutSettings> layoutSettingsMap =
338 LayoutSettings.getLayoutSettingsMap();
339
340 for (Map.Entry<String, LayoutSettings> entry :
341 layoutSettingsMap.entrySet()) {
342
343 LayoutSettings layoutSetting = entry.getValue();
344
345 if (!layoutSetting.isSitemapable()) {
346 continue;
347 }
348
349 List<Layout> layouts = LayoutLocalServiceUtil.getLayouts(
350 layoutSet.getGroupId(), layoutSet.getPrivateLayout(),
351 entry.getKey());
352
353 for (Layout layout : layouts) {
354 visitLayout(element, layout, themeDisplay);
355 }
356 }
357 }
358
359 protected void visitLayout(
360 Element element, Layout layout, ThemeDisplay themeDisplay)
361 throws PortalException, SystemException {
362
363 UnicodeProperties typeSettingsProperties =
364 layout.getTypeSettingsProperties();
365
366 if (!GetterUtil.getBoolean(
367 typeSettingsProperties.getProperty("sitemap-include"), true)) {
368
369 return;
370 }
371
372 String layoutFullURL = PortalUtil.getLayoutFullURL(
373 layout, themeDisplay);
374
375 layoutFullURL = PortalUtil.getCanonicalURL(
376 layoutFullURL, themeDisplay, layout);
377
378 Map<Locale, String> alternateURLs = getAlternateURLs(
379 layoutFullURL, themeDisplay, layout);
380
381 addURLElement(
382 element, layoutFullURL, typeSettingsProperties,
383 layout.getModifiedDate(), layoutFullURL, alternateURLs);
384
385 if (alternateURLs.size() > 1) {
386 Locale defaultLocale = LocaleUtil.getSiteDefault();
387
388 for (Map.Entry<Locale, String> entry : alternateURLs.entrySet()) {
389 Locale availableLocale = entry.getKey();
390 String alternateURL = entry.getValue();
391
392 if (availableLocale.equals(defaultLocale)) {
393 addURLElement(
394 element, alternateURL, typeSettingsProperties,
395 layout.getModifiedDate(), layoutFullURL, alternateURLs);
396 }
397 }
398 }
399 }
400
401 }