/** * The contents of this file are subject to the license and copyright * detailed in the LICENSE and NOTICE files at the root of the source * tree and available online at * * http://www.dspace.org/license/ */ package org.dspace.app.xmlui.aspect.discovery; import org.dspace.app.xmlui.cocoon.AbstractDSpaceTransformer; import org.dspace.app.xmlui.utils.HandleUtil; import org.dspace.app.xmlui.utils.DSpaceValidity; import org.dspace.app.xmlui.utils.UIException; import org.dspace.app.xmlui.utils.RequestUtils; import org.dspace.app.xmlui.wing.element.*; import org.dspace.app.xmlui.wing.WingException; import org.dspace.app.xmlui.wing.Message; import org.dspace.content.DSpaceObject; import org.dspace.content.Community; import org.dspace.content.Collection; import org.dspace.authorize.AuthorizeException; import org.dspace.core.ConfigurationManager; import org.dspace.core.Constants; import org.apache.cocoon.caching.CacheableProcessingComponent; import org.apache.cocoon.util.HashUtil; import org.apache.cocoon.environment.Request; import org.apache.cocoon.environment.ObjectModelHelper; import org.apache.excalibur.source.SourceValidity; import org.apache.log4j.Logger; import org.dspace.core.Context; import org.dspace.discovery.*; import org.dspace.services.ConfigurationService; import org.dspace.utils.DSpace; import org.xml.sax.SAXException; import java.io.Serializable; import java.io.IOException; import java.sql.SQLException; import java.util.*; import java.net.URLEncoder; import java.util.List; /** * Dynamic browse by page (not used @ the moment) * * @author Kevin Van de Velde (kevin at atmire dot com) * @author Mark Diggory (markd at atmire dot com) * @author Ben Bosman (ben at atmire dot com) */ public class BrowseFacet extends AbstractDSpaceTransformer implements CacheableProcessingComponent { private static final Logger log = Logger.getLogger(BrowseFacet.class); private static final Message T_dspace_home = message("xmlui.general.dspace_home"); /** * The cache of recently submitted items */ protected DiscoverResult queryResults; /** * Cached validity object */ protected SourceValidity validity; /** * Cached query arguments */ protected DiscoverQuery queryArgs; private int DEFAULT_PAGE_SIZE = 10; public static final String OFFSET = "offset"; public static final String FACET_FIELD = "field"; private ConfigurationService config = null; private SearchService searchService = null; public BrowseFacet() { DSpace dspace = new DSpace(); config = dspace.getConfigurationService(); searchService = dspace.getServiceManager().getServiceByName(SearchService.class.getName(),SearchService.class); } /** * Generate the unique caching key. * This key must be unique inside the space of this component. */ public Serializable getKey() { try { DSpaceObject dso = HandleUtil.obtainHandle(objectModel); if (dso == null) { return "0"; } return HashUtil.hash(dso.getHandle()); } catch (SQLException sqle) { // Ignore all errors and just return that the component is not // cachable. return "0"; } } /** * Generate the cache validity object. * <p/> * The validity object will include the collection being viewed and * all recently submitted items. This does not include the community / collection * hierarch, when this changes they will not be reflected in the cache. */ public SourceValidity getValidity() { if (this.validity == null) { try { DSpaceValidity validity = new DSpaceValidity(); DSpaceObject dso = HandleUtil.obtainHandle(objectModel); if (dso != null) { // Add the actual collection; validity.add(dso); } // add reciently submitted items, serialize solr query contents. DiscoverResult response = getQueryResponse(dso); validity.add("numFound:" + response.getDspaceObjects().size()); for (DSpaceObject resultDso : response.getDspaceObjects()) { validity.add(resultDso); } for (String facetField : response.getFacetResults().keySet()) { validity.add(facetField); List<DiscoverResult.FacetResult> facetValues = response.getFacetResults().get(facetField); for (DiscoverResult.FacetResult facetValue : facetValues) { validity.add(facetValue.getAsFilterQuery() + facetValue.getCount()); } } this.validity = validity.complete(); } catch (Exception e) { // Just ignore all errors and return an invalid cache. } //TODO: dependent on tags as well :) } return this.validity; } /** * Get the recently submitted items for the given community or collection. * * @param scope The collection. */ protected DiscoverResult getQueryResponse(DSpaceObject scope) { Request request = ObjectModelHelper.getRequest(objectModel); if (queryResults != null) { return queryResults; } queryArgs = new DiscoverQuery(); //Make sure we add our default filters //queryArgs.addFilterQueries(SearchUtils.getDefaultFilters("browse")); queryArgs.setQuery("search.resourcetype: " + Constants.ITEM + ((request.getParameter("query") != null && !"".equals(request.getParameter("query"))) ? " AND (" + request.getParameter("query") + ")" : "")); // queryArgs.setQuery("search.resourcetype:" + Constants.ITEM); queryArgs.setMaxResults(0); // TODO: change this ! queryArgs.setSortField( ConfigurationManager.getProperty("recent.submissions.sort-option"), DiscoverQuery.SORT_ORDER.asc ); queryArgs.addFilterQueries(getParameterFacetQueries()); //Set the default limit to 11 //query.setFacetLimit(11); queryArgs.setFacetMinCount(1); //sort //TODO: why this kind of sorting ? Should the sort not be on how many times the value appears like we do in the filter by sidebar ? // queryArgs.setFacetSort(config.getPropertyAsType("solr.browse.sort","lex")); // queryArgs.setFacet(true); int offset = RequestUtils.getIntParameter(request, OFFSET); if (offset == -1) { offset = 0; } queryArgs.setFacetOffset(offset); //We add +1 so we can use the extra one to make sure that we need to show the next page //queryArgs.setFacetLimit(DEFAULT_PAGE_SIZE + 1); //queryArgs.addFacetField(new DiscoverFacetField(request.getParameter(FACET_FIELD))); try { queryResults = searchService.search(context, scope, queryArgs); } catch (SearchServiceException e) { log.error(e.getMessage(), e); } return queryResults; } /** * Retrieves the lowest date value in the given field * @param query a solr query * @param dateField the field for which we want to retrieve our date * @param filterquery the filterqueries * @return the lowest date found, in a date object */ private Date getLowestDateValue(Context context, String query, String dateField, String... filterquery){ try { DiscoverQuery discoverQuery = new DiscoverQuery(); discoverQuery.setQuery(query); discoverQuery.setMaxResults(1); discoverQuery.setSortField(dateField, DiscoverQuery.SORT_ORDER.asc); discoverQuery.addFilterQueries(filterquery); DiscoverResult rsp = searchService.search(context, discoverQuery); // if(0 < rsp.getResults().getNumFound()){ // return (Date) rsp.getResults().get(0).getFieldValue(dateField); // } }catch (Exception e){ log.error("Unable to get lowest date", e); } return null; } /** * Add a page title and trail links. */ public void addPageMeta(PageMeta pageMeta) throws SAXException, WingException, SQLException, IOException, AuthorizeException { Request request = ObjectModelHelper.getRequest(objectModel); String facetField = request.getParameter(FACET_FIELD); pageMeta.addMetadata("title").addContent(message("xmlui.ArtifactBrowser.AbstractSearch.type_" + facetField + "_browse")); pageMeta.addTrailLink(contextPath + "/", T_dspace_home); DSpaceObject dso = HandleUtil.obtainHandle(objectModel); if ((dso instanceof Collection) || (dso instanceof Community)) { HandleUtil.buildHandleTrail(dso, pageMeta, contextPath); } pageMeta.addTrail().addContent(message("xmlui.ArtifactBrowser.AbstractSearch.type_" + facetField + "_browse")); } @Override public void addBody(Body body) throws SAXException, WingException, UIException, SQLException, IOException, AuthorizeException { Request request = ObjectModelHelper.getRequest(objectModel); DSpaceObject dso = HandleUtil.obtainHandle(objectModel); // Set up the major variables //Collection collection = (Collection) dso; // Build the collection viewer division. //Make sure we get our results queryResults = getQueryResponse(dso); if (this.queryResults != null) { Map<String, List<DiscoverResult.FacetResult>> facetFields = this.queryResults.getFacetResults(); if (facetFields == null) { facetFields = new LinkedHashMap<String, List<DiscoverResult.FacetResult>>(); } // facetFields.addAll(this.queryResults.getFacetDates()); if (facetFields.size() > 0) { String facetField = String.valueOf(facetFields.keySet().toArray(new String[facetFields.size()])[0]); java.util.List<DiscoverResult.FacetResult> values = facetFields.get(facetField); if (values != null && 0 < values.size()) { Division results = body.addDivision("browse-by-" + facetField + "-results", "primary"); results.setHead(message("xmlui.ArtifactBrowser.AbstractSearch.type_" + request.getParameter(FACET_FIELD) + "_browse")); // Find our faceting offset int offSet = queryArgs.getFacetOffset(); if(offSet == -1){ offSet = 0; } //Only show the nextpageurl if we have at least one result following our current results String nextPageUrl = null; if (values.size() == (DEFAULT_PAGE_SIZE + 1)) { nextPageUrl = getNextPageURL(request); } results.setSimplePagination((int) queryResults.getDspaceObjects().size(), offSet + 1, (offSet + (values.size() - 1)), getPreviousPageURL(request), nextPageUrl); Table singleTable = results.addTable("browse-by-" + facetField + "-results", (int) (queryResults.getDspaceObjects().size() + 1), 1); List<String> filterQueries = new ArrayList<String>(); if(request.getParameterValues("fq") != null) { filterQueries = Arrays.asList(request.getParameterValues("fq")); } for (int i = 0; i < values.size(); i++) { DiscoverResult.FacetResult value = values.get(i); String displayedValue = value.getDisplayedValue(); String filterQuery = value.getAsFilterQuery(); // if(field.getGap() != null){ // //We have a date get the year so we can display it // DateFormat simpleDateformat = new SimpleDateFormat("yyyy"); // displayedValue = simpleDateformat.format(SolrServiceImpl.toDate(displayedValue)); // filterQuery = ClientUtils.escapeQueryChars(value.getFacetField().getName()) + ":" + displayedValue + "*"; // } Cell cell = singleTable.addRow().addCell(); //No use in selecting the same filter twice if(filterQueries.contains(filterQuery)){ cell.addContent(displayedValue + " (" + value.getCount() + ")"); } else { cell.addXref( contextPath + (dso == null ? "" : "/handle/" + dso.getHandle()) + "/discover?" + "&fq=" + URLEncoder.encode(filterQuery, "UTF-8") + (request.getQueryString() != null ? "&" + request.getQueryString() : ""), displayedValue + " (" + value.getCount() + ")" ); } } } } } //DSpaceObject dso = HandleUtil.obtainHandle(objectModel); /* if (dso != null) { if (dso instanceof Collection) { browseContext.addItem().addXref(contextPath + "/discovery/?q=search.resourcetype%3A2+AND+location%3Al" + dso.getID(), T_head_this_collection ); } if (dso instanceof Community) { browseContext.addItem().addXref(contextPath + "/discovery/?q=search.resourcetype%3A2+AND+location%3Am" + dso.getID(), T_head_this_community ); } } browseGlobal.addItem().addXref(contextPath + "/discovery/?q=search.resourcetype%3A2", T_head_all_of_dspace ); */ } private String getNextPageURL(Request request) { Map<String, String> parameters = new HashMap<String, String>(); parameters.put(FACET_FIELD, request.getParameter(FACET_FIELD)); if (queryArgs.getFacetOffset() != -1) { parameters.put(OFFSET, String.valueOf(queryArgs.getFacetOffset() + DEFAULT_PAGE_SIZE)); } // Add the filter queries String url = generateURL("browse-discovery", parameters); String[] fqs = getParameterFacetQueries(); if (fqs != null) { StringBuilder urlBuilder = new StringBuilder(url); for (String fq : fqs) { urlBuilder.append("&fq=").append(fq); } url = urlBuilder.toString(); } return url; } private String getPreviousPageURL(Request request) { //If our offset should be 0 then we shouldn't be able to view a previous page url if (0 == queryArgs.getFacetOffset()) { return null; } Map<String, String> parameters = new HashMap<String, String>(); parameters.put(FACET_FIELD, request.getParameter(FACET_FIELD)); if (queryArgs.getFacetOffset() != -1) { parameters.put(OFFSET, String.valueOf(queryArgs.getFacetOffset() - DEFAULT_PAGE_SIZE)); } // Add the filter queries String url = generateURL("browse-discovery", parameters); String[] fqs = getParameterFacetQueries(); if (fqs != null) { StringBuilder urlBuilder = new StringBuilder(url); for (String fq : fqs) { urlBuilder.append("&fq=").append(fq); } url = urlBuilder.toString(); } return url; } /** * Recycle */ public void recycle() { // Clear out our item's cache. this.queryResults = null; this.validity = null; super.recycle(); } public String[] getParameterFacetQueries() { Request request = ObjectModelHelper.getRequest(objectModel); return request.getParameterValues("fq") != null ? request.getParameterValues("fq") : new String[0]; } }