View Javadoc
1   /*
2    * Copyright (C) 2003-2011 eXo Platform SAS.
3    *
4    * This program is free software; you can redistribute it and/or
5    * modify it under the terms of the GNU Affero General Public License
6    * as published by the Free Software Foundation; either version 3
7    * of the License, or (at your option) any later version.
8    *
9    * This program is distributed in the hope that it will be useful,
10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   * GNU General Public License for more details.
13   *
14   * You should have received a copy of the GNU General Public License
15   * along with this program; if not, see<http://www.gnu.org/licenses/>.
16   */
17  package org.exoplatform.services.seo.impl;
18  
19  import java.io.InputStream;
20  import java.io.StringWriter;
21  import java.util.ArrayList;
22  import java.util.Collections;
23  import java.util.Comparator;
24  import java.util.GregorianCalendar;
25  import java.util.List;
26  import java.util.Locale;
27  
28  import javax.jcr.Node;
29  import javax.jcr.NodeIterator;
30  import javax.jcr.Session;
31  import javax.xml.parsers.DocumentBuilder;
32  import javax.xml.parsers.DocumentBuilderFactory;
33  import javax.xml.transform.Transformer;
34  import javax.xml.transform.TransformerFactory;
35  import javax.xml.transform.dom.DOMSource;
36  import javax.xml.transform.stream.StreamResult;
37  
38  import org.exoplatform.container.xml.InitParams;
39  import org.exoplatform.container.xml.ObjectParameter;
40  import org.exoplatform.ecm.utils.MessageDigester;
41  import org.exoplatform.management.annotations.Managed;
42  import org.exoplatform.management.annotations.ManagedDescription;
43  import org.exoplatform.management.annotations.ManagedName;
44  import org.exoplatform.portal.application.PortalRequestContext;
45  import org.exoplatform.portal.webui.util.Util;
46  import org.exoplatform.services.cache.CacheService;
47  import org.exoplatform.services.cache.ExoCache;
48  import org.exoplatform.services.jcr.config.WorkspaceEntry;
49  import org.exoplatform.services.jcr.core.ManageableRepository;
50  import org.exoplatform.services.jcr.ext.common.SessionProvider;
51  import org.exoplatform.services.seo.PageMetadataModel;
52  import org.exoplatform.services.seo.SEOConfig;
53  import org.exoplatform.services.seo.SEOService;
54  import org.exoplatform.services.wcm.portal.LivePortalManagerService;
55  import org.exoplatform.services.wcm.utils.WCMCoreUtils;
56  
57  import org.apache.commons.lang.StringUtils;
58  import org.w3c.dom.Document;
59  import org.w3c.dom.Element;
60  
61  /**
62   * Created by The eXo Platform SAS Author : eXoPlatform exo@exoplatform.com Jun
63   * 17, 2011
64   */
65  public class SEOServiceImpl implements SEOService {
66    private ExoCache<String, Object> cache;
67  
68    public static final String EMPTY_CACHE_ENTRY = "EMPTY";
69    public static String METADATA_BASE_PATH = "SEO";
70    final static public String LANGUAGES    = "seo-languages";
71    public static String METADATA_PAGE_PATH = "pages";
72    public static String METADATA_CONTENT_PATH = "contents";
73    public static String SITEMAP_NAME = "sitemaps";
74    public static String ROBOTS_NAME = "robots";
75    private static String PUBLIC_MODE = "public";
76    private static String PRIVATE_MODE = "private";
77  
78    private final static String CACHE_NAME = "ecms.seo";
79  
80    private List<String> robotsindex = new ArrayList<String>();
81    private List<String> robotsfollow = new ArrayList<String>();
82    private List<String> frequency = new ArrayList<String>();
83    private SEOConfig seoConfig = null;
84  
85    private boolean isCached = true;
86  
87    /**
88     * Constructor method
89     *
90     * @param initParams
91     *          The initial parameters
92     * @throws Exception
93     */
94    public SEOServiceImpl(InitParams initParams) throws Exception {
95      ObjectParameter param = initParams.getObjectParam("seo.config");
96      if (param != null) {
97        seoConfig = (SEOConfig) param.getObject();
98        robotsindex = seoConfig.getRobotsIndex();
99        robotsfollow = seoConfig.getRobotsFollow();
100       frequency = seoConfig.getFrequency();
101     }
102     cache = WCMCoreUtils.getService(CacheService.class).getCacheInstance(
103             CACHE_NAME);
104   }
105 
106   /**
107    *{@inheritDoc}
108    */
109   public List<String> getRobotsIndexOptions() {
110     return robotsindex;
111   }
112 
113   /**
114    * {@inheritDoc}
115    */
116   public List<String> getRobotsFollowOptions() {
117     return robotsfollow;
118   }
119 
120   /**
121    * {@inheritDoc}
122    */
123   public List<String> getFrequencyOptions() {
124     return frequency;
125   }
126 
127   /**
128    * {@inheritDoc}
129    */
130   public void storeMetadata(PageMetadataModel metaModel, String portalName,
131                             boolean onContent, String language) throws Exception {
132     String uri = metaModel.getUri();
133     String cachedHash = getPageOrContentCacheKey(uri, language);
134     if (cache.get(cachedHash) != null) {
135       cache.remove(cachedHash);
136     }
137     String pageReference = metaModel.getPageReference();
138     cachedHash = getPageOrContentCacheKey(metaModel.getPageReference(), language);
139     if (cache.get(cachedHash) != null) {
140       cache.remove(cachedHash);
141     }
142 
143     // Inherit from parent page
144     /*
145      * if(!onContent) { if(metaModel.getPageParent() != null &&
146      * metaModel.getPageParent().length() > 0) { PageMetadataModel parentModel =
147      * this.getPageMetadata(metaModel.getPageParent()); if(parentModel != null)
148      * { metaModel = parentModel; metaModel.setUri(uri);
149      * metaModel.setPageReference(pageReference); } } }
150      */
151     String title = metaModel.getTitle();
152     String keyword = metaModel.getKeywords();
153     String description = metaModel.getDescription();
154     String robots = metaModel.getRobotsContent();
155     String fullStatus = metaModel.getFullStatus();
156     boolean sitemap = metaModel.getSitemap();
157     float priority = metaModel.getPriority();
158     String frequency = metaModel.getFrequency();
159     SessionProvider sessionProvider = WCMCoreUtils.getSystemSessionProvider();
160     Session session = null;
161     LivePortalManagerService livePortalManagerService = WCMCoreUtils
162         .getService(LivePortalManagerService.class);
163     Node dummyNode = livePortalManagerService.getLivePortal(sessionProvider,
164                                                             portalName);
165     session = dummyNode.getSession();    
166     if (!dummyNode.hasNode(METADATA_BASE_PATH)) {
167       dummyNode.addNode(METADATA_BASE_PATH);
168       session.save();
169     }
170     //Store robots data
171     updateRobots(dummyNode, portalName);    
172     // Store sitemap data
173     Node node = null;
174     if (onContent) {
175       node = session.getNodeByUUID(uri);
176       if (!node.isNodeType("mix:referenceable")) {
177         node.addMixin("mix:referenceable");
178       }
179     } else {
180       session = sessionProvider.getSession("portal-system", WCMCoreUtils
181                                            .getRepository());
182       String uuid = Util.getUIPortal().getSelectedUserNode().getId();
183       node = session.getNodeByUUID(uuid);
184       if (!node.isNodeType("exo:seoMetadata")) {
185         node.addMixin("exo:seoMetadata");
186       }
187     }    
188 
189     Node languageNode = null;
190     if(node.hasNode(LANGUAGES))
191       languageNode = node.getNode(LANGUAGES);
192     else
193       languageNode = node.addNode(LANGUAGES);
194     if(languageNode.canAddMixin("exo:hiddenable"))
195       languageNode.addMixin("exo:hiddenable");
196     session.save();
197     Node seoNode = null;
198     if(languageNode.hasNode(language)) seoNode = languageNode.getNode(language);
199     else seoNode = languageNode.addNode(language);
200     if (!seoNode.isNodeType("mix:referenceable")) {
201       seoNode.addMixin("mix:referenceable");
202     }
203     if (seoNode.isNodeType("exo:pageMetadata")) {
204       seoNode.setProperty("exo:metaKeywords", keyword);
205       seoNode.setProperty("exo:metaDescription", description);
206       seoNode.setProperty("exo:metaFully", fullStatus);
207       if (!onContent) {
208         seoNode.setProperty("exo:metaTitle", title);
209         seoNode.setProperty("exo:metaRobots", robots);
210         seoNode.setProperty("exo:metaSitemap", sitemap);
211         seoNode.setProperty("exo:metaPriority", priority);
212         seoNode.setProperty("exo:metaFrequency", frequency);
213         updateSiteMap(uri, priority, frequency, sitemap, portalName);
214       }
215       String hash = null;
216       if (onContent)
217         hash = cachedHash;
218       else
219         hash = getPageOrContentCacheKey(pageReference, language);
220       if (hash != null)
221         cache.put(hash, metaModel);
222     } else {
223       String hash = null;
224       seoNode.addMixin("exo:pageMetadata");
225       seoNode.setProperty("exo:metaKeywords", keyword);
226       seoNode.setProperty("exo:metaDescription", description);
227       seoNode.setProperty("exo:metaFully", fullStatus);
228       if (onContent) {
229         seoNode.setProperty("exo:metaUri", seoNode.getUUID());
230         hash = getHash(seoNode.getUUID() + language);
231       } else {      	
232         seoNode.setProperty("exo:metaTitle", title);
233         seoNode.setProperty("exo:metaUri", pageReference);
234         seoNode.setProperty("exo:metaRobots", robots);
235         seoNode.setProperty("exo:metaSitemap", sitemap);
236         seoNode.setProperty("exo:metaPriority", priority);
237         seoNode.setProperty("exo:metaFrequency", frequency);
238         updateSiteMap(uri, priority, frequency, sitemap, portalName);
239         hash = getPageOrContentCacheKey(pageReference, language);
240       }
241       if (hash != null)
242         cache.put(hash, metaModel);
243     }
244     session.save();
245   }
246 
247   private String getPageOrContentCacheKey(String uri, String language) throws Exception {
248     return getHash(uri + language);
249   } 
250 
251   public String getState(String path, String language, boolean onContent) throws Exception{
252     String state = "Empty";
253     Node node = null;
254     String hash = null;
255     PageMetadataModel metaModel = null;
256     if(onContent) {
257       node = getContentNode(path);
258       hash = node == null ? StringUtils.EMPTY : getPageOrContentCacheKey(node.getUUID(), language);
259       metaModel = (PageMetadataModel) getCachedEntry(hash, false);
260       if(metaModel != null) return metaModel.getFullStatus();
261 
262     } else {
263       hash = getPageOrContentCacheKey(path, language);
264       metaModel = (PageMetadataModel) getCachedEntry(hash, true);
265       if (isNullObject(metaModel)) {
266         return null;
267       }
268       if(metaModel != null) return metaModel.getFullStatus();
269       SessionProvider sessionProvider = WCMCoreUtils.getSystemSessionProvider();
270       Session session = sessionProvider.getSession("portal-system",
271                                                    WCMCoreUtils.getRepository());
272       String uuid = Util.getUIPortal().getSelectedUserNode().getId();
273       node = session.getNodeByUUID(uuid);
274     }
275     if(node.hasNode(LANGUAGES+"/"+language)) {
276       Node seoNode = node.getNode(LANGUAGES+"/"+language);
277       if (seoNode.isNodeType("exo:pageMetadata") && seoNode.hasProperty("exo:metaFully"))
278         return seoNode.getProperty("exo:metaFully").getString();  			
279     }
280     return state;
281   }
282 
283   private boolean isNullObject(PageMetadataModel metaModel) {
284     return metaModel == PageMetadataModel.NULL_PAGE_METADATA_MODEL;
285   }
286 
287   private Object getCachedEntry(String hash, boolean cacheNull) {
288     Object object = cache.get(hash);
289     if (cacheNull) {
290       if( object == null) {
291         cache.put(hash, EMPTY_CACHE_ENTRY);
292       } else if(EMPTY_CACHE_ENTRY.equals(object)) {
293         return PageMetadataModel.NULL_PAGE_METADATA_MODEL;
294       }
295     }
296     return object;
297   }
298 
299   public PageMetadataModel getMetadata(ArrayList<String> params,
300                                        String pageReference, String language) throws Exception {
301     PageMetadataModel metaModel = null;
302     if (params != null) {
303       metaModel = getContentMetadata(params, language);
304       if(metaModel == null) metaModel = getPageMetadata(pageReference, language);
305     } else {
306       metaModel = getPageMetadata(pageReference, language);
307     }
308     return metaModel;
309   }
310 
311   /**
312    * {@inheritDoc}
313    */
314   public PageMetadataModel getContentMetadata(ArrayList<String> params, String language)
315       throws Exception {
316     PageMetadataModel metaModel = null;
317     String pageUri = null;
318     Node contentNode = null;
319     for (int i = 0; i < params.size(); i++) {
320       contentNode = this.getContentNode(params.get(i).toString());
321       if (contentNode != null)
322         break;
323     }
324 
325     if (contentNode == null)
326       return null;
327     if (!contentNode.isNodeType("mix:referenceable")) {
328       contentNode.addMixin("mix:referenceable");
329     }
330     String hash = getPageOrContentCacheKey(contentNode.getUUID(), language);
331     metaModel = (PageMetadataModel) getCachedEntry(hash, false);
332 
333     if (metaModel == null && contentNode.hasNode(LANGUAGES+"/"+language)) {
334       //Getting seo node by language
335       Node seoNode = contentNode.getNode(LANGUAGES+"/"+language);
336       if (!seoNode.isNodeType("mix:referenceable")) {
337         seoNode.addMixin("mix:referenceable");
338       }
339       if (seoNode.isNodeType("exo:pageMetadata")) {
340         metaModel = new PageMetadataModel();
341         metaModel.setUri(pageUri);        
342         if (seoNode.hasProperty("exo:metaKeywords"))
343           metaModel.setKeywords((seoNode.getProperty("exo:metaKeywords"))
344                                 .getString());
345         if (seoNode.hasProperty("exo:metaDescription"))
346           metaModel.setDescription((seoNode
347               .getProperty("exo:metaDescription")).getString());
348         if (seoNode.hasProperty("exo:metaRobots"))
349           metaModel
350           .setRobotsContent((seoNode.getProperty("exo:metaRobots"))
351                             .getString());
352         if (seoNode.hasProperty("exo:metaSitemap"))
353           metaModel.setSiteMap(Boolean.parseBoolean((seoNode
354               .getProperty("exo:metaSitemap")).getString()));
355         if (seoNode.hasProperty("exo:metaPriority"))
356           metaModel.setPriority(Long.parseLong((seoNode
357               .getProperty("exo:metaPriority")).getString()));
358         if (seoNode.hasProperty("exo:metaFrequency"))
359           metaModel.setFrequency((seoNode.getProperty("exo:metaFrequency"))
360                                  .getString());
361         if (seoNode.hasProperty("exo:metaFully"))
362           metaModel.setFullStatus((seoNode.getProperty("exo:metaFully"))
363                                   .getString());
364         cache.put(hash, metaModel);
365       }
366     }
367     return metaModel;
368   }
369 
370   /**
371    * {@inheritDoc}
372    */
373   public PageMetadataModel getPageMetadata(String pageUri, String language) throws Exception {
374     PageMetadataModel metaModel = null;
375     String hash = getPageOrContentCacheKey(pageUri, language);
376     metaModel = (PageMetadataModel) getCachedEntry(hash, true);
377     if (isNullObject(metaModel)) {
378       return null;
379     }
380     if (metaModel == null) {
381       SessionProvider sessionProvider = WCMCoreUtils.getSystemSessionProvider();
382       Session session = sessionProvider.getSession("portal-system",
383                                                    WCMCoreUtils.getRepository());
384       String uuid = Util.getUIPortal().getSelectedUserNode().getId();
385       Node pageNode = session.getNodeByUUID(uuid);
386 
387       if (pageNode != null && pageNode.hasNode(LANGUAGES+"/"+language)) {
388         Node seoNode = pageNode.getNode(LANGUAGES+"/"+language);
389         if(seoNode.isNodeType("exo:pageMetadata")) {
390           metaModel = new PageMetadataModel();
391           if (seoNode.hasProperty("exo:metaTitle"))
392             metaModel.setTitle((seoNode.getProperty("exo:metaTitle")).getString());
393           if (seoNode.hasProperty("exo:metaKeywords"))
394             metaModel.setKeywords((seoNode.getProperty("exo:metaKeywords"))
395                                   .getString());
396           if (seoNode.hasProperty("exo:metaDescription"))
397             metaModel
398             .setDescription((seoNode.getProperty("exo:metaDescription"))
399                             .getString());
400           if (seoNode.hasProperty("exo:metaRobots"))
401             metaModel.setRobotsContent((seoNode.getProperty("exo:metaRobots"))
402                                        .getString());
403           if (seoNode.hasProperty("exo:metaSitemap"))
404             metaModel.setSiteMap(Boolean.parseBoolean((seoNode
405                 .getProperty("exo:metaSitemap")).getString()));
406           if (seoNode.hasProperty("exo:metaPriority"))
407             metaModel.setPriority(Long.parseLong((seoNode
408                 .getProperty("exo:metaPriority")).getString()));
409           if (seoNode.hasProperty("exo:metaFrequency"))
410             metaModel.setFrequency((seoNode.getProperty("exo:metaFrequency"))
411                                    .getString());
412           if (seoNode.hasProperty("exo:metaFully"))
413             metaModel.setFullStatus((seoNode.getProperty("exo:metaFully"))
414                                     .getString());
415           cache.put(hash, metaModel);
416         }
417       }
418     }
419     return metaModel;
420   }
421 
422   public List<Locale> getSEOLanguages(String portalName, String seoPath, boolean onContent) throws Exception {  	
423     List<Locale> languages = new ArrayList<Locale>();
424     Node languagesNode = null;
425     if(onContent) {
426       Node contentNode = null;
427       contentNode = getContentNode(seoPath);
428       if (contentNode != null && contentNode.hasNode(LANGUAGES)) languagesNode = contentNode.getNode(LANGUAGES);  	
429     } else {
430       SessionProvider sessionProvider = WCMCoreUtils.getSystemSessionProvider();
431       Session session = sessionProvider.getSession("portal-system",
432                                                    WCMCoreUtils.getRepository());
433       String uuid = Util.getUIPortal().getSelectedUserNode().getId();
434       Node pageNode = session.getNodeByUUID(uuid);
435       if (pageNode != null && pageNode.hasNode(LANGUAGES)) languagesNode = pageNode.getNode(LANGUAGES);
436     }
437     if(languagesNode != null) {
438       NodeIterator iter = languagesNode.getNodes();
439       while(iter.hasNext()) {   
440         String lang = iter.nextNode().getName();
441         String[] arr = lang.split("_");
442         if(arr.length > 1) {
443           languages.add(new Locale(arr[0],arr[1]));    			
444         } else languages.add(new Locale(lang));
445       }    	
446       Collections.sort(languages, new SEOItemComparator());
447       return languages;
448     } 
449     return languages;
450   }
451 
452   class SEOItemComparator implements Comparator<Locale> {
453     @Override
454     public int compare(Locale locale1, Locale locale2) {
455       return locale1.getDisplayName().compareTo(locale2.getDisplayName());
456     }
457   }
458 
459   /**
460    * {@inheritDoc}
461    */
462   public void removePageMetadata(PageMetadataModel metaModel,
463                                  String portalName, boolean onContent, String language) throws Exception {
464     SessionProvider sessionProvider = WCMCoreUtils.getSystemSessionProvider();
465     ManageableRepository currentRepo = WCMCoreUtils.getRepository();
466     Session session = sessionProvider.getSession(currentRepo.getConfiguration()
467                                                  .getDefaultWorkspaceName(), currentRepo);
468     String hash = "";
469     Node node = null;
470     if (onContent) {
471       node = session.getNodeByUUID(metaModel.getUri());
472     } else {
473       session = sessionProvider.getSession("portal-system", WCMCoreUtils
474                                            .getRepository());
475       String uuid = Util.getUIPortal().getSelectedUserNode().getId();
476       node = session.getNodeByUUID(uuid);
477     }    
478     Node seoNode = null;
479     if(node.hasNode(LANGUAGES+"/"+language)) 
480       seoNode = node.getNode(LANGUAGES+"/"+language);
481 
482     if (seoNode != null) {
483       seoNode.remove();
484       if (onContent)
485         hash = getHash(metaModel.getUri() + language);
486       else
487         hash = getHash(metaModel.getPageReference() + language);
488       cache.remove(hash);
489     }
490     session.save();
491   }
492 
493   /**
494    * Update sitemap content for portal
495    *
496    * @param uri
497    *          The uri of page
498    * @param priority
499    *          The priority of page
500    * @param frequency
501    *          The frequency of page
502    * @param visibleSitemap
503    *          visibleSitemap = true page is visible on sitemap visibleSitemap =
504    *          false page is invisible on sitemap
505    * @param portalName
506    *          The portal name
507    * @throws Exception
508    */
509   public void updateSiteMap(String uri, float priority, String frequency,
510                             boolean visibleSitemap, String portalName) throws Exception {
511     SessionProvider sessionProvider = WCMCoreUtils.getSystemSessionProvider();
512     LivePortalManagerService livePortalManagerService = WCMCoreUtils
513         .getService(LivePortalManagerService.class);
514     Node dummyNode = livePortalManagerService.getLivePortal(sessionProvider,
515                                                             portalName);
516     Session session = dummyNode.getSession();
517     uri = getStandardURL(uri);
518     if(uri == null) uri = "";
519     String uri_clone = "";
520     String public_path = "/" + PUBLIC_MODE + "/";
521     String private_path = "/" + PRIVATE_MODE + "/";
522     if (uri.indexOf(public_path) > 0)
523       uri_clone = uri.replaceFirst(public_path, private_path);
524     else if (uri.indexOf(private_path) > 0)
525       uri_clone = uri.replaceFirst(private_path, public_path);
526 
527     String sitemapData = "";
528     if (!dummyNode.getNode(METADATA_BASE_PATH).hasNode(SITEMAP_NAME)) {
529       dummyNode.getNode(METADATA_BASE_PATH).addNode(SITEMAP_NAME, "nt:file");
530       Node simapFolder = dummyNode.getNode(METADATA_BASE_PATH + "/"
531           + SITEMAP_NAME);
532       Node sitemapNode = simapFolder.addNode("jcr:content", "nt:resource");
533       sitemapNode.setProperty("jcr:mimeType", "text/xml");
534       DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
535       DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
536       // root elements
537       Document doc = docBuilder.newDocument();
538       Element rootElement = doc.createElement("urlset");
539       rootElement.setAttribute("xmlns",
540           "http://www.sitemaps.org/schemas/sitemap/0.9");
541       rootElement.setAttribute("xmlns:xsi",
542           "http://www.w3.org/2001/XMLSchema-instance");
543       rootElement
544       .setAttribute(
545                     "xsi:schemaLocation",
546           "http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd");
547       doc.appendChild(rootElement);
548       if (visibleSitemap) {
549         // Create element in sitemap for uri
550         Element urlElement = doc.createElement("url");
551         rootElement.appendChild(urlElement);
552         Element locElement = doc.createElement("loc");
553         locElement.setTextContent(uri);
554         urlElement.appendChild(locElement);
555         Element freqElement = doc.createElement("changefreq");
556         freqElement.setTextContent(frequency);
557         urlElement.appendChild(freqElement);
558         Element priorityElement = doc.createElement("priority");
559         if(priority >= 0) {        	
560           priorityElement.setTextContent(String.valueOf(priority));
561           urlElement.appendChild(priorityElement);
562         }
563         // Create element in sitemap for uri_clone
564         if (uri_clone != null && uri_clone.length() > 0) {
565           urlElement = doc.createElement("url");
566           rootElement.appendChild(urlElement);
567           locElement = doc.createElement("loc");
568           locElement.setTextContent(uri_clone);
569           urlElement.appendChild(locElement);
570           freqElement = doc.createElement("changefreq");
571           freqElement.setTextContent(frequency);
572           urlElement.appendChild(freqElement);          
573           if(priority >= 0) {
574             priorityElement = doc.createElement("priority");
575             priorityElement.setTextContent(String.valueOf(priority));
576             urlElement.appendChild(priorityElement);
577           }
578         }
579       }
580 
581       TransformerFactory transformerFactory = TransformerFactory.newInstance();
582       Transformer transformer = transformerFactory.newTransformer();
583       DOMSource source = new DOMSource(doc);
584       StringWriter writer = new StringWriter();
585       transformer.transform(source, new StreamResult(writer));
586       sitemapData = writer.toString();
587       sitemapNode.setProperty("jcr:data", sitemapData);
588       sitemapNode.setProperty("jcr:lastModified", new GregorianCalendar());
589       session.save();
590     } else {
591       Node sitemapFolder = dummyNode.getNode(METADATA_BASE_PATH + "/"
592           + SITEMAP_NAME);
593       Node sitemapNode = sitemapFolder.getNode("jcr:content");
594       DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory
595           .newInstance();
596       DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
597       InputStream stream = sitemapNode.getProperty("jcr:data").getStream();
598       Document doc = docBuilder.parse(stream);
599 
600       // normalize text representation
601       boolean fLoc = false;
602       doc.getDocumentElement().normalize();
603       Element root = doc.getDocumentElement();
604       ArrayList<org.w3c.dom.Node> arrNodes = new ArrayList<org.w3c.dom.Node>();
605       org.w3c.dom.NodeList listOfUrls = doc.getElementsByTagName("url");
606       for (int i = 0; i < listOfUrls.getLength(); i++) {
607         org.w3c.dom.Node urlNode = listOfUrls.item(i);
608         if (urlNode.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE) {
609           Element urlElement = (Element) urlNode;
610           org.w3c.dom.NodeList locList = urlElement.getElementsByTagName("loc");
611           Element locElement = (Element) locList.item(0);
612           // The location is exist
613           if(locElement.getChildNodes().item(0) != null) {
614             String locationValue = locElement.getChildNodes().item(0).getNodeValue();
615             if (locationValue != null & (locationValue.trim().equals(uri) || locationValue.trim().equals(uri_clone))) {
616               fLoc = true;
617               if (visibleSitemap) {
618                 org.w3c.dom.Node freqNode = urlElement.getElementsByTagName(
619                     "changefreq").item(0);
620                 freqNode.setTextContent(frequency);              
621                 if(priority >= 0) {
622                   org.w3c.dom.Node priorityNode = urlElement.getElementsByTagName("priority").item(0);
623                   if(priorityNode == null) {
624                     Element priorityElement = doc.createElement("priority");
625                     priorityElement.setTextContent(String.valueOf(priority));
626                     urlElement.appendChild(priorityElement);
627                   } else priorityNode.setTextContent(String.valueOf(priority));
628                 }
629               } else {
630                 arrNodes.add(urlNode);
631               }
632             }
633           }
634         }
635       }
636       // Remove element from sitemap.xml
637       if (arrNodes != null && arrNodes.size() > 0) {
638         for (int i = 0; i < arrNodes.size(); i++) {
639           root.removeChild(arrNodes.get(i));
640         }
641       }
642       // Update xml document for sitemap
643       if (!fLoc && visibleSitemap) {
644         // Create element in sitemap for uri
645         Element urlElement = doc.createElement("url");
646         Element locElement = doc.createElement("loc");
647         locElement.setTextContent(uri);
648         Element freqElement = doc.createElement("changefreq");
649         freqElement.setTextContent(frequency);
650         urlElement.appendChild(locElement);
651         urlElement.appendChild(freqElement);
652         Element priorityElement = doc.createElement("priority");
653         if(priority >= 0) {
654           priorityElement.setTextContent(String.valueOf(priority));
655           urlElement.appendChild(priorityElement);
656         }        
657         root.appendChild(urlElement);
658         // create element in sitemap for uri_clone
659         if (uri_clone != null && uri_clone.length() > 0) {
660           urlElement = doc.createElement("url");
661           locElement = doc.createElement("loc");
662           locElement.setTextContent(uri_clone);
663           freqElement = doc.createElement("changefreq");
664           freqElement.setTextContent(frequency);
665           urlElement.appendChild(locElement);
666           urlElement.appendChild(freqElement);
667           if(priority >= 0) {
668             priorityElement = doc.createElement("priority");
669             priorityElement.setTextContent(String.valueOf(priority));
670             urlElement.appendChild(priorityElement);
671           }          
672           root.appendChild(urlElement);
673         }
674       }
675       TransformerFactory transformerFactory = TransformerFactory.newInstance();
676       Transformer transformer = transformerFactory.newTransformer();
677       DOMSource source = new DOMSource(doc);
678       StringWriter writer = new StringWriter();
679       transformer.transform(source, new StreamResult(writer));
680       sitemapData = writer.toString();
681       sitemapNode.setProperty("jcr:data", sitemapData);
682       sitemapNode.setProperty("jcr:lastModified", new GregorianCalendar());
683     }
684 
685     if (sitemapData != null && sitemapData.length() > 0) {
686       String hash = getSiteMapCacheKey(portalName);
687       cache.put(hash, sitemapData);
688     }
689     session.save();
690   }
691 
692   public void updateRobots(Node dummyNode, String portalName) throws Exception {
693     if (!dummyNode.getNode(METADATA_BASE_PATH).hasNode(ROBOTS_NAME)) {
694       dummyNode.getNode(METADATA_BASE_PATH).addNode(ROBOTS_NAME, "nt:file");
695       Node robotsFolder = dummyNode.getNode(METADATA_BASE_PATH + "/"
696           + ROBOTS_NAME);
697       Node robotsNode = robotsFolder.addNode("jcr:content", "nt:resource");
698       robotsNode.setProperty("jcr:mimeType", "text/plain");      
699       PortalRequestContext ctx = Util.getPortalRequestContext();
700       StringBuffer robotsContent = new StringBuffer("# robots.txt \n");
701       robotsContent.append("User-agent: * \n");
702       robotsContent.append("Disallow: \n");
703       if(ctx.getRequest() != null) {
704         if (ctx.getRequest().getServerPort() != 80) {
705           robotsContent.append("Sitemap: ")
706           .append(ctx.getRequest().getScheme())
707           .append("://")
708           .append(ctx.getRequest().getServerName())
709           .append(":")
710           .append(ctx.getRequest().getServerPort())
711           .append(ctx.getPortalURI())
712           .append("sitemaps.xml \n");
713         } else {
714           robotsContent.append("Sitemap: ")
715           .append(ctx.getRequest().getScheme())
716           .append("://")
717           .append(ctx.getRequest().getServerName())
718           .append(ctx.getPortalURI())
719           .append("sitemaps.xml \n");
720         }
721       }
722       robotsNode.setProperty("jcr:data", robotsContent.toString());
723       robotsNode.setProperty("jcr:lastModified", new GregorianCalendar());
724       cache.put(getRobotsCacheKey(portalName), robotsContent.toString());
725     }
726   }
727 
728   private String getRobotsCacheKey(String portalName) throws Exception {
729     return getHash(portalName + ROBOTS_NAME);
730   }
731 
732   /**
733    * {@inheritDoc}
734    */
735   public String getSitemap(String portalName) throws Exception {
736     String hash = getSiteMapCacheKey(portalName);
737     String sitemapContent = (String) getCachedEntry(hash, false);
738 
739     if (sitemapContent == null || sitemapContent.length() == 0) {
740       SessionProvider sessionProvider = WCMCoreUtils.getSystemSessionProvider();
741       LivePortalManagerService livePortalManagerService = WCMCoreUtils
742           .getService(LivePortalManagerService.class);
743       Node dummyNode = livePortalManagerService.getLivePortal(sessionProvider,
744                                                               portalName);
745       Session session = dummyNode.getSession();
746       if (dummyNode.hasNode(METADATA_BASE_PATH)
747           && dummyNode.getNode(METADATA_BASE_PATH).hasNode(SITEMAP_NAME)) {
748         Node sitemapFolder = dummyNode.getNode(METADATA_BASE_PATH + "/"
749             + SITEMAP_NAME);
750         Node sitemapNode = sitemapFolder.getNode("jcr:content");
751         DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory
752             .newInstance();
753         DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
754         InputStream stream = sitemapNode.getProperty("jcr:data").getStream();
755         Document doc = docBuilder.parse(stream);
756         TransformerFactory transformerFactory = TransformerFactory
757             .newInstance();
758         Transformer transformer = transformerFactory.newTransformer();
759         DOMSource source = new DOMSource(doc);
760         StringWriter writer = new StringWriter();
761         transformer.transform(source, new StreamResult(writer));
762         sitemapContent = writer.toString();
763       } else {
764         DocumentBuilderFactory docFactory = DocumentBuilderFactory
765             .newInstance();
766         DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
767         Document doc = docBuilder.newDocument();
768         Element rootElement = doc.createElement("urlset");
769         doc.appendChild(rootElement);
770         TransformerFactory transformerFactory = TransformerFactory
771             .newInstance();
772         Transformer transformer = transformerFactory.newTransformer();
773         DOMSource source = new DOMSource(doc);
774         StringWriter writer = new StringWriter();
775         transformer.transform(source, new StreamResult(writer));
776         sitemapContent = writer.toString();
777       }
778       session.save();
779       cache.put(hash, sitemapContent);
780     }
781     return sitemapContent;
782   }
783 
784   private String getSiteMapCacheKey(String portalName) throws Exception {
785     return getHash(portalName + SITEMAP_NAME);
786   }
787 
788   /**
789    * {@inheritDoc}
790    */
791   public String getRobots(String portalName) throws Exception {
792     String hash = getRobotsCacheKey(portalName);
793     String robotsCache = (String) getCachedEntry(hash, false);
794 
795     if (robotsCache != null && robotsCache.trim().length() > 0) {
796       return robotsCache;
797     }
798 
799     StringBuffer robotsContent = null;
800     SessionProvider sessionProvider = WCMCoreUtils.getSystemSessionProvider();
801     LivePortalManagerService livePortalManagerService = WCMCoreUtils.getService(LivePortalManagerService.class);
802     Node dummyNode = livePortalManagerService.getLivePortal(sessionProvider, portalName);
803     Session session = dummyNode.getSession();
804     if (dummyNode.hasNode(METADATA_BASE_PATH)
805         && dummyNode.getNode(METADATA_BASE_PATH).hasNode(ROBOTS_NAME)) {
806       Node robotsFolder = dummyNode.getNode(METADATA_BASE_PATH + "/" + ROBOTS_NAME);
807       Node robotsNode = robotsFolder.getNode("jcr:content");
808       robotsContent = new StringBuffer(robotsNode.getProperty("jcr:data").getValue().getString());
809     } else {
810       robotsContent = new StringBuffer("# robots.txt \n");
811       robotsContent.append("User-agent: * \n");
812       robotsContent.append("Disallow: \n");
813     }
814     session.save();
815     cache.put(hash, robotsContent.toString());
816     return robotsContent.toString();
817   }
818 
819   /**
820    * Returns hash
821    *
822    * @param uri The uri of page
823    * @return
824    * @throws Exception
825    */
826   public String getHash(String uri) throws Exception {
827     String key = uri;
828     return MessageDigester.getHash(key);
829   }
830 
831   private String getStandardURL(String path) throws Exception {
832     if(path != null) {
833       if (path.substring(path.length() - 1, path.length()).equals("/"))
834         path = path.substring(0, path.length() - 1);
835     }
836     return path;
837   }
838 
839   public Node getContentNode(String seoPath) throws Exception {
840     Node seoNode = null;
841     if (seoPath != null && seoPath.length() > 0) {
842       String tmpPath = seoPath.trim();
843       if (tmpPath.startsWith("/"))
844         tmpPath = tmpPath.substring(1, tmpPath.length());
845       String[] arrPath = tmpPath.split("/");
846       if (arrPath != null && arrPath.length > 3) {
847         String repo = arrPath[0];
848         String ws = arrPath[1];
849         if (repo != null && ws != null) {
850           boolean isWs = false;
851           String nodePath = tmpPath.substring(
852                                               tmpPath.indexOf(ws) + ws.length(), tmpPath.length());
853           if (nodePath != null && nodePath.length() > 0) {
854             ManageableRepository manageRepo = WCMCoreUtils.getRepository();
855             List<WorkspaceEntry> wsList = manageRepo.getConfiguration().getWorkspaceEntries();
856             for (int i = 0; i < wsList.size(); i++) {
857               WorkspaceEntry wsEntry = (WorkspaceEntry) wsList.get(i);
858               if (wsEntry.getName().equals(ws)) {
859                 isWs = true;
860                 break;
861               }
862             }
863             if (isWs) {
864               Session session = WCMCoreUtils.getUserSessionProvider()
865                   .getSession(ws, manageRepo);
866               nodePath = nodePath.replaceAll("//", "/");
867               if (session.getItem(nodePath) != null) {
868                 if (session.getItem(nodePath).isNode()) {
869                   seoNode = (Node) session.getItem(nodePath);
870                 }
871               }
872             }
873           }
874         }
875       }
876     }
877     return seoNode;
878   }
879 
880   @Managed
881   @ManagedDescription("Is the cache used ?")
882   public boolean isCached() {
883     return isCached;
884   }
885 
886   @Managed
887   @ManagedDescription("How many nodes in the cache ?")
888   public int getCachedEntries() {
889     return this.cache.getCacheSize();
890   }
891 
892   @Managed
893   @ManagedDescription("Activate/deactivate the composer cache ?")
894   public void setCached(
895                         @ManagedDescription("Enable/Disable the cache ?") @ManagedName("isCached") boolean isCached) {
896     this.isCached = isCached;
897   }
898 }