/**
 *
 */
package org.exoplatform.services.wcm.publication;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import javax.jcr.*;
import javax.jcr.query.Query;
import javax.jcr.query.QueryManager;

import org.exoplatform.container.xml.InitParams;
import org.exoplatform.container.xml.ValueParam;
import org.exoplatform.management.annotations.Managed;
import org.exoplatform.management.annotations.ManagedDescription;
import org.exoplatform.management.jmx.annotations.NameTemplate;
import org.exoplatform.management.jmx.annotations.Property;
import org.exoplatform.management.rest.annotations.RESTEndpoint;
import org.exoplatform.portal.webui.util.Util;
import org.exoplatform.services.cms.documents.TrashService;
import org.exoplatform.services.cms.i18n.MultiLanguageService;
import org.exoplatform.services.cms.link.LinkManager;
import org.exoplatform.services.cms.taxonomy.TaxonomyService;
import org.exoplatform.services.cms.templates.TemplateService;
import org.exoplatform.services.ecm.publication.NotInPublicationLifecycleException;
import org.exoplatform.services.ecm.publication.PublicationPlugin;
import org.exoplatform.services.ecm.publication.PublicationService;
import org.exoplatform.services.jcr.RepositoryService;
import org.exoplatform.services.jcr.access.AccessControlEntry;
import org.exoplatform.services.jcr.access.PermissionType;
import org.exoplatform.services.jcr.core.ManageableRepository;
import org.exoplatform.services.jcr.ext.common.SessionProvider;
import org.exoplatform.services.jcr.impl.core.query.QueryImpl;
import org.exoplatform.services.jcr.sessions.ACLSessionProviderService;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
import org.exoplatform.services.security.ConversationState;
import org.exoplatform.services.security.Identity;
import org.exoplatform.services.security.IdentityConstants;
import org.exoplatform.services.wcm.core.NodeLocation;
import org.exoplatform.services.wcm.core.NodetypeConstant;
import org.exoplatform.services.wcm.core.WCMService;
import org.exoplatform.services.wcm.utils.WCMCoreUtils;
import org.picocontainer.Startable;

/**
 * The Class WCMComposerImpl.
 *
 * @author benjamin
 */
@Managed
@NameTemplate( { @Property(key = "view", value = "portal"),
    @Property(key = "service", value = "composer"), @Property(key = "type", value = "content") })
@ManagedDescription("WCM Composer service")
@RESTEndpoint(path = "wcmcomposerservice")
public class WCMComposerImpl implements WCMComposer, Startable {

    final static public String EXO_RESTORELOCATION = "exo:restoreLocation";

  /** The repository service. */
  private RepositoryService repositoryService;

  /** The link manager service. */
  private LinkManager linkManager;

  private PublicationService  publicationService;

  private TaxonomyService  taxonomyService;

  private TemplateService templateService;

  private WCMService wcmService;

  private MultiLanguageService multiLanguageService;

  private ACLSessionProviderService aclSessionProviderService;

  private TrashService trashService;

  /** The log. */
  private static final Log LOG = ExoLogger.getLogger(WCMComposerImpl.class.getName());

  /** The template filter query */
  private String templatesFilter;

  /** OrderBy properties accessed on Front side */
  private List<String> usedOrderBy;
  /** Languages properties accessed on Front side */
  private List<String> usedLanguages;
  /** PrimaryTypes properties accessed on Front side */
  private List<String> usedPrimaryTypes;
  /** shared group membership */
  private String sharedGroup;

  /**
   * Instantiates a new WCM composer impl.
   *
   * @throws Exception the exception
   */
  public WCMComposerImpl(InitParams params) throws Exception {
    if (params!=null) {
      ValueParam sharedGroupParam = params.getValueParam("sharedGroup");
      if (sharedGroupParam != null) {
        this.sharedGroup = sharedGroupParam.getValue();
      }
    }

    repositoryService = WCMCoreUtils.getService(RepositoryService.class);
    linkManager = WCMCoreUtils.getService(LinkManager.class);
    publicationService = WCMCoreUtils.getService(PublicationService.class);
    templateService = WCMCoreUtils.getService(TemplateService.class);
    wcmService = WCMCoreUtils.getService(WCMService.class);
    multiLanguageService = WCMCoreUtils.getService(MultiLanguageService.class);
    aclSessionProviderService = WCMCoreUtils.getService(ACLSessionProviderService.class);

    usedLanguages = new ArrayList<String>();
    usedLanguages.add(null);
    usedOrderBy = new ArrayList<String>();
    usedOrderBy.add(null);
    usedPrimaryTypes = new ArrayList<String>();
    usedPrimaryTypes.add(null);

  }

  /*
   * (non-Javadoc)
   * @see
   * org.exoplatform.services.wcm.publication.WCMComposer#getContent(java.lang
   * .String, java.lang.String, java.lang.String, java.util.HashMap)
   */
  public Node getContent(String workspace,
                         String nodeIdentifier,
                         HashMap<String, String> filters,
                         SessionProvider sessionProvider) throws Exception {
    String mode = filters.get(FILTER_MODE);
    String version = filters.get(FILTER_VERSION);
    String visibility = filters.get(FILTER_VISIBILITY);
    String remoteUser = getRemoteUser();
    String repository = null;

    try {
      repository = repositoryService.getCurrentRepository().getConfiguration().getName();
    } catch (Exception e) {
      if (LOG.isWarnEnabled()) {
        LOG.warn(e.getMessage());
      }
    }

    if (workspace==null) {
      if (nodeIdentifier.lastIndexOf("/") == 0) nodeIdentifier = nodeIdentifier.substring(1);
      String[] params = nodeIdentifier.split("/");
      workspace = params[1];
      try {
        nodeIdentifier = nodeIdentifier.substring(repository.length()+workspace.length()+1);
      } catch (Exception e) {
        if (LOG.isWarnEnabled()) {
          LOG.warn(e.getMessage());
        }
      }
    }

    Node node = null;
    try {
    if (WCMComposer.VISIBILITY_PUBLIC.equals(visibility) && MODE_LIVE.equals(mode)) {
        sessionProvider = remoteUser == null?
                          aclSessionProviderService.getAnonymSessionProvider() :
                          aclSessionProviderService.getACLSessionProvider(getAnyUserACL());
      }
      node = wcmService.getReferencedContent(sessionProvider, workspace, nodeIdentifier);
    } catch (RepositoryException e) {
      node = getNodeByCategory(nodeIdentifier);
    }
    if (version == null || !BASE_VERSION.equals(version)) {
      node = getViewableContent(node, filters);
    }

    return node;
  }

  public List<Node> getContents(String workspace,
                                String path,
                                HashMap<String, String> filters,
                                SessionProvider sessionProvider) throws Exception {
    String mode = filters.get(FILTER_MODE);
    String version = filters.get(FILTER_VERSION);
    String orderBy = filters.get(FILTER_ORDER_BY);
    String orderType = filters.get(FILTER_ORDER_TYPE);
    String visibility = filters.get(FILTER_VISIBILITY); 
    String remoteUser = null;
    if (WCMComposer.VISIBILITY_PUBLIC.equals(visibility)) {
      remoteUser = "##PUBLIC##VISIBILITY";
    } else {
      remoteUser = getRemoteUser();
    }

    if (MODE_EDIT.equals(mode) && "publication:liveDate".equals(orderBy)) {
      orderBy = "exo:dateModified";
      filters.put(FILTER_ORDER_BY, orderBy);
    }
    if ("exo:title".equals(orderBy)) {
      if(MODE_LIVE.equals(mode)) {
        orderBy = "exo:titlePublished "+orderType+", exo:title";
      }
      if ("exo:taxonomy".equals(this.getTypeFromPath(workspace, path, sessionProvider))) {
        orderBy = "exo:title "+orderType+", exo:titlePublished";
      }
      filters.put(FILTER_ORDER_BY, orderBy);
    }

    List<Node> nodes = new ArrayList<Node>();
    try {
      if (WCMComposer.VISIBILITY_PUBLIC.equals(visibility) && MODE_LIVE.equals(mode) && remoteUser != null) {
        sessionProvider = aclSessionProviderService.getACLSessionProvider(getAnyUserACL());
      }
      if (LOG.isDebugEnabled()) LOG.debug("##### "+path+":"+version+":"+remoteUser+":"+orderBy+":"+orderType);
      NodeIterator nodeIterator = getViewableContents(workspace, path, filters, sessionProvider, false);

      Node node = null, viewNode = null;
      while (nodeIterator != null && nodeIterator.hasNext()) {
        node = nodeIterator.nextNode();
        viewNode = getViewableContent(node, filters);
        if (viewNode != null) {
          nodes.add(viewNode);
        }
      }
    } catch (Exception e) {
      if (LOG.isWarnEnabled()) {
        LOG.warn(e.getMessage());
      }
    }

    return nodes;
  }

  public Result getPaginatedContents(NodeLocation nodeLocation,
                                     HashMap<String, String> filters,
                                     SessionProvider sessionProvider) throws Exception {
    String path = nodeLocation.getPath();
    String workspace = nodeLocation.getWorkspace();

    String mode = filters.get(FILTER_MODE);
    String version = filters.get(FILTER_VERSION);
    String orderBy = filters.get(FILTER_ORDER_BY);
    String orderType = filters.get(FILTER_ORDER_TYPE);
    String visibility = filters.get(FILTER_VISIBILITY);


    String remoteUser = getRemoteUser();

    if (MODE_EDIT.equals(mode) && "publication:liveDate".equals(orderBy)) {
      orderBy = "exo:dateModified";
      filters.put(FILTER_ORDER_BY, orderBy);
    }
    if (MODE_LIVE.equals(mode) && "exo:title".equals(orderBy)) {
      orderBy = "exo:titlePublished "+orderType+", exo:title";
      filters.put(FILTER_ORDER_BY, orderBy);
    }

    if (LOG.isDebugEnabled()) LOG.debug("##### "+path+":"+version+":"+remoteUser+":"+orderBy+":"+orderType);



    if (WCMComposer.VISIBILITY_PUBLIC.equals(visibility) && MODE_LIVE.equals(mode)) {
      sessionProvider = remoteUser == null?
                        aclSessionProviderService.getAnonymSessionProvider() :
                        aclSessionProviderService.getACLSessionProvider(getAnyUserACL());
    }
    ManageableRepository manageableRepository = repositoryService.getCurrentRepository();
    Session session = sessionProvider.getSession(workspace, manageableRepository);
    Node currentFolder = null;

    Item item = session.getItem(path);
    if (item != null) {
      currentFolder = (Node) item;
    }

    Result result;
    //Distinguish whether the targeted nodes are symlinks or not
    if (currentFolder != null && currentFolder.isNodeType("exo:taxonomy")) {
      result = getPaginatedTaxonomiesContent(nodeLocation, workspace, filters, sessionProvider);
    } else {
      result = getPaginatedNodesContent(nodeLocation, workspace, filters, sessionProvider);
    }

    return result;
  }

  /**
   * return paginated result in case of taxonomies nodes. This nodes are loaded in memory from jcr then
   * filtered against publication because the information about publication is not in the symlink but in its target
   * node
   * @param nodeLocation
   * @param workspace
   * @param filters
   * @param sessionProvider
   * @return current page result with populating taxonomies
   * @throws Exception
   */
  private Result getPaginatedTaxonomiesContent(NodeLocation nodeLocation, String workspace,
                                               HashMap<String, String> filters,
                                               SessionProvider sessionProvider) throws Exception{
    List<Node> nodes = new ArrayList<Node>();
    long totalSize;
    long offset = (filters.get(FILTER_OFFSET)!=null)?new Long(filters.get(FILTER_OFFSET)):0;
    String path = nodeLocation.getPath();
    NodeIterator taxonomyNodeIterator = getViewableContents(workspace, path, filters, sessionProvider, false);
    List<Node> taxonomyNodes = new ArrayList<Node>();
    Node taxonomyNode = null, taxonomyViewNode = null;
    if (taxonomyNodeIterator != null) {
      while (taxonomyNodeIterator.hasNext()) {
        taxonomyNode = taxonomyNodeIterator.nextNode();
        taxonomyViewNode = getViewableContent(taxonomyNode, filters);
        if (taxonomyViewNode != null) {
          taxonomyNodes.add(taxonomyViewNode);
        }
      }
    }
    long limit = (filters.get(FILTER_LIMIT)!=null)?new Integer(filters.get(FILTER_LIMIT)):0;
    long max = offset + limit;
    totalSize = taxonomyNodes.size();
    if (max > totalSize){
      max = totalSize;
    }
    for (long i = offset ; i < max ; i++ ){
      nodes.add(taxonomyNodes.get((int)i));
    }

    Result result = new Result(nodes, offset, totalSize, nodeLocation, filters);
    return result;

  }

  /**
   * return paginated result in case of document nodes. The nodes are filtered in jcr side. The publication statut is
   * part of node's properties
   * @param nodeLocation
   * @param workspace
   * @param filters
   * @param sessionProvider
   * @return current page result with populating nodes
   * @throws Exception
   */
  private Result getPaginatedNodesContent(NodeLocation nodeLocation, String workspace,
                                          HashMap<String, String> filters,
                                          SessionProvider sessionProvider) throws Exception{
    List<Node> nodes = new ArrayList<Node>();
    long totalSize;
    long offset = (filters.get(FILTER_OFFSET)!=null)?new Long(filters.get(FILTER_OFFSET)):0;
    String path = nodeLocation.getPath();
    totalSize = getViewabaleContentsSize(path, workspace, filters, sessionProvider);
    NodeIterator nodeIterator = getViewableContents(workspace, path, filters, sessionProvider, true);
    Node node = null, viewNode = null;
    if (nodeIterator != null) {
      while (nodeIterator.hasNext()) {
        node = nodeIterator.nextNode();
        viewNode = getViewableContent(node, filters);
        if (viewNode != null) {
          nodes.add(viewNode);
        }
      }
    }

    Result result = new Result(nodes, offset, totalSize, nodeLocation, filters);
    return result;

  }

  /**
   * get total contents' size
   * @param path
   * @param workspace
   * @param filters
   * @param sessionProvider
   * @return
   * @throws Exception
   */
  private long getViewabaleContentsSize(String path, String workspace,HashMap<String, String> filters,
                                      SessionProvider sessionProvider) throws Exception {

    long totalSize = (filters.get(FILTER_TOTAL)!=null)?new Long(filters.get(FILTER_TOTAL)):0;
    if (totalSize == 0) {
      NodeIterator nodeIterator = getViewableContents(workspace, path, filters, sessionProvider, false);
      if (nodeIterator != null) {
        totalSize = nodeIterator.getSize();
      }
    }
    return totalSize;
  }

  /*
   * (non-Javadoc)
   * @see
   * org.exoplatform.services.wcm.publication.WCMComposer#getContents(java.lang
   * .String, java.lang.String, java.lang.String, java.util.HashMap)
   */
  private NodeIterator getViewableContents(String workspace,
                                           String path,
                                           HashMap<String, String> filters,
                                           SessionProvider sessionProvider, boolean paginated) throws Exception {
    ManageableRepository manageableRepository = repositoryService.getCurrentRepository();
    Session session = sessionProvider.getSession(workspace, manageableRepository);
    QueryManager manager = session.getWorkspace().getQueryManager();
    String defaultLanguage = filters.get(FILTER_LANGUAGE);
    String mode = filters.get(FILTER_MODE);
    String orderBy = filters.get(FILTER_ORDER_BY);
    Boolean translation = Boolean.parseBoolean(filters.get(FILTER_TRANSLATION));
    String orderFilter = getOrderSQLFilter(filters);
    String recursive = filters.get(FILTER_RECURSIVE);
    String primaryType = filters.get(FILTER_PRIMARY_TYPE);
    String queryFilter = filters.get(FILTER_QUERY);
    String queryFilterFull = filters.get(FILTER_QUERY_FULL);
    StringBuffer statement = new StringBuffer();
    boolean filterTemplates = true;
    if (queryFilterFull!=null) {
      statement.append(queryFilterFull);
      updateSymlinkByQuery(workspace, queryFilterFull, sessionProvider);
    } else {
      addUsedPrimaryTypes(primaryType);
      if (primaryType == null) {
        primaryType = "nt:base";
        Node currentFolder = null;
        if ("/".equals(path)) {
          currentFolder = session.getRootNode();
        } else {
          Item item = session.getItem(path);
          if (item != null) {
            currentFolder = (Node) item;
          }
        }

        if (currentFolder != null && currentFolder.isNodeType("exo:taxonomy")) {
          primaryType = "exo:taxonomyLink";
        }
      } else {
        filterTemplates = false;
      }
      addUsedOrderBy(orderBy);

      statement.append("SELECT * FROM " + primaryType + " WHERE (jcr:path LIKE '" + path + "/%'");
      if (recursive==null || "false".equals(recursive)) {
        statement.append(" AND NOT jcr:path LIKE '" + path + "/%/%')");
      } else {
        statement.append(")");
      }
      if (translation) {
        statement.append(" AND ( exo:language = '" + defaultLanguage + "')");
      }
      // If clv view mode is live, only get nodes which has published version
      if (MODE_LIVE.equals(mode) && !"exo:taxonomyLink".equals(primaryType))
        statement.append(" AND NOT publication:currentState = 'unpublished' AND (publication:currentState IS NULL OR publication:currentState = 'published' " +
        		"OR exo:titlePublished IS NOT NULL)");
      if (filterTemplates) statement.append(" AND " + getTemplatesSQLFilter());
      if (queryFilter!=null) {
        statement.append(queryFilter);
      }
      statement.append(orderFilter);
      updateSymlink(workspace, path, sessionProvider);
    }
    Query query = manager.createQuery(statement.toString(), Query.SQL);

    if (paginated) {
      long offset = (filters.get(FILTER_OFFSET)!=null)?new Long(filters.get(FILTER_OFFSET)):0;
      long limit = (filters.get(FILTER_LIMIT)!=null)?new Long(filters.get(FILTER_LIMIT)):0;
      if (limit>0) {
        ((QueryImpl)query).setOffset(offset);
        ((QueryImpl)query).setLimit(limit);
      }
    }

    // order by title with in-sensitive case.
    ((QueryImpl)query).setCaseInsensitiveOrder(true);

    return query.execute().getNodes();
  }

  /**
   * Gets the node view.
   *
   * @param node the node
   *
   * @return the node view
   *
   * @throws Exception the exception
   */
  private Node getViewableContent(Node node, HashMap<String, String> filters) throws Exception {
    Node viewNode = null;
    if (trashService == null) {
      trashService = WCMCoreUtils.getService(TrashService.class);
    }
    try {
      node = getTargetNode(node);
    } catch (AccessDeniedException ade) {
      return null;
    }

    if (node != null && trashService.isInTrash(node)) {
      return null;
    }

    String languageFilter = filters.get(FILTER_LANGUAGE);
    Boolean translation = Boolean.parseBoolean(filters.get(FILTER_TRANSLATION));
    if (languageFilter!=null && translation) {
      addUsedLanguage(languageFilter);
      Node lnode = null;
      try {
        lnode = multiLanguageService.getLanguage(node, languageFilter);
      } catch (AccessDeniedException e) {
        if (LOG.isTraceEnabled()) LOG.trace("AccessDenied on "+languageFilter+" translation for "+node.getPath());
      }
      if (lnode!=null) {

        viewNode = getPublishedContent(lnode, filters);
        if (viewNode!=null) {
          return viewNode;
        }
        return null;
      }
    }

    if (node != null) {
      viewNode = getPublishedContent(node, filters);
    }

    return viewNode;
  }


  private Node getPublishedContent(Node node, HashMap<String, String> filters) throws Exception {
    HashMap<String, Object> context = new HashMap<String, Object>();
    String mode = filters.get(FILTER_MODE);
    context.put(WCMComposer.FILTER_MODE, mode);
    context.put(WCMComposer.PORTLET_MODE, filters.get(PORTLET_MODE));
    String lifecyleName = null;
    try {
      lifecyleName = publicationService.getNodeLifecycleName(node);
    } catch (NotInPublicationLifecycleException e) {
      // Don't log here, this is normal
    }
    if (lifecyleName == null) return node;
    PublicationPlugin publicationPlugin = publicationService.getPublicationPlugins().get(lifecyleName);
    Node viewNode = publicationPlugin.getNodeView(node, context);
    return viewNode;

  }

  private Node getTargetNode(Node showingNode) throws Exception {
    Node targetNode = null;
    if (linkManager.isLink(showingNode)) {
      try {
        targetNode = linkManager.getTarget(showingNode);
      } catch (ItemNotFoundException e) {
        targetNode = showingNode;
      }
    } else {
      targetNode = showingNode;
    }
    return targetNode;
  }

  public List<Node> getCategories(Node node) throws Exception {
    if (taxonomyService==null) taxonomyService = WCMCoreUtils.getService(TaxonomyService.class);
    List<Node> listCategories = new ArrayList<Node>();
    List<Node> listNode = getAllTaxonomyTrees();
    for(Node itemNode : listNode) {
      listCategories.addAll(taxonomyService.getCategories(node, itemNode.getName()));
    }
    return listCategories;
  }

  List<Node> getAllTaxonomyTrees() throws RepositoryException {
    if (taxonomyService==null) taxonomyService = WCMCoreUtils.getService(TaxonomyService.class);
    return taxonomyService.getAllTaxonomyTrees();
  }

  String displayCategory(Node node, List<Node> taxonomyTrees) {
    try {
      for (Node taxonomyTree : taxonomyTrees) {
        if (node.getPath().contains(taxonomyTree.getPath())) {
          return node.getPath().replace(taxonomyTree.getPath(), taxonomyTree.getName());
        }
      }
    } catch (RepositoryException e) {
      if (LOG.isErrorEnabled())LOG.error("Unexpected error when getting node taxonomies");
    }
    return "";
  }

  /**
   * We currently support 2 modes :
   * MODE_LIVE : PUBLISHED state only
   * MODE_EDIT : PUBLISHED, DRAFT, PENDING, STAGED, APPROVED allowed.
   *
   * @param mode the current mode (MODE_LIVE or MODE_EDIT)
   *
   * @return the allowed states
   */
  public List<String> getAllowedStates(String mode) {
    List<String> states = new ArrayList<String>();
    if (MODE_LIVE.equals(mode)) {
      states.add(PublicationDefaultStates.PUBLISHED);
    } else if (MODE_EDIT.equals(mode)) {
      states.add(PublicationDefaultStates.PUBLISHED);
      states.add(PublicationDefaultStates.DRAFT);
      states.add(PublicationDefaultStates.PENDING);
      states.add(PublicationDefaultStates.STAGED);
      states.add(PublicationDefaultStates.APPROVED);
    }
    return states;
  }


  @Managed
  @ManagedDescription("Clean all templates in Composer")
    public void cleanTemplates() throws Exception {
      this.templatesFilter = null;
      getTemplatesSQLFilter();
      if (LOG.isDebugEnabled()) LOG.debug("WCMComposer templates have been cleaned !");
    }

  @Managed
  @ManagedDescription("Used Languages")
  public List<String> getUsedLanguages() {
    return usedLanguages;
  }

  @Managed
  @ManagedDescription("Used Primary Types")
  public List<String> getUsedPrimaryTypes() {
    return usedPrimaryTypes;
  }

  @Managed
  @ManagedDescription("Used Order By")
  public List<String> getUsedOrderBy() {
    return usedOrderBy;
  }


  /* (non-Javadoc)
   * @see org.picocontainer.Startable#start()
   */
  public void start() {}

  /* (non-Javadoc)
   * @see org.picocontainer.Startable#stop()
   */
  public void stop() {}

  /**
   * Gets the order sql filter.
   *
   * @param filters the filters
   *
   * @return the order sql filter
   */
  private String getOrderSQLFilter(HashMap<String, String> filters) {
    StringBuffer orderQuery = new StringBuffer(" ORDER BY ");
    String orderBy = filters.get(FILTER_ORDER_BY);
    String orderType = filters.get(FILTER_ORDER_TYPE);
    if (orderType == null)
      orderType = "DESC";
    if (orderBy == null)
      orderBy = "exo:title";
    orderQuery.append(orderBy).append(" ").append(orderType);
    return orderQuery.toString();
  }

  /**
   * Gets all document nodetypes and write a query statement
   * @return a part of the query allow search all document node and taxonomy link also. Return null if there is any exception.
   */
  private String getTemplatesSQLFilter() {
    if (templatesFilter != null) return templatesFilter;
    return updateTemplatesSQLFilter();
  }
  /**
   * Update all document nodetypes and write a query statement
   * @return a part of the query allow search all document node and taxonomy link also. Return null if there is any exception.
   */
  public String updateTemplatesSQLFilter() {
    try {
      List<String> documentTypes = templateService.getDocumentTemplates();
      StringBuffer documentTypeClause = new StringBuffer("(");
      for (int i = 0; i < documentTypes.size(); i++) {
        String documentType = documentTypes.get(i);
        documentTypeClause.append("jcr:primaryType = '" + documentType + "'");
        if (i != (documentTypes.size() - 1)) documentTypeClause.append(" OR ");
      }
      templatesFilter = documentTypeClause.toString();
      templatesFilter += " OR jcr:primaryType = 'exo:taxonomyLink' OR jcr:primaryType = 'exo:symlink')";
      return templatesFilter;
    } catch (Exception e) {
      if (LOG.isErrorEnabled()) {
        LOG.error("Error when perform getTemlatesSQLFilter: ", e);
      }
      return null;
    }
  }

  /**
   * Gets the node by category.
   *
   * @param parameters the parameters
   *
   * @return the node by category
   *
   * @throws Exception the exception
   */
  private Node getNodeByCategory(String parameters) throws Exception {
    try {
      if (taxonomyService==null) taxonomyService = WCMCoreUtils.getService(TaxonomyService.class);
      Node taxonomyTree = taxonomyService.getTaxonomyTree(parameters.split("/")[0]);
      Node symlink = taxonomyTree.getNode(parameters.substring(parameters.indexOf("/") + 1));
      return linkManager.getTarget(symlink);
    } catch (Exception e) {
      return null;
    }
  }

  private void addUsedLanguage(String lang) {
    if (!usedLanguages.contains(lang)) usedLanguages.add(lang);
  }

  private void addUsedOrderBy(String orderBy) {
    if (!usedOrderBy.contains(orderBy)) usedOrderBy.add(orderBy);
  }
  private void addUsedPrimaryTypes(String primaryType) {
    if (!usedPrimaryTypes.contains(primaryType)) usedPrimaryTypes.add(primaryType);
  }

  private List<AccessControlEntry> getAnyUserACL() {
    List<AccessControlEntry> ret = new ArrayList<AccessControlEntry>();
    ret.add(new AccessControlEntry(sharedGroup, PermissionType.READ));
    return ret;
  }

  /**
   * Get login User
   * @return user name
   */
  private String getRemoteUser() {
    ConversationState conversationState = ConversationState.getCurrent();
    Identity identity = conversationState.getIdentity();
    if (identity != null && !IdentityConstants.ANONIM.equals(identity.getUserId())) {
      return identity.getUserId();
    }
    return null;
  }

  private void updateSymlink(String workspace, String path, SessionProvider sessionProvider) {
    if ("/".equals(path)) {
      path = "";
    }
    StringBuilder statement = new StringBuilder();
    statement.append("SELECT * FROM " + NodetypeConstant.EXO_SYMLINK + " WHERE (jcr:path LIKE '" + path + "/%'")
             .append(" AND NOT jcr:path LIKE '" + path + "/%/%')");
    updateSymlinkByQuery(workspace, statement.toString(), sessionProvider);
  }

  private void updateSymlinkByQuery(String workspace, String statement, SessionProvider sessionProvider) {
    try {
      ManageableRepository manageableRepository = repositoryService.getCurrentRepository();
      Session session = sessionProvider.getSession(workspace, manageableRepository);
      QueryManager manager = session.getWorkspace().getQueryManager();
      NodeIterator iter = manager.createQuery(statement, Query.SQL).execute().getNodes();
      while (iter.hasNext()) {
        try {
          Node currentNode = iter.nextNode();
          linkManager.updateSymlink(currentNode);
        } catch (Exception ex) {
          if (LOG.isErrorEnabled()) {
            LOG.error("Can not update symlink data", ex);
          }
        }
      }
    } catch (RepositoryException e) {
      if (LOG.isErrorEnabled()) {
        LOG.error("Can not update symlinks data", e);
      }
    }
  }
  
  private String getTypeFromPath (String workspace, String path, SessionProvider sessionProvider) throws Exception {
    ManageableRepository manageableRepository = repositoryService.getCurrentRepository();
    Session session = sessionProvider.getSession(workspace, manageableRepository);
    Node currentFolder = null;
    try {
       Node node = (Node)session.getItem(path);
       return node.getPrimaryNodeType().getName();
    } catch(PathNotFoundException pne) {
      return null;
    }
  }
}
