/* * Copyright (C) 2003-2007 eXo Platform SAS. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Affero General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see<http://www.gnu.org/licenses/>. */ package org.exoplatform.ecm.webui.component.explorer.search; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Comparator; import java.util.Date; import java.util.GregorianCalendar; import java.util.List; import java.util.Locale; import javax.jcr.AccessDeniedException; import javax.jcr.Item; import javax.jcr.ItemNotFoundException; import javax.jcr.Node; import javax.jcr.PathNotFoundException; import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.query.Query; import javax.jcr.query.QueryManager; import javax.jcr.query.QueryResult; import javax.jcr.query.Row; import org.exoplatform.ecm.jcr.model.Preference; import org.apache.commons.lang.StringUtils; import org.exoplatform.ecm.webui.component.explorer.UIDocumentContainer; import org.exoplatform.ecm.webui.component.explorer.UIDocumentWorkspace; import org.exoplatform.ecm.webui.component.explorer.UIDrivesArea; import org.exoplatform.ecm.webui.component.explorer.UIJCRExplorer; import org.exoplatform.ecm.webui.component.explorer.UIWorkingArea; import org.exoplatform.ecm.webui.utils.JCRExceptionManager; import org.exoplatform.ecm.webui.utils.Utils; import org.exoplatform.portal.webui.util.Util; import org.exoplatform.portal.webui.workspace.UIPortalApplication; import org.exoplatform.services.cms.BasePath; import org.exoplatform.services.cms.link.LinkManager; import org.exoplatform.services.cms.link.LinkUtils; import org.exoplatform.services.cms.link.NodeFinder; import org.exoplatform.services.cms.taxonomy.TaxonomyService; import org.exoplatform.services.cms.templates.TemplateService; import org.exoplatform.services.jcr.ext.hierarchy.NodeHierarchyCreator; import org.exoplatform.services.jcr.impl.core.JCRPath; import org.exoplatform.services.jcr.impl.core.SessionImpl; import org.exoplatform.services.log.ExoLogger; import org.exoplatform.services.log.Log; import org.exoplatform.services.wcm.core.NodetypeConstant; import org.exoplatform.services.wcm.search.base.AbstractPageList; import org.exoplatform.services.wcm.search.base.NodeSearchFilter; import org.exoplatform.services.wcm.search.base.PageListFactory; import org.exoplatform.services.wcm.search.base.QueryData; import org.exoplatform.services.wcm.search.base.SearchDataCreator; import org.exoplatform.services.wcm.utils.WCMCoreUtils; import org.exoplatform.wcm.webui.paginator.UILazyPageIterator; import org.exoplatform.web.application.ApplicationMessage; import org.exoplatform.webui.config.annotation.ComponentConfig; import org.exoplatform.webui.config.annotation.EventConfig; import org.exoplatform.webui.core.UIApplication; import org.exoplatform.webui.core.UIContainer; import org.exoplatform.webui.core.UIPageIterator; import org.exoplatform.webui.event.Event; import org.exoplatform.webui.event.EventListener; /** * Created by The eXo Platform SARL * Author : Tran The Trong * trongtt@gmail.com * Oct 2, 2006 * 16:37:15 * * Edited by : Dang Van Minh * minh.dang@exoplatform.com * Jan 5, 2007 */ @ComponentConfig( template = "app:/groovy/webui/component/explorer/search/UISearchResult.gtmpl", events = { @EventConfig(listeners = UISearchResult.ViewActionListener.class), @EventConfig(listeners = UISearchResult.OpenFolderActionListener.class), @EventConfig(listeners = UISearchResult.SortASCActionListener.class), @EventConfig(listeners = UISearchResult.SortDESCActionListener.class) } ) public class UISearchResult extends UIContainer { /** * Logger. */ private static final Log LOG = ExoLogger.getLogger(UISearchResult.class.getName()); private QueryData queryData_; private long searchTime_ = 0; private UIPageIterator uiPageIterator_; private static String iconType = ""; private static String iconScore = ""; static private int PAGE_SIZE = 10; private List<String> categoryPathList = new ArrayList<String>(); private String constraintsCondition; private String workspaceName = null; private String currentPath = null; private String keyword =""; private AbstractPageList<RowData> pageList; public List<String> getCategoryPathList() { return categoryPathList; } public void setCategoryPathList(List<String> categoryPathListItem) { categoryPathList = categoryPathListItem; } public String getConstraintsCondition() { return constraintsCondition; } public void setConstraintsCondition(String constraintsConditionItem) { constraintsCondition = constraintsConditionItem; } public UISearchResult() throws Exception { uiPageIterator_ = addChild(UILazyPageIterator.class, null, "UISearchResultPageIterator"); } public void setQuery(String queryStatement, String workspaceName, String language, boolean isSystemSession, String keyword) { queryData_ = new QueryData(queryStatement, workspaceName, language, isSystemSession); this.keyword = keyword; } public long getSearchTime() { return searchTime_; } public void setSearchTime(long time) { this.searchTime_ = time; } public List getCurrentList() throws Exception { return uiPageIterator_.getCurrentPageData(); } public DateFormat getSimpleDateFormat() { Locale locale = Util.getUIPortal().getAncestorOfType(UIPortalApplication.class).getLocale(); return SimpleDateFormat.getDateTimeInstance(SimpleDateFormat.SHORT, SimpleDateFormat.SHORT, locale); } public Session getSession() throws Exception { return getAncestorOfType(UIJCRExplorer.class).getTargetSession(); } public Date getDateCreated(Node node) throws Exception{ if (node.hasProperty("exo:dateCreated")) { return node.getProperty("exo:dateCreated").getDate().getTime(); } return new GregorianCalendar().getTime(); } public Node getNodeByPath(String path) throws Exception { try { JCRPath nodePath = ((SessionImpl)getSession()).getLocationFactory().parseJCRPath(path); return (Node)getSession().getItem(nodePath.getAsString(false)); } catch (Exception e) { return null; } } public UIPageIterator getUIPageIterator() { return uiPageIterator_; } public void updateGrid() throws Exception { TemplateService templateService = WCMCoreUtils.getService(TemplateService.class); List<String> documentList = templateService.getDocumentTemplates(); pageList = PageListFactory.createPageList(queryData_.getQueryStatement(), queryData_.getWorkSpace(), queryData_.getLanguage_(), queryData_.isSystemSession(), new NodeFilter(categoryPathList, keyword, documentList), new RowDataCreator(), PAGE_SIZE, 0); uiPageIterator_.setPageList(pageList); } private static class SearchComparator implements Comparator<RowData> { public static final String SORT_TYPE = "NODE_TYPE"; public static final String SORT_SCORE = "JCR_SCORE"; public static final String ASC = "ASC"; public static final String DESC = "DECS"; private String sortType; private String orderType; public void setSortType(String value) { sortType = value; } public void setOrderType(String value) { orderType = value; } public int compare(RowData row1, RowData row2) { try { if (SORT_TYPE.equals(sortType.trim())) { String s1 = row1.getJcrPrimaryType(); String s2 = row2.getJcrPrimaryType(); if (DESC.equals(orderType.trim())) { return s2.compareTo(s1); } return s1.compareTo(s2); } else if (SORT_SCORE.equals(sortType.trim())) { Long l1 = row1.getJcrScore(); Long l2 = row2.getJcrScore(); if (DESC.equals(orderType.trim())) { return l2.compareTo(l1); } return l1.compareTo(l2); } } catch (Exception e) { if (LOG.isErrorEnabled()) { LOG.error("Cannot compare rows", e); } } return 0; } } public String StriptHTML(String s) { String[] targets = {"<div>", "</div>", "<span>", "</span>"}; for (String target : targets) { s = s.replace(target, ""); } return s; } static public class ViewActionListener extends EventListener<UISearchResult> { public void execute(Event<UISearchResult> event) throws Exception { UISearchResult uiSearchResult = event.getSource(); UIJCRExplorer uiExplorer = uiSearchResult.getAncestorOfType(UIJCRExplorer.class); String path = event.getRequestContext().getRequestParameter(OBJECTID); UIApplication uiApp = uiSearchResult.getAncestorOfType(UIApplication.class); String workspaceName = event.getRequestContext().getRequestParameter("workspaceName"); Item item = null; try { Session session = uiExplorer.getSessionByWorkspace(workspaceName); // Check if the path exists NodeFinder nodeFinder = uiSearchResult.getApplicationComponent(NodeFinder.class); item = nodeFinder.getItem(session, path); } catch(PathNotFoundException pa) { uiApp.addMessage(new ApplicationMessage("UITreeExplorer.msg.path-not-found", null, ApplicationMessage.WARNING)) ; return ; } catch(ItemNotFoundException inf) { uiApp.addMessage(new ApplicationMessage("UITreeExplorer.msg.path-not-found", null, ApplicationMessage.WARNING)) ; return ; } catch(AccessDeniedException ace) { uiApp.addMessage(new ApplicationMessage("UIDocumentInfo.msg.access-denied", null, ApplicationMessage.WARNING)) ; return ; } catch(RepositoryException e) { if (LOG.isErrorEnabled()) { LOG.error("Repository cannot be found"); } uiApp.addMessage(new ApplicationMessage("UITreeExplorer.msg.repository-error", null, ApplicationMessage.WARNING)) ; return ; } catch (Exception e) { JCRExceptionManager.process(uiApp, e); return; } if (isInTrash(item)) return; UIWorkingArea uiWorkingArea = uiExplorer.getChild(UIWorkingArea.class); UIDocumentWorkspace uiDocumentWorkspace = uiWorkingArea.getChild(UIDocumentWorkspace.class); if(!uiDocumentWorkspace.isRendered()) { uiWorkingArea.getChild(UIDrivesArea.class).setRendered(false); uiWorkingArea.getChild(UIDocumentWorkspace.class).setRendered(true); } uiExplorer.setSelectNode(workspaceName, path) ; uiDocumentWorkspace.getChild(UIDocumentContainer.class).setRendered(true); uiSearchResult.setRendered(false); uiExplorer.refreshExplorer((Node)item, true); } private boolean isInTrash(Item item) throws RepositoryException { return (item instanceof Node) && Utils.isInTrash((Node) item); } } static public class OpenFolderActionListener extends EventListener<UISearchResult> { public void execute(Event<UISearchResult> event) throws Exception { UISearchResult uiSearchResult = event.getSource(); UIJCRExplorer uiExplorer = uiSearchResult.getAncestorOfType(UIJCRExplorer.class); String path = event.getRequestContext().getRequestParameter(OBJECTID); String folderPath = LinkUtils.getParentPath(path); Node node = null; try { node = uiExplorer.getNodeByPath(folderPath, uiExplorer.getTargetSession()); } catch(AccessDeniedException ace) { UIApplication uiApp = uiSearchResult.getAncestorOfType(UIApplication.class); uiApp.addMessage(new ApplicationMessage("UISearchResult.msg.access-denied", null, ApplicationMessage.WARNING)); return; } catch(PathNotFoundException ace) { UIApplication uiApp = uiSearchResult.getAncestorOfType(UIApplication.class); uiApp.addMessage(new ApplicationMessage("UISearchResult.msg.access-denied", null, ApplicationMessage.WARNING)); return; } catch(Exception e) { if (LOG.isErrorEnabled()) { LOG.error("Cannot access the node at " + folderPath, e); } } uiExplorer.setSelectNode(node.getSession().getWorkspace().getName(), folderPath); uiExplorer.refreshExplorer(node, true); } } static public class SortASCActionListener extends EventListener<UISearchResult> { public void execute(Event<UISearchResult> event) throws Exception { UISearchResult uiSearchResult = event.getSource(); String objectId = event.getRequestContext().getRequestParameter(OBJECTID); SearchComparator comparator = new SearchComparator(); if (objectId.equals("type")) { uiSearchResult.pageList.setSortByField(Utils.JCR_PRIMARYTYPE); comparator.setSortType(SearchComparator.SORT_TYPE); iconType = Preference.BLUE_DOWN_ARROW; iconScore = ""; } else if (objectId.equals("score")) { uiSearchResult.pageList.setSortByField(Utils.JCR_SCORE); comparator.setSortType(SearchComparator.SORT_SCORE); iconScore = Preference.BLUE_DOWN_ARROW; iconType = ""; } comparator.setOrderType(SearchComparator.ASC); uiSearchResult.pageList.setComparator(comparator); uiSearchResult.pageList.setOrder("ASC"); uiSearchResult.pageList.sortData(); event.getRequestContext().addUIComponentToUpdateByAjax(uiSearchResult.getParent()); } } static public class SortDESCActionListener extends EventListener<UISearchResult> { public void execute(Event<UISearchResult> event) throws Exception { UISearchResult uiSearchResult = event.getSource() ; String objectId = event.getRequestContext().getRequestParameter(OBJECTID); SearchComparator comparator = new SearchComparator(); if (objectId.equals("type")) { uiSearchResult.pageList.setSortByField(Utils.JCR_PRIMARYTYPE); comparator.setSortType(SearchComparator.SORT_TYPE); iconType = Preference.BLUE_UP_ARROW; iconScore = ""; } else if (objectId.equals("score")) { uiSearchResult.pageList.setSortByField(Utils.JCR_SCORE); comparator.setSortType(SearchComparator.SORT_SCORE); iconScore = Preference.BLUE_UP_ARROW; iconType = ""; } comparator.setOrderType(SearchComparator.DESC); uiSearchResult.pageList.setComparator(comparator); uiSearchResult.pageList.setOrder("DESC"); uiSearchResult.pageList.sortData(); // Collections.sort(uiSearchResult.currentListRows_, new SearchComparator()); // SearchResultPageList pageList = new SearchResultPageList(uiSearchResult.queryResult_, // uiSearchResult.currentListRows_, PAGE_SIZE, uiSearchResult.isEndOfIterator_); // uiSearchResult.currentAvailablePage_ = uiSearchResult.currentListNodes_.size()/PAGE_SIZE; // uiSearchResult.uiPageIterator_.setSearchResultPageList(pageList); // uiSearchResult.uiPageIterator_.setPageList(pageList); event.getRequestContext().addUIComponentToUpdateByAjax(uiSearchResult.getParent()); } } public static class NodeFilter implements NodeSearchFilter { private List<String> categoryPathList; private TaxonomyService taxonomyService; private NodeHierarchyCreator nodeHierarchyCreator; private String rootTreePath; private LinkManager linkManager = null; private String keyword =""; private List<String> documentTypes; final static private String CHECK_LINK_MATCH_QUERY1 = "select * from nt:base " + "where jcr:path = '$0' and ( contains(*, '$1') " + "or lower(exo:name) like '%$2%' " + "or lower(exo:title) like '%$2%')"; final static private String CHECK_LINK_MATCH_QUERY2 = "select * from nt:base where jcr:path like '$0/%' " + "and ( contains(*, '$1') or lower(exo:name) like '%$2%' " + "or lower(exo:title) like '%$2%')"; public NodeFilter(List<String> categories, String keyword, List<String> documentTypes) { taxonomyService = WCMCoreUtils.getService(TaxonomyService.class); nodeHierarchyCreator = WCMCoreUtils.getService(NodeHierarchyCreator.class); linkManager = WCMCoreUtils.getService(LinkManager.class); rootTreePath = nodeHierarchyCreator.getJcrPath(BasePath.TAXONOMIES_TREE_STORAGE_PATH); categoryPathList = categories; this.keyword = keyword; this.documentTypes = documentTypes; } public Node filterNodeToDisplay(Node node) { try { if (node == null || node.getPath().contains("/jcr:system/")) return null; if (node != null) { if ((categoryPathList != null) && (categoryPathList.size() > 0)){ for (String categoryPath : categoryPathList) { int index = categoryPath.indexOf("/"); String taxonomyName = categoryPath; String postFixTaxonomy = ""; if (index > 0) { taxonomyName = categoryPath.substring(0, index); postFixTaxonomy = categoryPath.substring(index + 1); } List<String> pathCategoriesList = new ArrayList<String>(); String searchCategory = taxonomyService.getTaxonomyTree(taxonomyName).getPath() + ("".equals(postFixTaxonomy) ? "" : "/" + postFixTaxonomy); Node targetNode = node.isNodeType(Utils.EXO_SYMLINK) ? linkManager.getTarget(node) : node; List<Node> listCategories = taxonomyService.getCategories(targetNode, taxonomyName); for (Node category : listCategories) { pathCategoriesList.add(category.getPath()); } if (pathCategoriesList.contains(searchCategory)) { if (node.isNodeType(Utils.EXO_SYMLINK)) { if (checkTargetMatch(node, keyword)) return node; }else { return node; } } } return null; } else { if (node.isNodeType(Utils.EXO_SYMLINK)) { if (checkTargetMatch(node, keyword)) return node; } else if (node.isNodeType(Utils.NT_RESOURCE)) { return node.getParent(); } else if (node.isNodeType(Utils.EXO_COMMENTS)) { return node.getParent().getParent(); } else { return node; } } } } catch (RepositoryException e) { if (LOG.isWarnEnabled()) { LOG.warn(e.getMessage()); } } return null; } /** * Check a symlink/taxonomylink if its target matches with keyword for searching ...link * @param symlinkNode * @param keyword * @return */ protected boolean checkTargetMatch(Node symlinkNode, String keyword) { String queryStatement = CHECK_LINK_MATCH_QUERY1; Session targetSession; Node target=null; if (keyword ==null || keyword.length()==0 ) return true; if (linkManager==null) { linkManager = WCMCoreUtils.getService(LinkManager.class); } try { if (!linkManager.isLink(symlinkNode)) return true; target = linkManager.getTarget(symlinkNode); if (target == null) return false; targetSession = target.getSession(); queryStatement = StringUtils.replace(queryStatement,"$0", target.getPath()); queryStatement = StringUtils.replace(queryStatement,"$1", keyword.replaceAll("'", "''")); queryStatement = StringUtils.replace(queryStatement,"$2", keyword.replaceAll("'", "''").toLowerCase()); QueryManager queryManager = targetSession.getWorkspace().getQueryManager(); Query query = queryManager.createQuery(queryStatement, Query.SQL); QueryResult queryResult = query.execute(); if ( queryResult.getNodes().getSize()>0 ) return true; if (isFodlderDocument(target)||target.hasNode("jcr:content") ) { queryStatement = CHECK_LINK_MATCH_QUERY2; queryStatement = StringUtils.replace(queryStatement,"$0", target.getPath()); queryStatement = StringUtils.replace(queryStatement,"$1", keyword.replaceAll("'", "''")); queryStatement = StringUtils.replace(queryStatement,"$2", keyword.replaceAll("'", "''").toLowerCase()); query = queryManager.createQuery(queryStatement, Query.SQL); queryResult = query.execute(); return queryResult.getNodes().getSize()>0; }else { return false; } } catch (RepositoryException e) { return false; } } private boolean isFodlderDocument(Node node) throws RepositoryException{ if (!node.isNodeType(NodetypeConstant.NT_UNSTRUCTURED)) return false; for (String documentType : documentTypes) { if (node.getPrimaryNodeType().isNodeType(documentType)) return true; } return false; } } public static class RowDataCreator implements SearchDataCreator<RowData> { public RowData createData(Node node, Row row) { return new RowData(row, node); } } public static class RowData { private String jcrPath = ""; private String repExcerpt = ""; private long jcrScore = 0; private String jcrPrimaryType = ""; public RowData(Row row) { this(row, null); } public RowData(Row row, Node node) { try { jcrPath = node != null ? node.getPath() : row.getValue("jcr:path").getString(); } catch (Exception e) { if (LOG.isWarnEnabled()) { LOG.warn(e.getMessage()); } } try { repExcerpt = row.getValue("rep:excerpt(.)").getString(); } catch (Exception e) { if (LOG.isWarnEnabled()) { LOG.warn(e.getMessage()); } } try { jcrScore = row.getValue("jcr:score").getLong(); } catch (Exception e) { if (LOG.isWarnEnabled()) { LOG.warn(e.getMessage()); } } try { jcrPrimaryType = row.getValue("jcr:primaryType").getString(); } catch (Exception e) { if (LOG.isWarnEnabled()) { LOG.warn(e.getMessage()); } } } public String getJcrPath() { return jcrPath; } public void setJcrPath(String jcrPath) { this.jcrPath = jcrPath; } public String getRepExcerpt() { return repExcerpt; } public void setRepExcerpt(String repExcerpt) { this.repExcerpt = repExcerpt; } public long getJcrScore() { return jcrScore; } public void setJcrScore(long jcrScore) { this.jcrScore = jcrScore; } public String getJcrPrimaryType() { return jcrPrimaryType; } public void setJcrPrimaryType(String value) { jcrPrimaryType = value; } public int hashCode() { return (jcrPath == null ? 0 : jcrPath.hashCode()); } public boolean equals(Object o) { if (o == null) return false; if (! (o instanceof RowData)) return false; RowData data = (RowData) o; return (jcrPath == null && data.jcrPath == null || jcrPath.equals(data.jcrPath)); } } }