SEOServiceImpl.java

  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. import java.io.InputStream;
  19. import java.io.StringWriter;
  20. import java.util.ArrayList;
  21. import java.util.Collections;
  22. import java.util.Comparator;
  23. import java.util.GregorianCalendar;
  24. import java.util.List;
  25. import java.util.Locale;

  26. import javax.jcr.Node;
  27. import javax.jcr.NodeIterator;
  28. import javax.jcr.Session;
  29. import javax.xml.parsers.DocumentBuilder;
  30. import javax.xml.parsers.DocumentBuilderFactory;
  31. import javax.xml.transform.Transformer;
  32. import javax.xml.transform.TransformerFactory;
  33. import javax.xml.transform.dom.DOMSource;
  34. import javax.xml.transform.stream.StreamResult;

  35. import org.exoplatform.container.xml.InitParams;
  36. import org.exoplatform.container.xml.ObjectParameter;
  37. import org.exoplatform.ecm.utils.MessageDigester;
  38. import org.exoplatform.management.annotations.Managed;
  39. import org.exoplatform.management.annotations.ManagedDescription;
  40. import org.exoplatform.management.annotations.ManagedName;
  41. import org.exoplatform.portal.application.PortalRequestContext;
  42. import org.exoplatform.portal.webui.util.Util;
  43. import org.exoplatform.services.cache.CacheService;
  44. import org.exoplatform.services.cache.ExoCache;
  45. import org.exoplatform.services.jcr.config.WorkspaceEntry;
  46. import org.exoplatform.services.jcr.core.ManageableRepository;
  47. import org.exoplatform.services.jcr.ext.common.SessionProvider;
  48. import org.exoplatform.services.seo.PageMetadataModel;
  49. import org.exoplatform.services.seo.SEOConfig;
  50. import org.exoplatform.services.seo.SEOService;
  51. import org.exoplatform.services.wcm.portal.LivePortalManagerService;
  52. import org.exoplatform.services.wcm.utils.WCMCoreUtils;

  53. import org.apache.commons.lang.StringUtils;
  54. import org.w3c.dom.Document;
  55. import org.w3c.dom.Element;

  56. /**
  57.  * Created by The eXo Platform SAS Author : eXoPlatform exo@exoplatform.com Jun
  58.  * 17, 2011
  59.  */
  60. public class SEOServiceImpl implements SEOService {
  61.   private ExoCache<String, Object> cache;

  62.   public static final String EMPTY_CACHE_ENTRY = "EMPTY";
  63.   public static String METADATA_BASE_PATH = "SEO";
  64.   final static public String LANGUAGES    = "seo-languages";
  65.   public static String METADATA_PAGE_PATH = "pages";
  66.   public static String METADATA_CONTENT_PATH = "contents";
  67.   public static String SITEMAP_NAME = "sitemaps";
  68.   public static String ROBOTS_NAME = "robots";
  69.   private static String PUBLIC_MODE = "public";
  70.   private static String PRIVATE_MODE = "private";

  71.   private final static String CACHE_NAME = "ecms.seo";

  72.   private List<String> robotsindex = new ArrayList<String>();
  73.   private List<String> robotsfollow = new ArrayList<String>();
  74.   private List<String> frequency = new ArrayList<String>();
  75.   private SEOConfig seoConfig = null;

  76.   private boolean isCached = true;

  77.   /**
  78.    * Constructor method
  79.    *
  80.    * @param initParams
  81.    *          The initial parameters
  82.    * @throws Exception
  83.    */
  84.   public SEOServiceImpl(InitParams initParams) throws Exception {
  85.     ObjectParameter param = initParams.getObjectParam("seo.config");
  86.     if (param != null) {
  87.       seoConfig = (SEOConfig) param.getObject();
  88.       robotsindex = seoConfig.getRobotsIndex();
  89.       robotsfollow = seoConfig.getRobotsFollow();
  90.       frequency = seoConfig.getFrequency();
  91.     }
  92.     cache = WCMCoreUtils.getService(CacheService.class).getCacheInstance(
  93.             CACHE_NAME);
  94.   }

  95.   /**
  96.    *{@inheritDoc}
  97.    */
  98.   public List<String> getRobotsIndexOptions() {
  99.     return robotsindex;
  100.   }

  101.   /**
  102.    * {@inheritDoc}
  103.    */
  104.   public List<String> getRobotsFollowOptions() {
  105.     return robotsfollow;
  106.   }

  107.   /**
  108.    * {@inheritDoc}
  109.    */
  110.   public List<String> getFrequencyOptions() {
  111.     return frequency;
  112.   }

  113.   /**
  114.    * {@inheritDoc}
  115.    */
  116.   public void storeMetadata(PageMetadataModel metaModel, String portalName,
  117.                             boolean onContent, String language) throws Exception {
  118.     String uri = metaModel.getUri();
  119.     String cachedHash = getPageOrContentCacheKey(uri, language);
  120.     if (cache.get(cachedHash) != null) {
  121.       cache.remove(cachedHash);
  122.     }
  123.     String pageReference = metaModel.getPageReference();
  124.     cachedHash = getPageOrContentCacheKey(metaModel.getPageReference(), language);
  125.     if (cache.get(cachedHash) != null) {
  126.       cache.remove(cachedHash);
  127.     }

  128.     // Inherit from parent page
  129.     /*
  130.      * if(!onContent) { if(metaModel.getPageParent() != null &&
  131.      * metaModel.getPageParent().length() > 0) { PageMetadataModel parentModel =
  132.      * this.getPageMetadata(metaModel.getPageParent()); if(parentModel != null)
  133.      * { metaModel = parentModel; metaModel.setUri(uri);
  134.      * metaModel.setPageReference(pageReference); } } }
  135.      */
  136.     String title = metaModel.getTitle();
  137.     String keyword = metaModel.getKeywords();
  138.     String description = metaModel.getDescription();
  139.     String robots = metaModel.getRobotsContent();
  140.     String fullStatus = metaModel.getFullStatus();
  141.     boolean sitemap = metaModel.getSitemap();
  142.     float priority = metaModel.getPriority();
  143.     String frequency = metaModel.getFrequency();
  144.     SessionProvider sessionProvider = WCMCoreUtils.getSystemSessionProvider();
  145.     Session session = null;
  146.     LivePortalManagerService livePortalManagerService = WCMCoreUtils
  147.         .getService(LivePortalManagerService.class);
  148.     Node dummyNode = livePortalManagerService.getLivePortal(sessionProvider,
  149.                                                             portalName);
  150.     session = dummyNode.getSession();    
  151.     if (!dummyNode.hasNode(METADATA_BASE_PATH)) {
  152.       dummyNode.addNode(METADATA_BASE_PATH);
  153.       session.save();
  154.     }
  155.     //Store robots data
  156.     updateRobots(dummyNode, portalName);    
  157.     // Store sitemap data
  158.     Node node = null;
  159.     if (onContent) {
  160.       node = session.getNodeByUUID(uri);
  161.       if (!node.isNodeType("mix:referenceable")) {
  162.         node.addMixin("mix:referenceable");
  163.       }
  164.     } else {
  165.       session = sessionProvider.getSession("portal-system", WCMCoreUtils
  166.                                            .getRepository());
  167.       String uuid = Util.getUIPortal().getSelectedUserNode().getId();
  168.       node = session.getNodeByUUID(uuid);
  169.       if (!node.isNodeType("exo:seoMetadata")) {
  170.         node.addMixin("exo:seoMetadata");
  171.       }
  172.     }    

  173.     Node languageNode = null;
  174.     if(node.hasNode(LANGUAGES))
  175.       languageNode = node.getNode(LANGUAGES);
  176.     else
  177.       languageNode = node.addNode(LANGUAGES);
  178.     if(languageNode.canAddMixin("exo:hiddenable"))
  179.       languageNode.addMixin("exo:hiddenable");
  180.     session.save();
  181.     Node seoNode = null;
  182.     if(languageNode.hasNode(language)) seoNode = languageNode.getNode(language);
  183.     else seoNode = languageNode.addNode(language);
  184.     if (!seoNode.isNodeType("mix:referenceable")) {
  185.       seoNode.addMixin("mix:referenceable");
  186.     }
  187.     if (seoNode.isNodeType("exo:pageMetadata")) {
  188.       seoNode.setProperty("exo:metaKeywords", keyword);
  189.       seoNode.setProperty("exo:metaDescription", description);
  190.       seoNode.setProperty("exo:metaFully", fullStatus);
  191.       if (!onContent) {
  192.         seoNode.setProperty("exo:metaTitle", title);
  193.         seoNode.setProperty("exo:metaRobots", robots);
  194.         seoNode.setProperty("exo:metaSitemap", sitemap);
  195.         seoNode.setProperty("exo:metaPriority", priority);
  196.         seoNode.setProperty("exo:metaFrequency", frequency);
  197.         updateSiteMap(uri, priority, frequency, sitemap, portalName);
  198.       }
  199.       String hash = null;
  200.       if (onContent)
  201.         hash = cachedHash;
  202.       else
  203.         hash = getPageOrContentCacheKey(pageReference, language);
  204.       if (hash != null)
  205.         cache.put(hash, metaModel);
  206.     } else {
  207.       String hash = null;
  208.       seoNode.addMixin("exo:pageMetadata");
  209.       seoNode.setProperty("exo:metaKeywords", keyword);
  210.       seoNode.setProperty("exo:metaDescription", description);
  211.       seoNode.setProperty("exo:metaFully", fullStatus);
  212.       if (onContent) {
  213.         seoNode.setProperty("exo:metaUri", seoNode.getUUID());
  214.         hash = getHash(seoNode.getUUID() + language);
  215.       } else {          
  216.         seoNode.setProperty("exo:metaTitle", title);
  217.         seoNode.setProperty("exo:metaUri", pageReference);
  218.         seoNode.setProperty("exo:metaRobots", robots);
  219.         seoNode.setProperty("exo:metaSitemap", sitemap);
  220.         seoNode.setProperty("exo:metaPriority", priority);
  221.         seoNode.setProperty("exo:metaFrequency", frequency);
  222.         updateSiteMap(uri, priority, frequency, sitemap, portalName);
  223.         hash = getPageOrContentCacheKey(pageReference, language);
  224.       }
  225.       if (hash != null)
  226.         cache.put(hash, metaModel);
  227.     }
  228.     session.save();
  229.   }

  230.   private String getPageOrContentCacheKey(String uri, String language) throws Exception {
  231.     return getHash(uri + language);
  232.   }

  233.   public String getState(String path, String language, boolean onContent) throws Exception{
  234.     String state = "Empty";
  235.     Node node = null;
  236.     String hash = null;
  237.     PageMetadataModel metaModel = null;
  238.     if(onContent) {
  239.       node = getContentNode(path);
  240.       hash = node == null ? StringUtils.EMPTY : getPageOrContentCacheKey(node.getUUID(), language);
  241.       metaModel = (PageMetadataModel) getCachedEntry(hash, false);
  242.       if(metaModel != null) return metaModel.getFullStatus();

  243.     } else {
  244.       hash = getPageOrContentCacheKey(path, language);
  245.       metaModel = (PageMetadataModel) getCachedEntry(hash, true);
  246.       if (isNullObject(metaModel)) {
  247.         return null;
  248.       }
  249.       if(metaModel != null) return metaModel.getFullStatus();
  250.       SessionProvider sessionProvider = WCMCoreUtils.getSystemSessionProvider();
  251.       Session session = sessionProvider.getSession("portal-system",
  252.                                                    WCMCoreUtils.getRepository());
  253.       String uuid = Util.getUIPortal().getSelectedUserNode().getId();
  254.       node = session.getNodeByUUID(uuid);
  255.     }
  256.     if(node.hasNode(LANGUAGES+"/"+language)) {
  257.       Node seoNode = node.getNode(LANGUAGES+"/"+language);
  258.       if (seoNode.isNodeType("exo:pageMetadata") && seoNode.hasProperty("exo:metaFully"))
  259.         return seoNode.getProperty("exo:metaFully").getString();            
  260.     }
  261.     return state;
  262.   }

  263.   private boolean isNullObject(PageMetadataModel metaModel) {
  264.     return metaModel == PageMetadataModel.NULL_PAGE_METADATA_MODEL;
  265.   }

  266.   private Object getCachedEntry(String hash, boolean cacheNull) {
  267.     Object object = cache.get(hash);
  268.     if (cacheNull) {
  269.       if( object == null) {
  270.         cache.put(hash, EMPTY_CACHE_ENTRY);
  271.       } else if(EMPTY_CACHE_ENTRY.equals(object)) {
  272.         return PageMetadataModel.NULL_PAGE_METADATA_MODEL;
  273.       }
  274.     }
  275.     return object;
  276.   }

  277.   public PageMetadataModel getMetadata(ArrayList<String> params,
  278.                                        String pageReference, String language) throws Exception {
  279.     PageMetadataModel metaModel = null;
  280.     if (params != null) {
  281.       metaModel = getContentMetadata(params, language);
  282.       if(metaModel == null) metaModel = getPageMetadata(pageReference, language);
  283.     } else {
  284.       metaModel = getPageMetadata(pageReference, language);
  285.     }
  286.     return metaModel;
  287.   }

  288.   /**
  289.    * {@inheritDoc}
  290.    */
  291.   public PageMetadataModel getContentMetadata(ArrayList<String> params, String language)
  292.       throws Exception {
  293.     PageMetadataModel metaModel = null;
  294.     String pageUri = null;
  295.     Node contentNode = null;
  296.     for (int i = 0; i < params.size(); i++) {
  297.       contentNode = this.getContentNode(params.get(i).toString());
  298.       if (contentNode != null)
  299.         break;
  300.     }

  301.     if (contentNode == null)
  302.       return null;
  303.     if (!contentNode.isNodeType("mix:referenceable")) {
  304.       contentNode.addMixin("mix:referenceable");
  305.     }
  306.     String hash = getPageOrContentCacheKey(contentNode.getUUID(), language);
  307.     metaModel = (PageMetadataModel) getCachedEntry(hash, false);

  308.     if (metaModel == null && contentNode.hasNode(LANGUAGES+"/"+language)) {
  309.       //Getting seo node by language
  310.       Node seoNode = contentNode.getNode(LANGUAGES+"/"+language);
  311.       if (!seoNode.isNodeType("mix:referenceable")) {
  312.         seoNode.addMixin("mix:referenceable");
  313.       }
  314.       if (seoNode.isNodeType("exo:pageMetadata")) {
  315.         metaModel = new PageMetadataModel();
  316.         metaModel.setUri(pageUri);        
  317.         if (seoNode.hasProperty("exo:metaKeywords"))
  318.           metaModel.setKeywords((seoNode.getProperty("exo:metaKeywords"))
  319.                                 .getString());
  320.         if (seoNode.hasProperty("exo:metaDescription"))
  321.           metaModel.setDescription((seoNode
  322.               .getProperty("exo:metaDescription")).getString());
  323.         if (seoNode.hasProperty("exo:metaRobots"))
  324.           metaModel
  325.           .setRobotsContent((seoNode.getProperty("exo:metaRobots"))
  326.                             .getString());
  327.         if (seoNode.hasProperty("exo:metaSitemap"))
  328.           metaModel.setSiteMap(Boolean.parseBoolean((seoNode
  329.               .getProperty("exo:metaSitemap")).getString()));
  330.         if (seoNode.hasProperty("exo:metaPriority"))
  331.           metaModel.setPriority(Long.parseLong((seoNode
  332.               .getProperty("exo:metaPriority")).getString()));
  333.         if (seoNode.hasProperty("exo:metaFrequency"))
  334.           metaModel.setFrequency((seoNode.getProperty("exo:metaFrequency"))
  335.                                  .getString());
  336.         if (seoNode.hasProperty("exo:metaFully"))
  337.           metaModel.setFullStatus((seoNode.getProperty("exo:metaFully"))
  338.                                   .getString());
  339.         cache.put(hash, metaModel);
  340.       }
  341.     }
  342.     return metaModel;
  343.   }

  344.   /**
  345.    * {@inheritDoc}
  346.    */
  347.   public PageMetadataModel getPageMetadata(String pageUri, String language) throws Exception {
  348.     PageMetadataModel metaModel = null;
  349.     String hash = getPageOrContentCacheKey(pageUri, language);
  350.     metaModel = (PageMetadataModel) getCachedEntry(hash, true);
  351.     if (isNullObject(metaModel)) {
  352.       return null;
  353.     }
  354.     if (metaModel == null) {
  355.       SessionProvider sessionProvider = WCMCoreUtils.getSystemSessionProvider();
  356.       Session session = sessionProvider.getSession("portal-system",
  357.                                                    WCMCoreUtils.getRepository());
  358.       String uuid = Util.getUIPortal().getSelectedUserNode().getId();
  359.       Node pageNode = session.getNodeByUUID(uuid);

  360.       if (pageNode != null && pageNode.hasNode(LANGUAGES+"/"+language)) {
  361.         Node seoNode = pageNode.getNode(LANGUAGES+"/"+language);
  362.         if(seoNode.isNodeType("exo:pageMetadata")) {
  363.           metaModel = new PageMetadataModel();
  364.           if (seoNode.hasProperty("exo:metaTitle"))
  365.             metaModel.setTitle((seoNode.getProperty("exo:metaTitle")).getString());
  366.           if (seoNode.hasProperty("exo:metaKeywords"))
  367.             metaModel.setKeywords((seoNode.getProperty("exo:metaKeywords"))
  368.                                   .getString());
  369.           if (seoNode.hasProperty("exo:metaDescription"))
  370.             metaModel
  371.             .setDescription((seoNode.getProperty("exo:metaDescription"))
  372.                             .getString());
  373.           if (seoNode.hasProperty("exo:metaRobots"))
  374.             metaModel.setRobotsContent((seoNode.getProperty("exo:metaRobots"))
  375.                                        .getString());
  376.           if (seoNode.hasProperty("exo:metaSitemap"))
  377.             metaModel.setSiteMap(Boolean.parseBoolean((seoNode
  378.                 .getProperty("exo:metaSitemap")).getString()));
  379.           if (seoNode.hasProperty("exo:metaPriority"))
  380.             metaModel.setPriority(Long.parseLong((seoNode
  381.                 .getProperty("exo:metaPriority")).getString()));
  382.           if (seoNode.hasProperty("exo:metaFrequency"))
  383.             metaModel.setFrequency((seoNode.getProperty("exo:metaFrequency"))
  384.                                    .getString());
  385.           if (seoNode.hasProperty("exo:metaFully"))
  386.             metaModel.setFullStatus((seoNode.getProperty("exo:metaFully"))
  387.                                     .getString());
  388.           cache.put(hash, metaModel);
  389.         }
  390.       }
  391.     }
  392.     return metaModel;
  393.   }

  394.   public List<Locale> getSEOLanguages(String portalName, String seoPath, boolean onContent) throws Exception {      
  395.     List<Locale> languages = new ArrayList<Locale>();
  396.     Node languagesNode = null;
  397.     if(onContent) {
  398.       Node contentNode = null;
  399.       contentNode = getContentNode(seoPath);
  400.       if (contentNode != null && contentNode.hasNode(LANGUAGES)) languagesNode = contentNode.getNode(LANGUAGES);    
  401.     } else {
  402.       SessionProvider sessionProvider = WCMCoreUtils.getSystemSessionProvider();
  403.       Session session = sessionProvider.getSession("portal-system",
  404.                                                    WCMCoreUtils.getRepository());
  405.       String uuid = Util.getUIPortal().getSelectedUserNode().getId();
  406.       Node pageNode = session.getNodeByUUID(uuid);
  407.       if (pageNode != null && pageNode.hasNode(LANGUAGES)) languagesNode = pageNode.getNode(LANGUAGES);
  408.     }
  409.     if(languagesNode != null) {
  410.       NodeIterator iter = languagesNode.getNodes();
  411.       while(iter.hasNext()) {  
  412.         String lang = iter.nextNode().getName();
  413.         String[] arr = lang.split("_");
  414.         if(arr.length > 1) {
  415.           languages.add(new Locale(arr[0],arr[1]));            
  416.         } else languages.add(new Locale(lang));
  417.       }    
  418.       Collections.sort(languages, new SEOItemComparator());
  419.       return languages;
  420.     }
  421.     return languages;
  422.   }

  423.   class SEOItemComparator implements Comparator<Locale> {
  424.     @Override
  425.     public int compare(Locale locale1, Locale locale2) {
  426.       return locale1.getDisplayName().compareTo(locale2.getDisplayName());
  427.     }
  428.   }

  429.   /**
  430.    * {@inheritDoc}
  431.    */
  432.   public void removePageMetadata(PageMetadataModel metaModel,
  433.                                  String portalName, boolean onContent, String language) throws Exception {
  434.     SessionProvider sessionProvider = WCMCoreUtils.getSystemSessionProvider();
  435.     ManageableRepository currentRepo = WCMCoreUtils.getRepository();
  436.     Session session = sessionProvider.getSession(currentRepo.getConfiguration()
  437.                                                  .getDefaultWorkspaceName(), currentRepo);
  438.     String hash = "";
  439.     Node node = null;
  440.     if (onContent) {
  441.       node = session.getNodeByUUID(metaModel.getUri());
  442.     } else {
  443.       session = sessionProvider.getSession("portal-system", WCMCoreUtils
  444.                                            .getRepository());
  445.       String uuid = Util.getUIPortal().getSelectedUserNode().getId();
  446.       node = session.getNodeByUUID(uuid);
  447.     }    
  448.     Node seoNode = null;
  449.     if(node.hasNode(LANGUAGES+"/"+language))
  450.       seoNode = node.getNode(LANGUAGES+"/"+language);

  451.     if (seoNode != null) {
  452.       seoNode.remove();
  453.       if (onContent)
  454.         hash = getHash(metaModel.getUri() + language);
  455.       else
  456.         hash = getHash(metaModel.getPageReference() + language);
  457.       cache.remove(hash);
  458.     }
  459.     session.save();
  460.   }

  461.   /**
  462.    * Update sitemap content for portal
  463.    *
  464.    * @param uri
  465.    *          The uri of page
  466.    * @param priority
  467.    *          The priority of page
  468.    * @param frequency
  469.    *          The frequency of page
  470.    * @param visibleSitemap
  471.    *          visibleSitemap = true page is visible on sitemap visibleSitemap =
  472.    *          false page is invisible on sitemap
  473.    * @param portalName
  474.    *          The portal name
  475.    * @throws Exception
  476.    */
  477.   public void updateSiteMap(String uri, float priority, String frequency,
  478.                             boolean visibleSitemap, String portalName) throws Exception {
  479.     SessionProvider sessionProvider = WCMCoreUtils.getSystemSessionProvider();
  480.     LivePortalManagerService livePortalManagerService = WCMCoreUtils
  481.         .getService(LivePortalManagerService.class);
  482.     Node dummyNode = livePortalManagerService.getLivePortal(sessionProvider,
  483.                                                             portalName);
  484.     Session session = dummyNode.getSession();
  485.     uri = getStandardURL(uri);
  486.     if(uri == null) uri = "";
  487.     String uri_clone = "";
  488.     String public_path = "/" + PUBLIC_MODE + "/";
  489.     String private_path = "/" + PRIVATE_MODE + "/";
  490.     if (uri.indexOf(public_path) > 0)
  491.       uri_clone = uri.replaceFirst(public_path, private_path);
  492.     else if (uri.indexOf(private_path) > 0)
  493.       uri_clone = uri.replaceFirst(private_path, public_path);

  494.     String sitemapData = "";
  495.     if (!dummyNode.getNode(METADATA_BASE_PATH).hasNode(SITEMAP_NAME)) {
  496.       dummyNode.getNode(METADATA_BASE_PATH).addNode(SITEMAP_NAME, "nt:file");
  497.       Node simapFolder = dummyNode.getNode(METADATA_BASE_PATH + "/"
  498.           + SITEMAP_NAME);
  499.       Node sitemapNode = simapFolder.addNode("jcr:content", "nt:resource");
  500.       sitemapNode.setProperty("jcr:mimeType", "text/xml");
  501.       DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
  502.       DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
  503.       // root elements
  504.       Document doc = docBuilder.newDocument();
  505.       Element rootElement = doc.createElement("urlset");
  506.       rootElement.setAttribute("xmlns",
  507.           "http://www.sitemaps.org/schemas/sitemap/0.9");
  508.       rootElement.setAttribute("xmlns:xsi",
  509.           "http://www.w3.org/2001/XMLSchema-instance");
  510.       rootElement
  511.       .setAttribute(
  512.                     "xsi:schemaLocation",
  513.           "http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd");
  514.       doc.appendChild(rootElement);
  515.       if (visibleSitemap) {
  516.         // Create element in sitemap for uri
  517.         Element urlElement = doc.createElement("url");
  518.         rootElement.appendChild(urlElement);
  519.         Element locElement = doc.createElement("loc");
  520.         locElement.setTextContent(uri);
  521.         urlElement.appendChild(locElement);
  522.         Element freqElement = doc.createElement("changefreq");
  523.         freqElement.setTextContent(frequency);
  524.         urlElement.appendChild(freqElement);
  525.         Element priorityElement = doc.createElement("priority");
  526.         if(priority >= 0) {        
  527.           priorityElement.setTextContent(String.valueOf(priority));
  528.           urlElement.appendChild(priorityElement);
  529.         }
  530.         // Create element in sitemap for uri_clone
  531.         if (uri_clone != null && uri_clone.length() > 0) {
  532.           urlElement = doc.createElement("url");
  533.           rootElement.appendChild(urlElement);
  534.           locElement = doc.createElement("loc");
  535.           locElement.setTextContent(uri_clone);
  536.           urlElement.appendChild(locElement);
  537.           freqElement = doc.createElement("changefreq");
  538.           freqElement.setTextContent(frequency);
  539.           urlElement.appendChild(freqElement);          
  540.           if(priority >= 0) {
  541.             priorityElement = doc.createElement("priority");
  542.             priorityElement.setTextContent(String.valueOf(priority));
  543.             urlElement.appendChild(priorityElement);
  544.           }
  545.         }
  546.       }

  547.       TransformerFactory transformerFactory = TransformerFactory.newInstance();
  548.       Transformer transformer = transformerFactory.newTransformer();
  549.       DOMSource source = new DOMSource(doc);
  550.       StringWriter writer = new StringWriter();
  551.       transformer.transform(source, new StreamResult(writer));
  552.       sitemapData = writer.toString();
  553.       sitemapNode.setProperty("jcr:data", sitemapData);
  554.       sitemapNode.setProperty("jcr:lastModified", new GregorianCalendar());
  555.       session.save();
  556.     } else {
  557.       Node sitemapFolder = dummyNode.getNode(METADATA_BASE_PATH + "/"
  558.           + SITEMAP_NAME);
  559.       Node sitemapNode = sitemapFolder.getNode("jcr:content");
  560.       DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory
  561.           .newInstance();
  562.       DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
  563.       InputStream stream = sitemapNode.getProperty("jcr:data").getStream();
  564.       Document doc = docBuilder.parse(stream);

  565.       // normalize text representation
  566.       boolean fLoc = false;
  567.       doc.getDocumentElement().normalize();
  568.       Element root = doc.getDocumentElement();
  569.       ArrayList<org.w3c.dom.Node> arrNodes = new ArrayList<org.w3c.dom.Node>();
  570.       org.w3c.dom.NodeList listOfUrls = doc.getElementsByTagName("url");
  571.       for (int i = 0; i < listOfUrls.getLength(); i++) {
  572.         org.w3c.dom.Node urlNode = listOfUrls.item(i);
  573.         if (urlNode.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE) {
  574.           Element urlElement = (Element) urlNode;
  575.           org.w3c.dom.NodeList locList = urlElement.getElementsByTagName("loc");
  576.           Element locElement = (Element) locList.item(0);
  577.           // The location is exist
  578.           if(locElement.getChildNodes().item(0) != null) {
  579.             String locationValue = locElement.getChildNodes().item(0).getNodeValue();
  580.             if (locationValue != null & (locationValue.trim().equals(uri) || locationValue.trim().equals(uri_clone))) {
  581.               fLoc = true;
  582.               if (visibleSitemap) {
  583.                 org.w3c.dom.Node freqNode = urlElement.getElementsByTagName(
  584.                     "changefreq").item(0);
  585.                 freqNode.setTextContent(frequency);              
  586.                 if(priority >= 0) {
  587.                   org.w3c.dom.Node priorityNode = urlElement.getElementsByTagName("priority").item(0);
  588.                   if(priorityNode == null) {
  589.                     Element priorityElement = doc.createElement("priority");
  590.                     priorityElement.setTextContent(String.valueOf(priority));
  591.                     urlElement.appendChild(priorityElement);
  592.                   } else priorityNode.setTextContent(String.valueOf(priority));
  593.                 }
  594.               } else {
  595.                 arrNodes.add(urlNode);
  596.               }
  597.             }
  598.           }
  599.         }
  600.       }
  601.       // Remove element from sitemap.xml
  602.       if (arrNodes != null && arrNodes.size() > 0) {
  603.         for (int i = 0; i < arrNodes.size(); i++) {
  604.           root.removeChild(arrNodes.get(i));
  605.         }
  606.       }
  607.       // Update xml document for sitemap
  608.       if (!fLoc && visibleSitemap) {
  609.         // Create element in sitemap for uri
  610.         Element urlElement = doc.createElement("url");
  611.         Element locElement = doc.createElement("loc");
  612.         locElement.setTextContent(uri);
  613.         Element freqElement = doc.createElement("changefreq");
  614.         freqElement.setTextContent(frequency);
  615.         urlElement.appendChild(locElement);
  616.         urlElement.appendChild(freqElement);
  617.         Element priorityElement = doc.createElement("priority");
  618.         if(priority >= 0) {
  619.           priorityElement.setTextContent(String.valueOf(priority));
  620.           urlElement.appendChild(priorityElement);
  621.         }        
  622.         root.appendChild(urlElement);
  623.         // create element in sitemap for uri_clone
  624.         if (uri_clone != null && uri_clone.length() > 0) {
  625.           urlElement = doc.createElement("url");
  626.           locElement = doc.createElement("loc");
  627.           locElement.setTextContent(uri_clone);
  628.           freqElement = doc.createElement("changefreq");
  629.           freqElement.setTextContent(frequency);
  630.           urlElement.appendChild(locElement);
  631.           urlElement.appendChild(freqElement);
  632.           if(priority >= 0) {
  633.             priorityElement = doc.createElement("priority");
  634.             priorityElement.setTextContent(String.valueOf(priority));
  635.             urlElement.appendChild(priorityElement);
  636.           }          
  637.           root.appendChild(urlElement);
  638.         }
  639.       }
  640.       TransformerFactory transformerFactory = TransformerFactory.newInstance();
  641.       Transformer transformer = transformerFactory.newTransformer();
  642.       DOMSource source = new DOMSource(doc);
  643.       StringWriter writer = new StringWriter();
  644.       transformer.transform(source, new StreamResult(writer));
  645.       sitemapData = writer.toString();
  646.       sitemapNode.setProperty("jcr:data", sitemapData);
  647.       sitemapNode.setProperty("jcr:lastModified", new GregorianCalendar());
  648.     }

  649.     if (sitemapData != null && sitemapData.length() > 0) {
  650.       String hash = getSiteMapCacheKey(portalName);
  651.       cache.put(hash, sitemapData);
  652.     }
  653.     session.save();
  654.   }

  655.   public void updateRobots(Node dummyNode, String portalName) throws Exception {
  656.     if (!dummyNode.getNode(METADATA_BASE_PATH).hasNode(ROBOTS_NAME)) {
  657.       dummyNode.getNode(METADATA_BASE_PATH).addNode(ROBOTS_NAME, "nt:file");
  658.       Node robotsFolder = dummyNode.getNode(METADATA_BASE_PATH + "/"
  659.           + ROBOTS_NAME);
  660.       Node robotsNode = robotsFolder.addNode("jcr:content", "nt:resource");
  661.       robotsNode.setProperty("jcr:mimeType", "text/plain");      
  662.       PortalRequestContext ctx = Util.getPortalRequestContext();
  663.       StringBuffer robotsContent = new StringBuffer("# robots.txt \n");
  664.       robotsContent.append("User-agent: * \n");
  665.       robotsContent.append("Disallow: \n");
  666.       if(ctx.getRequest() != null) {
  667.         if (ctx.getRequest().getServerPort() != 80) {
  668.           robotsContent.append("Sitemap: ")
  669.           .append(ctx.getRequest().getScheme())
  670.           .append("://")
  671.           .append(ctx.getRequest().getServerName())
  672.           .append(":")
  673.           .append(ctx.getRequest().getServerPort())
  674.           .append(ctx.getPortalURI())
  675.           .append("sitemaps.xml \n");
  676.         } else {
  677.           robotsContent.append("Sitemap: ")
  678.           .append(ctx.getRequest().getScheme())
  679.           .append("://")
  680.           .append(ctx.getRequest().getServerName())
  681.           .append(ctx.getPortalURI())
  682.           .append("sitemaps.xml \n");
  683.         }
  684.       }
  685.       robotsNode.setProperty("jcr:data", robotsContent.toString());
  686.       robotsNode.setProperty("jcr:lastModified", new GregorianCalendar());
  687.       cache.put(getRobotsCacheKey(portalName), robotsContent.toString());
  688.     }
  689.   }

  690.   private String getRobotsCacheKey(String portalName) throws Exception {
  691.     return getHash(portalName + ROBOTS_NAME);
  692.   }

  693.   /**
  694.    * {@inheritDoc}
  695.    */
  696.   public String getSitemap(String portalName) throws Exception {
  697.     String hash = getSiteMapCacheKey(portalName);
  698.     String sitemapContent = (String) getCachedEntry(hash, false);

  699.     if (sitemapContent == null || sitemapContent.length() == 0) {
  700.       SessionProvider sessionProvider = WCMCoreUtils.getSystemSessionProvider();
  701.       LivePortalManagerService livePortalManagerService = WCMCoreUtils
  702.           .getService(LivePortalManagerService.class);
  703.       Node dummyNode = livePortalManagerService.getLivePortal(sessionProvider,
  704.                                                               portalName);
  705.       Session session = dummyNode.getSession();
  706.       if (dummyNode.hasNode(METADATA_BASE_PATH)
  707.           && dummyNode.getNode(METADATA_BASE_PATH).hasNode(SITEMAP_NAME)) {
  708.         Node sitemapFolder = dummyNode.getNode(METADATA_BASE_PATH + "/"
  709.             + SITEMAP_NAME);
  710.         Node sitemapNode = sitemapFolder.getNode("jcr:content");
  711.         DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory
  712.             .newInstance();
  713.         DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
  714.         InputStream stream = sitemapNode.getProperty("jcr:data").getStream();
  715.         Document doc = docBuilder.parse(stream);
  716.         TransformerFactory transformerFactory = TransformerFactory
  717.             .newInstance();
  718.         Transformer transformer = transformerFactory.newTransformer();
  719.         DOMSource source = new DOMSource(doc);
  720.         StringWriter writer = new StringWriter();
  721.         transformer.transform(source, new StreamResult(writer));
  722.         sitemapContent = writer.toString();
  723.       } else {
  724.         DocumentBuilderFactory docFactory = DocumentBuilderFactory
  725.             .newInstance();
  726.         DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
  727.         Document doc = docBuilder.newDocument();
  728.         Element rootElement = doc.createElement("urlset");
  729.         doc.appendChild(rootElement);
  730.         TransformerFactory transformerFactory = TransformerFactory
  731.             .newInstance();
  732.         Transformer transformer = transformerFactory.newTransformer();
  733.         DOMSource source = new DOMSource(doc);
  734.         StringWriter writer = new StringWriter();
  735.         transformer.transform(source, new StreamResult(writer));
  736.         sitemapContent = writer.toString();
  737.       }
  738.       session.save();
  739.       cache.put(hash, sitemapContent);
  740.     }
  741.     return sitemapContent;
  742.   }

  743.   private String getSiteMapCacheKey(String portalName) throws Exception {
  744.     return getHash(portalName + SITEMAP_NAME);
  745.   }

  746.   /**
  747.    * {@inheritDoc}
  748.    */
  749.   public String getRobots(String portalName) throws Exception {
  750.     String hash = getRobotsCacheKey(portalName);
  751.     String robotsCache = (String) getCachedEntry(hash, false);

  752.     if (robotsCache != null && robotsCache.trim().length() > 0) {
  753.       return robotsCache;
  754.     }

  755.     StringBuffer robotsContent = null;
  756.     SessionProvider sessionProvider = WCMCoreUtils.getSystemSessionProvider();
  757.     LivePortalManagerService livePortalManagerService = WCMCoreUtils.getService(LivePortalManagerService.class);
  758.     Node dummyNode = livePortalManagerService.getLivePortal(sessionProvider, portalName);
  759.     Session session = dummyNode.getSession();
  760.     if (dummyNode.hasNode(METADATA_BASE_PATH)
  761.         && dummyNode.getNode(METADATA_BASE_PATH).hasNode(ROBOTS_NAME)) {
  762.       Node robotsFolder = dummyNode.getNode(METADATA_BASE_PATH + "/" + ROBOTS_NAME);
  763.       Node robotsNode = robotsFolder.getNode("jcr:content");
  764.       robotsContent = new StringBuffer(robotsNode.getProperty("jcr:data").getValue().getString());
  765.     } else {
  766.       robotsContent = new StringBuffer("# robots.txt \n");
  767.       robotsContent.append("User-agent: * \n");
  768.       robotsContent.append("Disallow: \n");
  769.     }
  770.     session.save();
  771.     cache.put(hash, robotsContent.toString());
  772.     return robotsContent.toString();
  773.   }

  774.   /**
  775.    * Returns hash
  776.    *
  777.    * @param uri The uri of page
  778.    * @return
  779.    * @throws Exception
  780.    */
  781.   public String getHash(String uri) throws Exception {
  782.     String key = uri;
  783.     return MessageDigester.getHash(key);
  784.   }

  785.   private String getStandardURL(String path) throws Exception {
  786.     if(path != null) {
  787.       if (path.substring(path.length() - 1, path.length()).equals("/"))
  788.         path = path.substring(0, path.length() - 1);
  789.     }
  790.     return path;
  791.   }

  792.   public Node getContentNode(String seoPath) throws Exception {
  793.     Node seoNode = null;
  794.     if (seoPath != null && seoPath.length() > 0) {
  795.       String tmpPath = seoPath.trim();
  796.       if (tmpPath.startsWith("/"))
  797.         tmpPath = tmpPath.substring(1, tmpPath.length());
  798.       String[] arrPath = tmpPath.split("/");
  799.       if (arrPath != null && arrPath.length > 3) {
  800.         String repo = arrPath[0];
  801.         String ws = arrPath[1];
  802.         if (repo != null && ws != null) {
  803.           boolean isWs = false;
  804.           String nodePath = tmpPath.substring(
  805.                                               tmpPath.indexOf(ws) + ws.length(), tmpPath.length());
  806.           if (nodePath != null && nodePath.length() > 0) {
  807.             ManageableRepository manageRepo = WCMCoreUtils.getRepository();
  808.             List<WorkspaceEntry> wsList = manageRepo.getConfiguration().getWorkspaceEntries();
  809.             for (int i = 0; i < wsList.size(); i++) {
  810.               WorkspaceEntry wsEntry = (WorkspaceEntry) wsList.get(i);
  811.               if (wsEntry.getName().equals(ws)) {
  812.                 isWs = true;
  813.                 break;
  814.               }
  815.             }
  816.             if (isWs) {
  817.               Session session = WCMCoreUtils.getUserSessionProvider()
  818.                   .getSession(ws, manageRepo);
  819.               nodePath = nodePath.replaceAll("//", "/");
  820.               if (session.getItem(nodePath) != null) {
  821.                 if (session.getItem(nodePath).isNode()) {
  822.                   seoNode = (Node) session.getItem(nodePath);
  823.                 }
  824.               }
  825.             }
  826.           }
  827.         }
  828.       }
  829.     }
  830.     return seoNode;
  831.   }

  832.   @Managed
  833.   @ManagedDescription("Is the cache used ?")
  834.   public boolean isCached() {
  835.     return isCached;
  836.   }

  837.   @Managed
  838.   @ManagedDescription("How many nodes in the cache ?")
  839.   public int getCachedEntries() {
  840.     return this.cache.getCacheSize();
  841.   }

  842.   @Managed
  843.   @ManagedDescription("Activate/deactivate the composer cache ?")
  844.   public void setCached(
  845.                         @ManagedDescription("Enable/Disable the cache ?") @ManagedName("isCached") boolean isCached) {
  846.     this.isCached = isCached;
  847.   }
  848. }