mirror of
https://github.com/DSpace/DSpace.git
synced 2025-10-07 01:54:22 +00:00
Merge pull request #86 from atmire/atmire-statistics
[DS-1243] @mire solr statistics contribution
This commit is contained in:
@@ -103,7 +103,8 @@ public class DSIndexer
|
||||
|
||||
private static int batchFlushAfterDocuments = ConfigurationManager.getIntProperty("search.batch.documents", 20);
|
||||
private static boolean batchProcessingMode = false;
|
||||
|
||||
static final Version luceneVersion = Version.LUCENE_35;
|
||||
|
||||
// Class to hold the index configuration (one instance per config line)
|
||||
private static class IndexConfig
|
||||
{
|
||||
@@ -637,7 +638,7 @@ public class DSIndexer
|
||||
Class analyzerClass = Class.forName(analyzerClassName);
|
||||
Constructor constructor = analyzerClass.getDeclaredConstructor(Version.class);
|
||||
constructor.setAccessible(true);
|
||||
analyzer = (Analyzer) constructor.newInstance(Version.LUCENE_33);
|
||||
analyzer = (Analyzer) constructor.newInstance(luceneVersion);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -914,7 +915,7 @@ public class DSIndexer
|
||||
throws IOException
|
||||
{
|
||||
Directory dir = FSDirectory.open(new File(indexDirectory));
|
||||
IndexWriterConfig iwc = new IndexWriterConfig(Version.LUCENE_33, getAnalyzer());
|
||||
IndexWriterConfig iwc = new IndexWriterConfig(luceneVersion, getAnalyzer());
|
||||
if(wipeExisting){
|
||||
iwc.setOpenMode(IndexWriterConfig.OpenMode.CREATE);
|
||||
}else{
|
||||
|
@@ -122,7 +122,7 @@ public class DSQuery
|
||||
// grab a searcher, and do the search
|
||||
IndexSearcher searcher = getSearcher(c);
|
||||
|
||||
QueryParser qp = new QueryParser(Version.LUCENE_33, "default", DSIndexer.getAnalyzer());
|
||||
QueryParser qp = new QueryParser(DSIndexer.luceneVersion, "default", DSIndexer.getAnalyzer());
|
||||
log.debug("Final query string: " + querystring);
|
||||
|
||||
if (operator == null || operator.equals("OR"))
|
||||
|
@@ -28,7 +28,9 @@ public class LoggerUsageEventListener extends AbstractUsageEventListener{
|
||||
|
||||
public void receiveEvent(Event event) {
|
||||
|
||||
if(event instanceof UsageEvent)
|
||||
//Search events are already logged
|
||||
//UsageSearchEvent is already logged in the search classes, no need to repeat this logging
|
||||
if(event instanceof UsageEvent && !(event instanceof UsageSearchEvent))
|
||||
{
|
||||
UsageEvent ue = (UsageEvent)event;
|
||||
|
||||
|
@@ -30,6 +30,7 @@ public class UsageEvent extends Event {
|
||||
REMOVE ("remove"),
|
||||
BROWSE ("browse"),
|
||||
SEARCH ("search"),
|
||||
WORKFLOW ("workflow"),
|
||||
LOGIN ("login"),
|
||||
SUBSCRIBE ("subscribe"),
|
||||
UNSUBSCRIBE ("unsubscribe"),
|
||||
@@ -59,12 +60,13 @@ public class UsageEvent extends Event {
|
||||
|
||||
private static String checkParams(Action action, HttpServletRequest request, Context context, DSpaceObject object)
|
||||
{
|
||||
StringBuilder eventName = new StringBuilder();
|
||||
if(action == null)
|
||||
{
|
||||
throw new IllegalStateException("action cannot be null");
|
||||
}
|
||||
|
||||
if(request == null)
|
||||
if(action != Action.WORKFLOW && request == null)
|
||||
{
|
||||
throw new IllegalStateException("request cannot be null");
|
||||
}
|
||||
@@ -75,21 +77,17 @@ public class UsageEvent extends Event {
|
||||
throw new IllegalStateException("context cannot be null");
|
||||
}
|
||||
|
||||
if(object == null)
|
||||
if(action != Action.WORKFLOW && action != Action.SEARCH && object == null)
|
||||
{
|
||||
throw new IllegalStateException("object cannot be null");
|
||||
}else
|
||||
if(object != null){
|
||||
String objText = Constants.typeText[object.getType()].toLowerCase();
|
||||
eventName.append(objText).append(":");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
String objText = Constants.typeText[object.getType()].toLowerCase();
|
||||
return objText + ":" + action.text();
|
||||
}catch(Exception e)
|
||||
{
|
||||
eventName.append(action.text());
|
||||
|
||||
}
|
||||
return "";
|
||||
|
||||
return eventName.toString();
|
||||
}
|
||||
|
||||
public UsageEvent(Action action, HttpServletRequest request, Context context, DSpaceObject object)
|
||||
|
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
* 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.usage;
|
||||
|
||||
import org.dspace.content.DSpaceObject;
|
||||
import org.dspace.core.Context;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Extends the standard usage event to contain search information
|
||||
* search information includes the query(s) used & the scope
|
||||
*
|
||||
* @author Kevin Van de Velde (kevin at atmire dot com)
|
||||
* @author Ben Bosman (ben at atmire dot com)
|
||||
* @author Mark Diggory (markd at atmire dot com)
|
||||
*/
|
||||
public class UsageSearchEvent extends UsageEvent{
|
||||
|
||||
private List<String> queries;
|
||||
private DSpaceObject scope;
|
||||
|
||||
/** Optional search parameters **/
|
||||
private int rpp;
|
||||
private String sortBy;
|
||||
private String sortOrder;
|
||||
private int page;
|
||||
|
||||
|
||||
public UsageSearchEvent(Action action, HttpServletRequest request, Context context, DSpaceObject object, List<String> queries, DSpaceObject scope) {
|
||||
super(action, request, context, object);
|
||||
|
||||
this.queries = queries;
|
||||
this.scope = scope;
|
||||
this.rpp = -1;
|
||||
this.sortBy = null;
|
||||
this.sortOrder = null;
|
||||
this.page = -1;
|
||||
}
|
||||
|
||||
public List<String> getQueries() {
|
||||
return queries;
|
||||
}
|
||||
|
||||
public DSpaceObject getScope() {
|
||||
return scope;
|
||||
}
|
||||
|
||||
public int getRpp() {
|
||||
return rpp;
|
||||
}
|
||||
|
||||
public void setRpp(int rpp) {
|
||||
this.rpp = rpp;
|
||||
}
|
||||
|
||||
public String getSortBy() {
|
||||
return sortBy;
|
||||
}
|
||||
|
||||
public void setSortBy(String sortBy) {
|
||||
this.sortBy = sortBy;
|
||||
}
|
||||
|
||||
public String getSortOrder() {
|
||||
return sortOrder;
|
||||
}
|
||||
|
||||
public void setSortOrder(String sortOrder) {
|
||||
this.sortOrder = sortOrder;
|
||||
}
|
||||
|
||||
public int getPage() {
|
||||
return page;
|
||||
}
|
||||
|
||||
public void setPage(int page) {
|
||||
this.page = page;
|
||||
}
|
||||
}
|
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* 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.usage;
|
||||
|
||||
import org.dspace.content.Collection;
|
||||
import org.dspace.content.InProgressSubmission;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.eperson.EPerson;
|
||||
import org.dspace.eperson.Group;
|
||||
|
||||
/**
|
||||
* Extends the standard usage event to contain workflow information
|
||||
*
|
||||
*
|
||||
* @author Kevin Van de Velde (kevin at atmire dot com)
|
||||
* @author Ben Bosman (ben at atmire dot com)
|
||||
* @author Mark Diggory (markd at atmire dot com)
|
||||
*/
|
||||
public class UsageWorkflowEvent extends UsageEvent {
|
||||
|
||||
private String workflowStep;
|
||||
private String oldState;
|
||||
private EPerson[] epersonOwners;
|
||||
private Group[] groupOwners;
|
||||
private Collection scope;
|
||||
private EPerson actor;
|
||||
private InProgressSubmission workflowItem;
|
||||
|
||||
public UsageWorkflowEvent(Context context, Item item, InProgressSubmission workflowItem, String workflowStep, String oldState, Collection scope, EPerson actor) {
|
||||
super(Action.WORKFLOW, null, context, item);
|
||||
this.workflowItem = workflowItem;
|
||||
this.workflowStep = workflowStep;
|
||||
this.oldState = oldState;
|
||||
this.scope = scope;
|
||||
this.actor = actor;
|
||||
}
|
||||
|
||||
public String getWorkflowStep() {
|
||||
return workflowStep;
|
||||
}
|
||||
|
||||
|
||||
public String getOldState() {
|
||||
return oldState;
|
||||
}
|
||||
|
||||
public Collection getScope() {
|
||||
return scope;
|
||||
}
|
||||
|
||||
public EPerson[] getEpersonOwners() {
|
||||
return epersonOwners;
|
||||
}
|
||||
|
||||
public void setEpersonOwners(EPerson... epersonOwners) {
|
||||
this.epersonOwners = epersonOwners;
|
||||
}
|
||||
|
||||
public Group[] getGroupOwners() {
|
||||
return groupOwners;
|
||||
}
|
||||
|
||||
public void setGroupOwners(Group... newGroupOwner) {
|
||||
this.groupOwners = newGroupOwner;
|
||||
}
|
||||
|
||||
public EPerson getActor() {
|
||||
return actor;
|
||||
}
|
||||
|
||||
public InProgressSubmission getWorkflowItem() {
|
||||
return workflowItem;
|
||||
}
|
||||
}
|
@@ -41,6 +41,8 @@ import org.dspace.handle.HandleManager;
|
||||
import org.dspace.storage.rdbms.DatabaseManager;
|
||||
import org.dspace.storage.rdbms.TableRow;
|
||||
import org.dspace.storage.rdbms.TableRowIterator;
|
||||
import org.dspace.usage.UsageWorkflowEvent;
|
||||
import org.dspace.utils.DSpace;
|
||||
|
||||
/**
|
||||
* Workflow state machine
|
||||
@@ -518,6 +520,9 @@ public class WorkflowManager
|
||||
Group mygroup = null;
|
||||
boolean archived = false;
|
||||
|
||||
//Gather our old data for launching the workflow event
|
||||
int oldState = wi.getState();
|
||||
|
||||
wi.setState(newstate);
|
||||
|
||||
switch (newstate)
|
||||
@@ -657,6 +662,8 @@ public class WorkflowManager
|
||||
break;
|
||||
}
|
||||
|
||||
logWorkflowEvent(c, wi.getItem(), wi, c.getCurrentUser(), newstate, newowner, mycollection, oldState, mygroup);
|
||||
|
||||
if (!archived)
|
||||
{
|
||||
wi.update();
|
||||
@@ -665,6 +672,22 @@ public class WorkflowManager
|
||||
return archived;
|
||||
}
|
||||
|
||||
private static void logWorkflowEvent(Context c, Item item, WorkflowItem workflowItem, EPerson actor, int newstate, EPerson newOwner, Collection mycollection, int oldState, Group newOwnerGroup) {
|
||||
if(newstate == WFSTATE_ARCHIVE || newstate == WFSTATE_STEP1POOL || newstate == WFSTATE_STEP2POOL || newstate == WFSTATE_STEP3POOL){
|
||||
//Clear the newowner variable since this one isn't owned anymore !
|
||||
newOwner = null;
|
||||
}
|
||||
|
||||
UsageWorkflowEvent usageWorkflowEvent = new UsageWorkflowEvent(c, item, workflowItem, workflowText[newstate], workflowText[oldState], mycollection, actor);
|
||||
if(newOwner != null){
|
||||
usageWorkflowEvent.setEpersonOwners(newOwner);
|
||||
}
|
||||
if(newOwnerGroup != null){
|
||||
usageWorkflowEvent.setGroupOwners(newOwnerGroup);
|
||||
}
|
||||
new DSpace().getEventService().fireEvent(usageWorkflowEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the text representing the given workflow state
|
||||
*
|
||||
@@ -816,6 +839,8 @@ public class WorkflowManager
|
||||
String rejection_message) throws SQLException, AuthorizeException,
|
||||
IOException
|
||||
{
|
||||
|
||||
int oldState = wi.getState();
|
||||
// authorize a DSpaceActions.REJECT
|
||||
// stop workflow
|
||||
deleteTasks(c, wi);
|
||||
@@ -848,6 +873,8 @@ public class WorkflowManager
|
||||
+ "collection_id=" + wi.getCollection().getID() + "eperson_id="
|
||||
+ e.getID()));
|
||||
|
||||
logWorkflowEvent(c, wsi.getItem(), wi, e, WFSTATE_SUBMIT, null, wsi.getCollection(), oldState, null);
|
||||
|
||||
return wsi;
|
||||
}
|
||||
|
||||
|
@@ -19,6 +19,8 @@ import org.dspace.eperson.Group;
|
||||
import org.dspace.handle.HandleManager;
|
||||
import org.dspace.storage.rdbms.DatabaseManager;
|
||||
import org.dspace.storage.rdbms.TableRow;
|
||||
import org.dspace.usage.UsageWorkflowEvent;
|
||||
import org.dspace.utils.DSpace;
|
||||
import org.dspace.xmlworkflow.state.Step;
|
||||
import org.dspace.xmlworkflow.state.Workflow;
|
||||
import org.dspace.xmlworkflow.state.actions.*;
|
||||
@@ -144,6 +146,9 @@ public class XmlWorkflowManager {
|
||||
// record the start of the workflow w/provenance message
|
||||
recordStart(wfi.getItem(), firstActionConfig.getProcessingAction());
|
||||
|
||||
//Fire an event !
|
||||
logWorkflowEvent(context, firstStep.getWorkflow().getID(), null, null, wfi, null, firstStep, firstActionConfig);
|
||||
|
||||
//If we don't have a UI activate it
|
||||
if(!firstActionConfig.requiresUI()){
|
||||
ActionResult outcome = firstActionConfig.getProcessingAction().execute(context, wfi, firstStep, null);
|
||||
@@ -185,55 +190,81 @@ public class XmlWorkflowManager {
|
||||
return null;
|
||||
}else
|
||||
if (currentOutcome.getType() == ActionResult.TYPE.TYPE_OUTCOME) {
|
||||
//We have completed our action search & retrieve the next action
|
||||
Step nextStep = null;
|
||||
WorkflowActionConfig nextActionConfig = null;
|
||||
if(currentOutcome.getResult() == ActionResult.OUTCOME_COMPLETE){
|
||||
nextActionConfig = currentStep.getNextAction(currentActionConfig);
|
||||
}
|
||||
|
||||
if (nextActionConfig != null) {
|
||||
nextActionConfig.getProcessingAction().activate(c, wfi);
|
||||
if (nextActionConfig.requiresUI() && !enteredNewStep) {
|
||||
createOwnedTask(c, wfi, currentStep, nextActionConfig, user);
|
||||
return nextActionConfig;
|
||||
} else if( nextActionConfig.requiresUI() && enteredNewStep){
|
||||
//We have entered a new step and have encountered a UI, return null since the current user doesn't have anything to do with this
|
||||
c.restoreAuthSystemState();
|
||||
return null;
|
||||
} else {
|
||||
ActionResult newOutcome = nextActionConfig.getProcessingAction().execute(c, wfi, currentStep, null);
|
||||
return processOutcome(c, user, workflow, currentStep, nextActionConfig, newOutcome, wfi, enteredNewStep);
|
||||
try {
|
||||
//We have completed our action search & retrieve the next action
|
||||
if(currentOutcome.getResult() == ActionResult.OUTCOME_COMPLETE){
|
||||
nextActionConfig = currentStep.getNextAction(currentActionConfig);
|
||||
}
|
||||
}else
|
||||
if(enteredNewStep){
|
||||
// If the user finished his/her step, we keep processing until there is a UI step action or no step at all
|
||||
Step nextStep = workflow.getNextStep(c, wfi, currentStep, currentOutcome.getResult());
|
||||
c.turnOffAuthorisationSystem();
|
||||
return processNextStep(c, user, workflow, currentOutcome, wfi, nextStep);
|
||||
} else {
|
||||
//
|
||||
ClaimedTask task = ClaimedTask.findByWorkflowIdAndEPerson(c, wfi.getID(), user.getID());
|
||||
|
||||
//Check if we have a task for this action (might not be the case with automatic steps)
|
||||
//First add it to our list of finished users, since no more actions remain
|
||||
WorkflowRequirementsManager.addFinishedUser(c, wfi, user);
|
||||
c.turnOffAuthorisationSystem();
|
||||
//Check if our requirements have been met
|
||||
if((currentStep.isFinished(c, wfi) && currentOutcome.getResult() == ActionResult.OUTCOME_COMPLETE) || currentOutcome.getResult() != ActionResult.OUTCOME_COMPLETE){
|
||||
//Delete all the table rows containing the users who performed this task
|
||||
WorkflowRequirementsManager.clearInProgressUsers(c, wfi);
|
||||
//Remove all the tasks
|
||||
XmlWorkflowManager.deleteAllTasks(c, wfi);
|
||||
if (nextActionConfig != null) {
|
||||
//We remain in the current step since an action is found
|
||||
nextStep = currentStep;
|
||||
nextActionConfig.getProcessingAction().activate(c, wfi);
|
||||
if (nextActionConfig.requiresUI() && !enteredNewStep) {
|
||||
createOwnedTask(c, wfi, currentStep, nextActionConfig, user);
|
||||
return nextActionConfig;
|
||||
} else if( nextActionConfig.requiresUI() && enteredNewStep){
|
||||
//We have entered a new step and have encountered a UI, return null since the current user doesn't have anything to do with this
|
||||
c.restoreAuthSystemState();
|
||||
return null;
|
||||
} else {
|
||||
ActionResult newOutcome = nextActionConfig.getProcessingAction().execute(c, wfi, currentStep, null);
|
||||
return processOutcome(c, user, workflow, currentStep, nextActionConfig, newOutcome, wfi, enteredNewStep);
|
||||
}
|
||||
}else
|
||||
if(enteredNewStep){
|
||||
// If the user finished his/her step, we keep processing until there is a UI step action or no step at all
|
||||
nextStep = workflow.getNextStep(c, wfi, currentStep, currentOutcome.getResult());
|
||||
c.turnOffAuthorisationSystem();
|
||||
nextActionConfig = processNextStep(c, user, workflow, currentOutcome, wfi, nextStep);
|
||||
//If we require a user interface return null so that the user is redirected to the "submissions page"
|
||||
if(nextActionConfig == null || nextActionConfig.requiresUI()){
|
||||
return null;
|
||||
}else{
|
||||
return nextActionConfig;
|
||||
}
|
||||
} else {
|
||||
ClaimedTask task = ClaimedTask.findByWorkflowIdAndEPerson(c, wfi.getID(), user.getID());
|
||||
|
||||
//Check if we have a task for this action (might not be the case with automatic steps)
|
||||
//First add it to our list of finished users, since no more actions remain
|
||||
WorkflowRequirementsManager.addFinishedUser(c, wfi, user);
|
||||
c.turnOffAuthorisationSystem();
|
||||
//Check if our requirements have been met
|
||||
if((currentStep.isFinished(c, wfi) && currentOutcome.getResult() == ActionResult.OUTCOME_COMPLETE) || currentOutcome.getResult() != ActionResult.OUTCOME_COMPLETE){
|
||||
//Delete all the table rows containing the users who performed this task
|
||||
WorkflowRequirementsManager.clearInProgressUsers(c, wfi);
|
||||
//Remove all the tasks
|
||||
XmlWorkflowManager.deleteAllTasks(c, wfi);
|
||||
|
||||
|
||||
Step nextStep = workflow.getNextStep(c, wfi, currentStep, currentOutcome.getResult());
|
||||
nextStep = workflow.getNextStep(c, wfi, currentStep, currentOutcome.getResult());
|
||||
|
||||
return processNextStep(c, user, workflow, currentOutcome, wfi, nextStep);
|
||||
}else{
|
||||
//We are done with our actions so go to the submissions page but remove action ClaimedAction first
|
||||
deleteClaimedTask(c, wfi, task);
|
||||
c.restoreAuthSystemState();
|
||||
return null;
|
||||
nextActionConfig = processNextStep(c, user, workflow, currentOutcome, wfi, nextStep);
|
||||
//If we require a user interface return null so that the user is redirected to the "submissions page"
|
||||
if(nextActionConfig == null || nextActionConfig.requiresUI()){
|
||||
return null;
|
||||
}else{
|
||||
return nextActionConfig;
|
||||
}
|
||||
}else{
|
||||
//We are done with our actions so go to the submissions page but remove action ClaimedAction first
|
||||
deleteClaimedTask(c, wfi, task);
|
||||
c.restoreAuthSystemState();
|
||||
nextStep = currentStep;
|
||||
nextActionConfig = currentActionConfig;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}catch (Exception e){
|
||||
log.error("error while processing workflow outcome", e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
finally {
|
||||
if((nextStep != null && nextActionConfig != null) || wfi.getItem().isArchived()){
|
||||
logWorkflowEvent(c, currentStep.getWorkflow().getID(), currentStep.getId(), currentActionConfig.getId(), wfi, user, nextStep, nextActionConfig);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,6 +274,51 @@ public class XmlWorkflowManager {
|
||||
throw new WorkflowException("Invalid step outcome");
|
||||
}
|
||||
|
||||
protected static void logWorkflowEvent(Context c, String workflowId, String previousStepId, String previousActionConfigId, XmlWorkflowItem wfi, EPerson actor, Step newStep, WorkflowActionConfig newActionConfig) throws SQLException {
|
||||
try {
|
||||
//Fire an event so we can log our action !
|
||||
Item item = wfi.getItem();
|
||||
Collection myCollection = wfi.getCollection();
|
||||
String workflowStepString = null;
|
||||
|
||||
List<EPerson> currentEpersonOwners = new ArrayList<EPerson>();
|
||||
List<Group> currentGroupOwners = new ArrayList<Group>();
|
||||
//These are only null if our item is sent back to the submission
|
||||
if(newStep != null && newActionConfig != null){
|
||||
workflowStepString = workflowId + "." + newStep.getId() + "." + newActionConfig.getId();
|
||||
|
||||
//Retrieve the current owners of the task
|
||||
List<ClaimedTask> claimedTasks = ClaimedTask.find(c, wfi.getID(), newStep.getId());
|
||||
List<PoolTask> pooledTasks = PoolTask.find(c, wfi);
|
||||
for (PoolTask poolTask : pooledTasks){
|
||||
if(poolTask.getEpersonID() != -1){
|
||||
currentEpersonOwners.add(EPerson.find(c, poolTask.getEpersonID()));
|
||||
}else{
|
||||
currentGroupOwners.add(Group.find(c, poolTask.getGroupID()));
|
||||
}
|
||||
}
|
||||
for (ClaimedTask claimedTask : claimedTasks) {
|
||||
currentEpersonOwners.add(EPerson.find(c, claimedTask.getOwnerID()));
|
||||
}
|
||||
}
|
||||
String previousWorkflowStepString = null;
|
||||
if(previousStepId != null && previousActionConfigId != null){
|
||||
previousWorkflowStepString = workflowId + "." + previousStepId + "." + previousActionConfigId;
|
||||
}
|
||||
|
||||
//Fire our usage event !
|
||||
UsageWorkflowEvent usageWorkflowEvent = new UsageWorkflowEvent(c, item, wfi, workflowStepString, previousWorkflowStepString, myCollection, actor);
|
||||
|
||||
usageWorkflowEvent.setEpersonOwners(currentEpersonOwners.toArray(new EPerson[currentEpersonOwners.size()]));
|
||||
usageWorkflowEvent.setGroupOwners(currentGroupOwners.toArray(new Group[currentGroupOwners.size()]));
|
||||
|
||||
new DSpace().getEventService().fireEvent(usageWorkflowEvent);
|
||||
} catch (Exception e) {
|
||||
//Catch all errors we do not want our workflow to crash because the logging threw an exception
|
||||
log.error(LogManager.getHeader(c, "Error while logging workflow event", "Workflow Item: " + wfi.getID()), e);
|
||||
}
|
||||
}
|
||||
|
||||
private static WorkflowActionConfig processNextStep(Context c, EPerson user, Workflow workflow, ActionResult currentOutcome, XmlWorkflowItem wfi, Step nextStep) throws SQLException, IOException, AuthorizeException, WorkflowException, WorkflowConfigurationException {
|
||||
WorkflowActionConfig nextActionConfig;
|
||||
if(nextStep!=null){
|
||||
@@ -253,7 +329,7 @@ public class XmlWorkflowManager {
|
||||
if (nextActionConfig.requiresUI()) {
|
||||
//Since a new step has been started, stop executing actions once one with a user interface is present.
|
||||
c.restoreAuthSystemState();
|
||||
return null;
|
||||
return nextActionConfig;
|
||||
} else {
|
||||
ActionResult newOutcome = nextActionConfig.getProcessingAction().execute(c, wfi, nextStep, null);
|
||||
c.restoreAuthSystemState();
|
||||
@@ -581,6 +657,18 @@ public class XmlWorkflowManager {
|
||||
String rejection_message) throws SQLException, AuthorizeException,
|
||||
IOException
|
||||
{
|
||||
|
||||
String workflowID = null;
|
||||
String currentStepId = null;
|
||||
String currentActionConfigId = null;
|
||||
ClaimedTask claimedTask = ClaimedTask.findByWorkflowIdAndEPerson(c, wi.getID(), e.getID());
|
||||
if(claimedTask != null){
|
||||
//Log it
|
||||
workflowID = claimedTask.getWorkflowID();
|
||||
currentStepId = claimedTask.getStepID();
|
||||
currentActionConfigId = claimedTask.getActionID();
|
||||
}
|
||||
|
||||
// authorize a DSpaceActions.REJECT
|
||||
// stop workflow
|
||||
deleteAllTasks(c, wi);
|
||||
@@ -627,6 +715,7 @@ public class XmlWorkflowManager {
|
||||
+ "collection_id=" + wi.getCollection().getID() + "eperson_id="
|
||||
+ e.getID()));
|
||||
|
||||
logWorkflowEvent(c, workflowID, currentStepId, currentActionConfigId, wi, e, null, null);
|
||||
|
||||
c.restoreAuthSystemState();
|
||||
return wsi;
|
||||
|
@@ -29,12 +29,16 @@
|
||||
<dependency>
|
||||
<groupId>org.apache.solr</groupId>
|
||||
<artifactId>solr-solrj</artifactId>
|
||||
<version>3.3.0</version>
|
||||
<version>3.5.0</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>jcl-over-slf4j</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
|
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* 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.apache.cocoon.environment.Request;
|
||||
import org.dspace.app.xmlui.cocoon.SearchLoggerAction;
|
||||
import org.dspace.app.xmlui.utils.ContextUtil;
|
||||
import org.dspace.core.Context;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Kevin Van de Velde (kevin at atmire dot com)
|
||||
* @author Ben Bosman (ben at atmire dot com)
|
||||
* @author Mark Diggory (markd at atmire dot com)
|
||||
*/
|
||||
public class DiscoverySearchLoggerAction extends SearchLoggerAction {
|
||||
|
||||
@Override
|
||||
protected List<String> getQueries(Request request) throws SQLException {
|
||||
Context context = ContextUtil.obtainContext(request);
|
||||
List<String> queries = new ArrayList<String>();
|
||||
if(request.getParameter("query") != null){
|
||||
queries.add(request.getParameter("query"));
|
||||
}
|
||||
|
||||
queries.addAll(Arrays.asList(DiscoveryUIUtils.getFilterQueries(request, context)));
|
||||
|
||||
return queries;
|
||||
}
|
||||
}
|
@@ -44,6 +44,9 @@ and searching the repository.
|
||||
<map:transformer name="RestrictedItem" src="org.dspace.app.xmlui.aspect.artifactbrowser.RestrictedItem"/>
|
||||
</map:transformers>
|
||||
|
||||
<map:actions>
|
||||
<map:action name="DiscoverySearchLoggerAction" src="org.dspace.app.xmlui.aspect.discovery.DiscoverySearchLoggerAction"/>
|
||||
</map:actions>
|
||||
|
||||
<map:matchers default="wildcard">
|
||||
<map:matcher name="HandleTypeMatcher" src="org.dspace.app.xmlui.aspect.general.HandleTypeMatcher"/>
|
||||
@@ -97,6 +100,7 @@ and searching the repository.
|
||||
|
||||
<!-- Search -->
|
||||
<map:match pattern="discover">
|
||||
<map:act type="DiscoverySearchLoggerAction"/>
|
||||
<map:transform type="SidebarFacetsTransformer"/>
|
||||
<map:transform type="SimpleSearch"/>
|
||||
<map:transform type="IncludePageMeta">
|
||||
@@ -133,6 +137,7 @@ and searching the repository.
|
||||
|
||||
<!-- Simple search -->
|
||||
<map:match pattern="handle/*/*/discover">
|
||||
<map:act type="DiscoverySearchLoggerAction"/>
|
||||
<map:transform type="SidebarFacetsTransformer"/>
|
||||
<map:transform type="SimpleSearch"/>
|
||||
<map:transform type="IncludePageMeta">
|
||||
|
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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/
|
||||
*/
|
||||
(function ($) {
|
||||
|
||||
/**
|
||||
* Function ensures that all the links clicked in our results pass through the internal logging mechanism
|
||||
*/
|
||||
$(document).ready(function() {
|
||||
//Retrieve all links with handles attached (comm/coll/item links)
|
||||
var urls = $('div#aspect_discovery_SimpleSearch_div_search-results').find('a');
|
||||
|
||||
urls.click(function(){
|
||||
var $this = $(this);
|
||||
//Instead of redirecting us to the page, first send us to the statistics logger
|
||||
//By doing this we ensure that we register the query to the result
|
||||
var form = $('form#aspect_discovery_SimpleSearch_div_main-form');
|
||||
form.attr('action', form.attr('action').replace('/discover', '') + '/dso-display');
|
||||
//Manipulate the fq boxes to all switch to query since the logging doesn't take into account filter queries
|
||||
form.find('input[name="fq"]').attr('name', 'query');
|
||||
form.find('input[name="redirectUrl"]').val($this.attr('href'));
|
||||
form.submit();
|
||||
return false;
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
})(jQuery);
|
File diff suppressed because it is too large
Load Diff
@@ -1,338 +1,338 @@
|
||||
/**
|
||||
* 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.webui.servlet;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.ServletException;
|
||||
import org.dspace.authorize.AuthorizeException;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
import org.dspace.core.ConfigurationManager;
|
||||
import org.dspace.core.Constants;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.eperson.Group;
|
||||
|
||||
import org.dspace.content.DSpaceObject;
|
||||
import org.dspace.handle.HandleManager;
|
||||
|
||||
import org.dspace.statistics.Dataset;
|
||||
import org.dspace.statistics.content.DatasetDSpaceObjectGenerator;
|
||||
import org.dspace.statistics.content.DatasetTimeGenerator;
|
||||
import org.dspace.statistics.content.DatasetTypeGenerator;
|
||||
import org.dspace.statistics.content.StatisticsDataVisits;
|
||||
import org.dspace.statistics.content.StatisticsListing;
|
||||
import org.dspace.statistics.content.StatisticsTable;
|
||||
|
||||
import org.dspace.app.webui.components.StatisticsBean;
|
||||
import org.dspace.app.webui.util.JSPManager;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author Kim Shepherd
|
||||
* @version $Revision: 4386 $
|
||||
*/
|
||||
public class DisplayStatisticsServlet extends DSpaceServlet
|
||||
{
|
||||
/** log4j logger */
|
||||
private static Logger log = Logger.getLogger(DisplayStatisticsServlet.class);
|
||||
|
||||
|
||||
protected void doDSGet(Context context, HttpServletRequest request,
|
||||
HttpServletResponse response) throws ServletException, IOException,
|
||||
SQLException, AuthorizeException
|
||||
{
|
||||
|
||||
// is the statistics data publically viewable?
|
||||
boolean privatereport = ConfigurationManager.getBooleanProperty("usage-statistics", "authorization.admin");
|
||||
|
||||
// is the user a member of the Administrator (1) group?
|
||||
boolean admin = Group.isMember(context, 1);
|
||||
|
||||
if (!privatereport || admin)
|
||||
{
|
||||
displayStatistics(context, request, response);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new AuthorizeException();
|
||||
}
|
||||
}
|
||||
|
||||
protected void displayStatistics(Context context, HttpServletRequest request,
|
||||
HttpServletResponse response) throws ServletException, IOException,
|
||||
SQLException, AuthorizeException
|
||||
{
|
||||
|
||||
DSpaceObject dso = null;
|
||||
String handle = request.getParameter("handle");
|
||||
|
||||
if("".equals(handle) || handle == null)
|
||||
{
|
||||
// We didn't get passed a handle parameter.
|
||||
// That means we're looking at /handle/*/*/statistics
|
||||
// with handle injected as attribute from HandleServlet
|
||||
handle = (String) request.getAttribute("handle");
|
||||
|
||||
}
|
||||
|
||||
if(handle != null)
|
||||
{
|
||||
dso = HandleManager.resolveToObject(context, handle);
|
||||
}
|
||||
|
||||
if(dso == null)
|
||||
{
|
||||
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||
JSPManager.showJSP(request, response, "/error/404.jsp");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
boolean isItem = false;
|
||||
|
||||
StatisticsBean statsVisits = new StatisticsBean();
|
||||
StatisticsBean statsMonthlyVisits = new StatisticsBean();
|
||||
StatisticsBean statsFileDownloads = new StatisticsBean();
|
||||
StatisticsBean statsCountryVisits = new StatisticsBean();
|
||||
StatisticsBean statsCityVisits = new StatisticsBean();
|
||||
|
||||
try
|
||||
{
|
||||
StatisticsListing statListing = new StatisticsListing(
|
||||
new StatisticsDataVisits(dso));
|
||||
|
||||
statListing.setTitle("Total Visits");
|
||||
statListing.setId("list1");
|
||||
|
||||
DatasetDSpaceObjectGenerator dsoAxis = new DatasetDSpaceObjectGenerator();
|
||||
dsoAxis.addDsoChild(dso.getType(), 10, false, -1);
|
||||
statListing.addDatasetGenerator(dsoAxis);
|
||||
Dataset dataset = statListing.getDataset(context);
|
||||
|
||||
dataset = statListing.getDataset();
|
||||
|
||||
if (dataset == null)
|
||||
{
|
||||
|
||||
dataset = statListing.getDataset(context);
|
||||
}
|
||||
|
||||
if (dataset != null)
|
||||
{
|
||||
String[][] matrix = dataset.getMatrixFormatted();
|
||||
List<String> colLabels = dataset.getColLabels();
|
||||
List<String> rowLabels = dataset.getRowLabels();
|
||||
|
||||
statsVisits.setMatrix(matrix);
|
||||
statsVisits.setColLabels(colLabels);
|
||||
statsVisits.setRowLabels(rowLabels);
|
||||
}
|
||||
|
||||
|
||||
} catch (Exception e)
|
||||
{
|
||||
log.error(
|
||||
"Error occured while creating statistics for dso with ID: "
|
||||
+ dso.getID() + " and type " + dso.getType()
|
||||
+ " and handle: " + dso.getHandle(), e);
|
||||
}
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
StatisticsTable statisticsTable = new StatisticsTable(new StatisticsDataVisits(dso));
|
||||
|
||||
statisticsTable.setTitle("Total Visits Per Month");
|
||||
statisticsTable.setId("tab1");
|
||||
|
||||
DatasetTimeGenerator timeAxis = new DatasetTimeGenerator();
|
||||
timeAxis.setDateInterval("month", "-6", "+1");
|
||||
statisticsTable.addDatasetGenerator(timeAxis);
|
||||
|
||||
DatasetDSpaceObjectGenerator dsoAxis = new DatasetDSpaceObjectGenerator();
|
||||
dsoAxis.addDsoChild(dso.getType(), 10, false, -1);
|
||||
statisticsTable.addDatasetGenerator(dsoAxis);
|
||||
Dataset dataset = statisticsTable.getDataset(context);
|
||||
|
||||
dataset = statisticsTable.getDataset();
|
||||
|
||||
if (dataset == null)
|
||||
{
|
||||
|
||||
dataset = statisticsTable.getDataset(context);
|
||||
}
|
||||
|
||||
if (dataset != null)
|
||||
{
|
||||
String[][] matrix = dataset.getMatrixFormatted();
|
||||
List<String> colLabels = dataset.getColLabels();
|
||||
List<String> rowLabels = dataset.getRowLabels();
|
||||
|
||||
statsMonthlyVisits.setMatrix(matrix);
|
||||
statsMonthlyVisits.setColLabels(colLabels);
|
||||
statsMonthlyVisits.setRowLabels(rowLabels);
|
||||
}
|
||||
} catch (Exception e)
|
||||
{
|
||||
log.error(
|
||||
"Error occured while creating statistics for dso with ID: "
|
||||
+ dso.getID() + " and type " + dso.getType()
|
||||
+ " and handle: " + dso.getHandle(), e);
|
||||
}
|
||||
|
||||
if(dso instanceof org.dspace.content.Item)
|
||||
{
|
||||
isItem = true;
|
||||
try
|
||||
{
|
||||
|
||||
StatisticsListing statisticsTable = new StatisticsListing(new StatisticsDataVisits(dso));
|
||||
|
||||
statisticsTable.setTitle("File Downloads");
|
||||
statisticsTable.setId("tab1");
|
||||
|
||||
DatasetDSpaceObjectGenerator dsoAxis = new DatasetDSpaceObjectGenerator();
|
||||
dsoAxis.addDsoChild(Constants.BITSTREAM, 10, false, -1);
|
||||
statisticsTable.addDatasetGenerator(dsoAxis);
|
||||
|
||||
Dataset dataset = statisticsTable.getDataset(context);
|
||||
|
||||
dataset = statisticsTable.getDataset();
|
||||
|
||||
if (dataset == null)
|
||||
{
|
||||
|
||||
dataset = statisticsTable.getDataset(context);
|
||||
}
|
||||
|
||||
if (dataset != null)
|
||||
{
|
||||
String[][] matrix = dataset.getMatrixFormatted();
|
||||
List<String> colLabels = dataset.getColLabels();
|
||||
List<String> rowLabels = dataset.getRowLabels();
|
||||
|
||||
statsFileDownloads.setMatrix(matrix);
|
||||
statsFileDownloads.setColLabels(colLabels);
|
||||
statsFileDownloads.setRowLabels(rowLabels);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.error(
|
||||
"Error occured while creating statistics for dso with ID: "
|
||||
+ dso.getID() + " and type " + dso.getType()
|
||||
+ " and handle: " + dso.getHandle(), e);
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
StatisticsListing statisticsTable = new StatisticsListing(new StatisticsDataVisits(dso));
|
||||
|
||||
statisticsTable.setTitle("Top country views");
|
||||
statisticsTable.setId("tab1");
|
||||
|
||||
DatasetTypeGenerator typeAxis = new DatasetTypeGenerator();
|
||||
typeAxis.setType("countryCode");
|
||||
typeAxis.setMax(10);
|
||||
statisticsTable.addDatasetGenerator(typeAxis);
|
||||
|
||||
Dataset dataset = statisticsTable.getDataset(context);
|
||||
|
||||
dataset = statisticsTable.getDataset();
|
||||
|
||||
if (dataset == null)
|
||||
{
|
||||
|
||||
dataset = statisticsTable.getDataset(context);
|
||||
}
|
||||
|
||||
if (dataset != null)
|
||||
{
|
||||
String[][] matrix = dataset.getMatrixFormatted();
|
||||
List<String> colLabels = dataset.getColLabels();
|
||||
List<String> rowLabels = dataset.getRowLabels();
|
||||
|
||||
statsCountryVisits.setMatrix(matrix);
|
||||
statsCountryVisits.setColLabels(colLabels);
|
||||
statsCountryVisits.setRowLabels(rowLabels);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.error(
|
||||
"Error occured while creating statistics for dso with ID: "
|
||||
+ dso.getID() + " and type " + dso.getType()
|
||||
+ " and handle: " + dso.getHandle(), e);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
StatisticsListing statisticsTable = new StatisticsListing(new StatisticsDataVisits(dso));
|
||||
|
||||
statisticsTable.setTitle("Top city views");
|
||||
statisticsTable.setId("tab1");
|
||||
|
||||
DatasetTypeGenerator typeAxis = new DatasetTypeGenerator();
|
||||
typeAxis.setType("city");
|
||||
typeAxis.setMax(10);
|
||||
statisticsTable.addDatasetGenerator(typeAxis);
|
||||
|
||||
Dataset dataset = statisticsTable.getDataset(context);
|
||||
|
||||
dataset = statisticsTable.getDataset();
|
||||
|
||||
if (dataset == null)
|
||||
{
|
||||
|
||||
dataset = statisticsTable.getDataset(context);
|
||||
}
|
||||
|
||||
if (dataset != null)
|
||||
{
|
||||
String[][] matrix = dataset.getMatrixFormatted();
|
||||
List<String> colLabels = dataset.getColLabels();
|
||||
List<String> rowLabels = dataset.getRowLabels();
|
||||
|
||||
statsCityVisits.setMatrix(matrix);
|
||||
statsCityVisits.setColLabels(colLabels);
|
||||
statsCityVisits.setRowLabels(rowLabels);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.error(
|
||||
"Error occured while creating statistics for dso with ID: "
|
||||
+ dso.getID() + " and type " + dso.getType()
|
||||
+ " and handle: " + dso.getHandle(), e);
|
||||
}
|
||||
|
||||
|
||||
request.setAttribute("statsVisits", statsVisits);
|
||||
request.setAttribute("statsMonthlyVisits", statsMonthlyVisits);
|
||||
request.setAttribute("statsFileDownloads", statsFileDownloads);
|
||||
request.setAttribute("statsCountryVisits",statsCountryVisits);
|
||||
request.setAttribute("statsCityVisits", statsCityVisits);
|
||||
request.setAttribute("isItem", isItem);
|
||||
|
||||
JSPManager.showJSP(request, response, "display-statistics.jsp");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* 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.webui.servlet;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.ServletException;
|
||||
import org.dspace.authorize.AuthorizeException;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
import org.dspace.core.ConfigurationManager;
|
||||
import org.dspace.core.Constants;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.eperson.Group;
|
||||
|
||||
import org.dspace.content.DSpaceObject;
|
||||
import org.dspace.handle.HandleManager;
|
||||
|
||||
import org.dspace.statistics.Dataset;
|
||||
import org.dspace.statistics.content.DatasetDSpaceObjectGenerator;
|
||||
import org.dspace.statistics.content.DatasetTimeGenerator;
|
||||
import org.dspace.statistics.content.DatasetTypeGenerator;
|
||||
import org.dspace.statistics.content.StatisticsDataVisits;
|
||||
import org.dspace.statistics.content.StatisticsListing;
|
||||
import org.dspace.statistics.content.StatisticsTable;
|
||||
|
||||
import org.dspace.app.webui.components.StatisticsBean;
|
||||
import org.dspace.app.webui.util.JSPManager;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author Kim Shepherd
|
||||
* @version $Revision: 4386 $
|
||||
*/
|
||||
public class DisplayStatisticsServlet extends DSpaceServlet
|
||||
{
|
||||
/** log4j logger */
|
||||
private static Logger log = Logger.getLogger(DisplayStatisticsServlet.class);
|
||||
|
||||
|
||||
protected void doDSGet(Context context, HttpServletRequest request,
|
||||
HttpServletResponse response) throws ServletException, IOException,
|
||||
SQLException, AuthorizeException
|
||||
{
|
||||
|
||||
// is the statistics data publically viewable?
|
||||
boolean privatereport = ConfigurationManager.getBooleanProperty("usage-statistics", "authorization.admin");
|
||||
|
||||
// is the user a member of the Administrator (1) group?
|
||||
boolean admin = Group.isMember(context, 1);
|
||||
|
||||
if (!privatereport || admin)
|
||||
{
|
||||
displayStatistics(context, request, response);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new AuthorizeException();
|
||||
}
|
||||
}
|
||||
|
||||
protected void displayStatistics(Context context, HttpServletRequest request,
|
||||
HttpServletResponse response) throws ServletException, IOException,
|
||||
SQLException, AuthorizeException
|
||||
{
|
||||
|
||||
DSpaceObject dso = null;
|
||||
String handle = request.getParameter("handle");
|
||||
|
||||
if("".equals(handle) || handle == null)
|
||||
{
|
||||
// We didn't get passed a handle parameter.
|
||||
// That means we're looking at /handle/*/*/statistics
|
||||
// with handle injected as attribute from HandleServlet
|
||||
handle = (String) request.getAttribute("handle");
|
||||
|
||||
}
|
||||
|
||||
if(handle != null)
|
||||
{
|
||||
dso = HandleManager.resolveToObject(context, handle);
|
||||
}
|
||||
|
||||
if(dso == null)
|
||||
{
|
||||
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||
JSPManager.showJSP(request, response, "/error/404.jsp");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
boolean isItem = false;
|
||||
|
||||
StatisticsBean statsVisits = new StatisticsBean();
|
||||
StatisticsBean statsMonthlyVisits = new StatisticsBean();
|
||||
StatisticsBean statsFileDownloads = new StatisticsBean();
|
||||
StatisticsBean statsCountryVisits = new StatisticsBean();
|
||||
StatisticsBean statsCityVisits = new StatisticsBean();
|
||||
|
||||
try
|
||||
{
|
||||
StatisticsListing statListing = new StatisticsListing(
|
||||
new StatisticsDataVisits(dso));
|
||||
|
||||
statListing.setTitle("Total Visits");
|
||||
statListing.setId("list1");
|
||||
|
||||
DatasetDSpaceObjectGenerator dsoAxis = new DatasetDSpaceObjectGenerator();
|
||||
dsoAxis.addDsoChild(dso.getType(), 10, false, -1);
|
||||
statListing.addDatasetGenerator(dsoAxis);
|
||||
Dataset dataset = statListing.getDataset(context);
|
||||
|
||||
dataset = statListing.getDataset();
|
||||
|
||||
if (dataset == null)
|
||||
{
|
||||
|
||||
dataset = statListing.getDataset(context);
|
||||
}
|
||||
|
||||
if (dataset != null)
|
||||
{
|
||||
String[][] matrix = dataset.getMatrix();
|
||||
List<String> colLabels = dataset.getColLabels();
|
||||
List<String> rowLabels = dataset.getRowLabels();
|
||||
|
||||
statsVisits.setMatrix(matrix);
|
||||
statsVisits.setColLabels(colLabels);
|
||||
statsVisits.setRowLabels(rowLabels);
|
||||
}
|
||||
|
||||
|
||||
} catch (Exception e)
|
||||
{
|
||||
log.error(
|
||||
"Error occured while creating statistics for dso with ID: "
|
||||
+ dso.getID() + " and type " + dso.getType()
|
||||
+ " and handle: " + dso.getHandle(), e);
|
||||
}
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
StatisticsTable statisticsTable = new StatisticsTable(new StatisticsDataVisits(dso));
|
||||
|
||||
statisticsTable.setTitle("Total Visits Per Month");
|
||||
statisticsTable.setId("tab1");
|
||||
|
||||
DatasetTimeGenerator timeAxis = new DatasetTimeGenerator();
|
||||
timeAxis.setDateInterval("month", "-6", "+1");
|
||||
statisticsTable.addDatasetGenerator(timeAxis);
|
||||
|
||||
DatasetDSpaceObjectGenerator dsoAxis = new DatasetDSpaceObjectGenerator();
|
||||
dsoAxis.addDsoChild(dso.getType(), 10, false, -1);
|
||||
statisticsTable.addDatasetGenerator(dsoAxis);
|
||||
Dataset dataset = statisticsTable.getDataset(context);
|
||||
|
||||
dataset = statisticsTable.getDataset();
|
||||
|
||||
if (dataset == null)
|
||||
{
|
||||
|
||||
dataset = statisticsTable.getDataset(context);
|
||||
}
|
||||
|
||||
if (dataset != null)
|
||||
{
|
||||
String[][] matrix = dataset.getMatrix();
|
||||
List<String> colLabels = dataset.getColLabels();
|
||||
List<String> rowLabels = dataset.getRowLabels();
|
||||
|
||||
statsMonthlyVisits.setMatrix(matrix);
|
||||
statsMonthlyVisits.setColLabels(colLabels);
|
||||
statsMonthlyVisits.setRowLabels(rowLabels);
|
||||
}
|
||||
} catch (Exception e)
|
||||
{
|
||||
log.error(
|
||||
"Error occured while creating statistics for dso with ID: "
|
||||
+ dso.getID() + " and type " + dso.getType()
|
||||
+ " and handle: " + dso.getHandle(), e);
|
||||
}
|
||||
|
||||
if(dso instanceof org.dspace.content.Item)
|
||||
{
|
||||
isItem = true;
|
||||
try
|
||||
{
|
||||
|
||||
StatisticsListing statisticsTable = new StatisticsListing(new StatisticsDataVisits(dso));
|
||||
|
||||
statisticsTable.setTitle("File Downloads");
|
||||
statisticsTable.setId("tab1");
|
||||
|
||||
DatasetDSpaceObjectGenerator dsoAxis = new DatasetDSpaceObjectGenerator();
|
||||
dsoAxis.addDsoChild(Constants.BITSTREAM, 10, false, -1);
|
||||
statisticsTable.addDatasetGenerator(dsoAxis);
|
||||
|
||||
Dataset dataset = statisticsTable.getDataset(context);
|
||||
|
||||
dataset = statisticsTable.getDataset();
|
||||
|
||||
if (dataset == null)
|
||||
{
|
||||
|
||||
dataset = statisticsTable.getDataset(context);
|
||||
}
|
||||
|
||||
if (dataset != null)
|
||||
{
|
||||
String[][] matrix = dataset.getMatrix();
|
||||
List<String> colLabels = dataset.getColLabels();
|
||||
List<String> rowLabels = dataset.getRowLabels();
|
||||
|
||||
statsFileDownloads.setMatrix(matrix);
|
||||
statsFileDownloads.setColLabels(colLabels);
|
||||
statsFileDownloads.setRowLabels(rowLabels);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.error(
|
||||
"Error occured while creating statistics for dso with ID: "
|
||||
+ dso.getID() + " and type " + dso.getType()
|
||||
+ " and handle: " + dso.getHandle(), e);
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
StatisticsListing statisticsTable = new StatisticsListing(new StatisticsDataVisits(dso));
|
||||
|
||||
statisticsTable.setTitle("Top country views");
|
||||
statisticsTable.setId("tab1");
|
||||
|
||||
DatasetTypeGenerator typeAxis = new DatasetTypeGenerator();
|
||||
typeAxis.setType("countryCode");
|
||||
typeAxis.setMax(10);
|
||||
statisticsTable.addDatasetGenerator(typeAxis);
|
||||
|
||||
Dataset dataset = statisticsTable.getDataset(context);
|
||||
|
||||
dataset = statisticsTable.getDataset();
|
||||
|
||||
if (dataset == null)
|
||||
{
|
||||
|
||||
dataset = statisticsTable.getDataset(context);
|
||||
}
|
||||
|
||||
if (dataset != null)
|
||||
{
|
||||
String[][] matrix = dataset.getMatrix();
|
||||
List<String> colLabels = dataset.getColLabels();
|
||||
List<String> rowLabels = dataset.getRowLabels();
|
||||
|
||||
statsCountryVisits.setMatrix(matrix);
|
||||
statsCountryVisits.setColLabels(colLabels);
|
||||
statsCountryVisits.setRowLabels(rowLabels);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.error(
|
||||
"Error occured while creating statistics for dso with ID: "
|
||||
+ dso.getID() + " and type " + dso.getType()
|
||||
+ " and handle: " + dso.getHandle(), e);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
StatisticsListing statisticsTable = new StatisticsListing(new StatisticsDataVisits(dso));
|
||||
|
||||
statisticsTable.setTitle("Top city views");
|
||||
statisticsTable.setId("tab1");
|
||||
|
||||
DatasetTypeGenerator typeAxis = new DatasetTypeGenerator();
|
||||
typeAxis.setType("city");
|
||||
typeAxis.setMax(10);
|
||||
statisticsTable.addDatasetGenerator(typeAxis);
|
||||
|
||||
Dataset dataset = statisticsTable.getDataset(context);
|
||||
|
||||
dataset = statisticsTable.getDataset();
|
||||
|
||||
if (dataset == null)
|
||||
{
|
||||
|
||||
dataset = statisticsTable.getDataset(context);
|
||||
}
|
||||
|
||||
if (dataset != null)
|
||||
{
|
||||
String[][] matrix = dataset.getMatrix();
|
||||
List<String> colLabels = dataset.getColLabels();
|
||||
List<String> rowLabels = dataset.getRowLabels();
|
||||
|
||||
statsCityVisits.setMatrix(matrix);
|
||||
statsCityVisits.setColLabels(colLabels);
|
||||
statsCityVisits.setRowLabels(rowLabels);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.error(
|
||||
"Error occured while creating statistics for dso with ID: "
|
||||
+ dso.getID() + " and type " + dso.getType()
|
||||
+ " and handle: " + dso.getHandle(), e);
|
||||
}
|
||||
|
||||
|
||||
request.setAttribute("statsVisits", statsVisits);
|
||||
request.setAttribute("statsMonthlyVisits", statsMonthlyVisits);
|
||||
request.setAttribute("statsFileDownloads", statsFileDownloads);
|
||||
request.setAttribute("statsCountryVisits",statsCountryVisits);
|
||||
request.setAttribute("statsCityVisits", statsCityVisits);
|
||||
request.setAttribute("isItem", isItem);
|
||||
|
||||
JSPManager.showJSP(request, response, "display-statistics.jsp");
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* 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.webui.servlet;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.dspace.authorize.AuthorizeException;
|
||||
import org.dspace.content.DSpaceObject;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.handle.HandleManager;
|
||||
import org.dspace.usage.UsageEvent;
|
||||
import org.dspace.usage.UsageSearchEvent;
|
||||
import org.dspace.utils.DSpace;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Every time a user clicks on a search result he will be redirected through this servlet
|
||||
* this servlet will retrieve all query information & store this for the search statistics
|
||||
* Once everything has been stored the user will be
|
||||
* redirected to the dso he clicked on (indicated by the redirectUrl parameter)
|
||||
*
|
||||
* @author Kevin Van de Velde (kevin at atmire dot com)
|
||||
* @author Ben Bosman (ben at atmire dot com)
|
||||
* @author Mark Diggory (markd at atmire dot com)
|
||||
*/
|
||||
public class SearchResultLogServlet extends DSpaceServlet{
|
||||
|
||||
@Override
|
||||
protected void doDSPost(Context context, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException, SQLException, AuthorizeException {
|
||||
String redirectUrl = request.getParameter("redirectUrl");
|
||||
String scopeHandle = request.getParameter("scope");
|
||||
DSpaceObject scope = HandleManager.resolveToObject(context, scopeHandle);
|
||||
String resultHandle = StringUtils.substringAfter(redirectUrl, "/handle/");
|
||||
DSpaceObject result = HandleManager.resolveToObject(context, resultHandle);
|
||||
|
||||
//Fire an event to log our search result
|
||||
UsageSearchEvent searchEvent = new UsageSearchEvent(
|
||||
UsageEvent.Action.SEARCH,
|
||||
request,
|
||||
context,
|
||||
result,
|
||||
Arrays.asList(request.getParameterValues("query")), scope);
|
||||
|
||||
if(!StringUtils.isBlank(request.getParameter("rpp"))){
|
||||
searchEvent.setRpp(Integer.parseInt(request.getParameter("rpp")));
|
||||
}
|
||||
if(!StringUtils.isBlank(request.getParameter("sort_by"))){
|
||||
searchEvent.setSortBy(request.getParameter("sort_by"));
|
||||
}
|
||||
if(!StringUtils.isBlank(request.getParameter("order"))){
|
||||
searchEvent.setSortOrder(request.getParameter("order"));
|
||||
}
|
||||
if(!StringUtils.isBlank(request.getParameter("page"))){
|
||||
searchEvent.setPage(Integer.parseInt(request.getParameter("page")));
|
||||
}
|
||||
|
||||
new DSpace().getEventService().fireEvent(
|
||||
searchEvent);
|
||||
|
||||
|
||||
response.sendRedirect(redirectUrl);
|
||||
|
||||
}
|
||||
}
|
@@ -428,6 +428,11 @@
|
||||
<servlet-class>org.dspace.app.webui.servlet.AuthorityChooseServlet</servlet-class>
|
||||
</servlet>
|
||||
|
||||
<servlet>
|
||||
<servlet-name>SearchResultLogServlet</servlet-name>
|
||||
<servlet-class>org.dspace.app.webui.servlet.SearchResultLogServlet</servlet-class>
|
||||
</servlet>
|
||||
|
||||
<!-- shibbolized dspace -->
|
||||
<servlet>
|
||||
<servlet-name>shibboleth-login</servlet-name>
|
||||
@@ -722,6 +727,11 @@
|
||||
<url-pattern>/json/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>SearchResultLogServlet</servlet-name>
|
||||
<url-pattern>/dso-display</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<!-- Icon MIME type -->
|
||||
<mime-mapping>
|
||||
<extension>ico</extension>
|
||||
|
@@ -92,6 +92,10 @@
|
||||
|
||||
<dspace:layout titlekey="jsp.search.results.title">
|
||||
|
||||
<script type="text/javascript" src="<%= request.getContextPath() %>/static/js/jquery/jquery-1.6.2.min.js"> </script>
|
||||
<script type="text/javascript" src="<%= request.getContextPath() %>/static/js/search-results.js"> </script>
|
||||
|
||||
|
||||
<%-- <h1>Search Results</h1> --%>
|
||||
|
||||
<h1><fmt:message key="jsp.search.results.title"/></h1>
|
||||
@@ -293,6 +297,10 @@ else
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<%
|
||||
if(0 < communities.length || 0 < collections.length || 0 < items.length){
|
||||
%>
|
||||
<div id="search-results-division">
|
||||
<% if (communities.length > 0 ) { %>
|
||||
<%-- <h3>Community Hits:</h3> --%>
|
||||
<h3><fmt:message key="jsp.search.results.comhits"/></h3>
|
||||
@@ -312,6 +320,10 @@ else
|
||||
<h3><fmt:message key="jsp.search.results.itemhits"/></h3>
|
||||
<dspace:itemlist items="<%= items %>" sortOption="<%= so %>" authorLimit="<%= qResults.getEtAl() %>" />
|
||||
<% } %>
|
||||
</div>
|
||||
<%
|
||||
}
|
||||
%>
|
||||
|
||||
<p align="center">
|
||||
|
||||
@@ -383,5 +395,15 @@ if (pageTotal > pageCurrent)
|
||||
|
||||
</p>
|
||||
|
||||
<form id="dso-display" action="<%=request.getContextPath()%>/dso-display" method="post">
|
||||
<input type="hidden" name="query" value="<%=query%>"/>
|
||||
<input type="hidden" name="rpp" value="<%=rpp%>"/>
|
||||
<input type="hidden" name="page" value="<%=pageCurrent%>"/>
|
||||
<input type="hidden" name="sort_by" value="<%=(so != null ? so.getNumber() : 0)%>"/>
|
||||
<input type="hidden" name="order" value="<%=order%>"/>
|
||||
<input type="hidden" name="scope" value="<%=collection != null ? collection.getHandle() : (community != null ? community.getHandle() : "")%>"/>
|
||||
<input type="hidden" name="redirectUrl" value=""/>
|
||||
</form>
|
||||
|
||||
</dspace:layout>
|
||||
|
||||
|
25
dspace-jspui/dspace-jspui-webapp/src/main/webapp/static/js/jquery/jquery-1.6.2.min.js
vendored
Normal file
25
dspace-jspui/dspace-jspui-webapp/src/main/webapp/static/js/jquery/jquery-1.6.2.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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/
|
||||
*/
|
||||
$.noConflict();
|
||||
(function ($) {
|
||||
|
||||
/**
|
||||
* Function ensures that all the links clicked in our results pass through the internal logging mechanism
|
||||
*/
|
||||
$(document).ready(function() {
|
||||
|
||||
|
||||
//Retrieve all links with handles attached (comm/coll/item links)
|
||||
var urls = $('div#search-results-division').find('a');
|
||||
|
||||
urls.click(function(){
|
||||
var $this = $(this);
|
||||
//Instead of redirecting us to the page, first send us to the statistics logger
|
||||
//By doing this we ensure that we register the query to the result
|
||||
var form = $('form#dso-display');
|
||||
form.find('input[name="redirectUrl"]').val($this.attr('href'));
|
||||
form.submit();
|
||||
return false;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
})(jQuery);
|
@@ -145,14 +145,19 @@
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<artifactId>jcl-over-slf4j</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>jcl-over-slf4j</artifactId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-configuration</groupId>
|
||||
<artifactId>commons-configuration</artifactId>
|
||||
<version>1.8</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.dspace.dependencies</groupId>
|
||||
<artifactId>dspace-geoip</artifactId>
|
||||
|
@@ -18,13 +18,14 @@ import java.util.Map;
|
||||
|
||||
import com.Ostermiller.util.ExcelCSVPrinter;
|
||||
import org.apache.commons.lang.ArrayUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author kevinvandevelde at atmire.com
|
||||
* Date: 21-jan-2009
|
||||
* Time: 13:44:48
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class Dataset {
|
||||
|
||||
@@ -41,14 +42,14 @@ public class Dataset {
|
||||
/* The attributes for the rows */
|
||||
private List<Map<String, String>> rowLabelsAttrs;
|
||||
/* The data in a matrix */
|
||||
private float[][]matrix;
|
||||
private String[][]matrix;
|
||||
/* The format in which we format our floats */
|
||||
private String format = "0";
|
||||
|
||||
|
||||
|
||||
public Dataset(int rows, int cols){
|
||||
matrix = new float[rows][cols];
|
||||
matrix = new String[rows][cols];
|
||||
nbRows = rows;
|
||||
nbCols = cols;
|
||||
initColumnLabels(cols);
|
||||
@@ -56,7 +57,7 @@ public class Dataset {
|
||||
}
|
||||
|
||||
public Dataset(float[][] matrix){
|
||||
this.matrix = (float[][]) ArrayUtils.clone(matrix);
|
||||
this.matrix = (String[][]) ArrayUtils.clone(matrix);
|
||||
nbRows = matrix.length;
|
||||
if(0 < matrix.length && 0 < matrix[0].length)
|
||||
{
|
||||
@@ -146,10 +147,6 @@ public class Dataset {
|
||||
return rowLabels;
|
||||
}
|
||||
|
||||
public float[][] getMatrix() {
|
||||
return (float[][]) ArrayUtils.clone(matrix);
|
||||
}
|
||||
|
||||
public int getNbRows() {
|
||||
return nbRows;
|
||||
}
|
||||
@@ -166,40 +163,32 @@ public class Dataset {
|
||||
this.format = format;
|
||||
}
|
||||
|
||||
public String[][] getMatrixFormatted(){
|
||||
DecimalFormat decimalFormat = new DecimalFormat(format);
|
||||
public String[][] getMatrix(){
|
||||
if (matrix.length == 0) {
|
||||
return new String[0][0];
|
||||
} else {
|
||||
String[][] strMatrix = new String[matrix.length][matrix[0].length];
|
||||
for (int i = 0; i < matrix.length; i++) {
|
||||
for (int j = 0; j < matrix[i].length; j++) {
|
||||
strMatrix[i][j] = decimalFormat.format(matrix[i][j]);
|
||||
}
|
||||
}
|
||||
return strMatrix;
|
||||
return matrix;
|
||||
}
|
||||
}
|
||||
|
||||
public void addValueToMatrix(int row, int coll, float value) {
|
||||
matrix[row][coll] = value;
|
||||
DecimalFormat decimalFormat = new DecimalFormat(format);
|
||||
matrix[row][coll] = decimalFormat.format(value);
|
||||
}
|
||||
|
||||
|
||||
public void addValueToMatrix(int row, int coll, String value) throws ParseException {
|
||||
DecimalFormat decimalFormat = new DecimalFormat(format);
|
||||
Number number = decimalFormat.parse(value);
|
||||
matrix[row][coll] = number.floatValue();
|
||||
matrix[row][coll] = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns false if this dataset only contains zero's.
|
||||
* Returns false if this dataset only contains zero's.
|
||||
*/
|
||||
public boolean containsNonZeroValues(){
|
||||
if (matrix != null) {
|
||||
for (float[] vector : matrix) {
|
||||
for (float v : vector) {
|
||||
if (v != 0)
|
||||
for (String[] vector : matrix) {
|
||||
for (String v : vector) {
|
||||
if (StringUtils.isBlank(v) || v.equals("0"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -215,7 +204,7 @@ public class Dataset {
|
||||
//Lets make sure we at least have something to flip
|
||||
if(0 < matrix.length && 0 < matrix[0].length){
|
||||
//Flip the data first
|
||||
float[][] newMatrix = new float[matrix[0].length][matrix.length];
|
||||
String[][] newMatrix = new String[matrix[0].length][matrix.length];
|
||||
for (int i = 0; i < matrix.length; i++) {
|
||||
for (int j = 0; j < matrix[i].length; j++) {
|
||||
newMatrix[j][i] = matrix[i][j];
|
||||
@@ -258,7 +247,7 @@ public class Dataset {
|
||||
ecsvp.writeln();
|
||||
List<String> rowLabels = getRowLabels();
|
||||
|
||||
String[][] matrix = getMatrixFormatted();
|
||||
String[][] matrix = getMatrix();
|
||||
for (int i = 0; i < rowLabels.size(); i++) {
|
||||
String rowLabel = rowLabels.get(i);
|
||||
ecsvp.write(rowLabel);
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -12,6 +12,9 @@ import org.dspace.eperson.EPerson;
|
||||
import org.dspace.services.model.Event;
|
||||
import org.dspace.usage.AbstractUsageEventListener;
|
||||
import org.dspace.usage.UsageEvent;
|
||||
import org.dspace.usage.UsageSearchEvent;
|
||||
import org.dspace.usage.UsageWorkflowEvent;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* Simple SolrLoggerUsageEvent facade to separate Solr specific
|
||||
@@ -29,12 +32,27 @@ public class SolrLoggerUsageEventListener extends AbstractUsageEventListener {
|
||||
if(event instanceof UsageEvent)
|
||||
{
|
||||
try{
|
||||
|
||||
UsageEvent ue = (UsageEvent)event;
|
||||
|
||||
EPerson currentUser = ue.getContext() == null ? null : ue.getContext().getCurrentUser();
|
||||
|
||||
SolrLogger.post(ue.getObject(), ue.getRequest(), currentUser);
|
||||
if(UsageEvent.Action.VIEW == ue.getAction()){
|
||||
SolrLogger.postView(ue.getObject(), ue.getRequest(), currentUser);
|
||||
}else
|
||||
if(UsageEvent.Action.SEARCH == ue.getAction()){
|
||||
UsageSearchEvent usageSearchEvent = (UsageSearchEvent) ue;
|
||||
//Only log if the user has already filled in a query !
|
||||
if(!CollectionUtils.isEmpty(((UsageSearchEvent) ue).getQueries())){
|
||||
SolrLogger.postSearch(ue.getObject(), ue.getRequest(), currentUser,
|
||||
usageSearchEvent.getQueries(), usageSearchEvent.getRpp(), usageSearchEvent.getSortBy(),
|
||||
usageSearchEvent.getSortOrder(), usageSearchEvent.getPage(), usageSearchEvent.getScope());
|
||||
}
|
||||
}else
|
||||
if(UsageEvent.Action.WORKFLOW == ue.getAction()){
|
||||
UsageWorkflowEvent usageWorkflowEvent = (UsageWorkflowEvent) ue;
|
||||
|
||||
SolrLogger.postWorkflow(usageWorkflowEvent);
|
||||
}
|
||||
|
||||
}
|
||||
catch(Exception e)
|
||||
|
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* 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.statistics.content;
|
||||
|
||||
/**
|
||||
* @author Kevin Van de Velde (kevin at atmire dot com)
|
||||
* @author Ben Bosman (ben at atmire dot com)
|
||||
* @author Mark Diggory (markd at atmire dot com)
|
||||
*/
|
||||
public class DatasetSearchGenerator extends DatasetTypeGenerator {
|
||||
|
||||
public static enum Mode {
|
||||
SEARCH_OVERVIEW ("search_overview"),
|
||||
SEARCH_OVERVIEW_TOTAL ("search_overview_total");
|
||||
|
||||
private final String text;
|
||||
|
||||
Mode(String text) {
|
||||
this.text = text;
|
||||
}
|
||||
public String text() { return text; }
|
||||
}
|
||||
|
||||
private Mode mode;
|
||||
private boolean percentage = false;
|
||||
private boolean retrievePageViews;
|
||||
|
||||
public boolean isRetrievePageViews() {
|
||||
return retrievePageViews;
|
||||
}
|
||||
|
||||
public void setRetrievePageViews(boolean retrievePageViews) {
|
||||
this.retrievePageViews = retrievePageViews;
|
||||
}
|
||||
|
||||
public void setPercentage(boolean percentage){
|
||||
this.percentage = percentage;
|
||||
}
|
||||
|
||||
public boolean isPercentage() {
|
||||
return percentage;
|
||||
}
|
||||
|
||||
public Mode getMode() {
|
||||
return mode;
|
||||
}
|
||||
|
||||
public void setMode(Mode mode) {
|
||||
this.mode = mode;
|
||||
}
|
||||
}
|
@@ -0,0 +1,238 @@
|
||||
/**
|
||||
* 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.statistics.content;
|
||||
|
||||
import org.apache.commons.lang.ArrayUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.solr.client.solrj.SolrServerException;
|
||||
import org.apache.solr.client.solrj.util.ClientUtils;
|
||||
import org.dspace.content.DSpaceObject;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.statistics.Dataset;
|
||||
import org.dspace.statistics.ObjectCount;
|
||||
import org.dspace.statistics.SolrLogger;
|
||||
import org.dspace.statistics.content.filter.StatisticsFilter;
|
||||
import org.dspace.utils.DSpace;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A statistics data implementation that will query the statistics backend for search information
|
||||
*
|
||||
* @author Kevin Van de Velde (kevin at atmire dot com)
|
||||
* @author Ben Bosman (ben at atmire dot com)
|
||||
* @author Mark Diggory (markd at atmire dot com)
|
||||
*/
|
||||
public class StatisticsDataSearches extends StatisticsData {
|
||||
|
||||
private static final DecimalFormat pageViewFormat = new DecimalFormat("0.00");
|
||||
private static final DecimalFormat percentageFormat = new DecimalFormat("0.00%");
|
||||
/** Current DSpaceObject for which to generate the statistics. */
|
||||
private DSpaceObject currentDso;
|
||||
|
||||
public StatisticsDataSearches(DSpaceObject dso) {
|
||||
super();
|
||||
this.currentDso = dso;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public Dataset createDataset(Context context) throws SQLException, SolrServerException, IOException, ParseException {
|
||||
// Check if we already have one.
|
||||
// If we do then give it back.
|
||||
if(getDataset() != null)
|
||||
{
|
||||
return getDataset();
|
||||
}
|
||||
|
||||
List<StatisticsFilter> filters = getFilters();
|
||||
List<String> defaultFilters = new ArrayList<String>();
|
||||
for (StatisticsFilter statisticsFilter : filters) {
|
||||
defaultFilters.add(statisticsFilter.toQuery());
|
||||
}
|
||||
|
||||
String defaultFilterQuery = StringUtils.join(defaultFilters.iterator(), " AND ");
|
||||
|
||||
String query = getQuery();
|
||||
|
||||
Dataset dataset = new Dataset(0,0);
|
||||
List<DatasetGenerator> datasetGenerators = getDatasetGenerators();
|
||||
if(0 < datasetGenerators.size()){
|
||||
//At the moment we can only have one dataset generator
|
||||
DatasetGenerator datasetGenerator = datasetGenerators.get(0);
|
||||
if(datasetGenerator instanceof DatasetSearchGenerator){
|
||||
DatasetSearchGenerator typeGenerator = (DatasetSearchGenerator) datasetGenerator;
|
||||
|
||||
if(typeGenerator.getMode() == DatasetSearchGenerator.Mode.SEARCH_OVERVIEW){
|
||||
StringBuilder fqBuffer = new StringBuilder(defaultFilterQuery);
|
||||
if(0 < fqBuffer.length())
|
||||
{
|
||||
fqBuffer.append(" AND ");
|
||||
}
|
||||
fqBuffer.append(getSearchFilterQuery());
|
||||
|
||||
ObjectCount[] topCounts = SolrLogger.queryFacetField(query, fqBuffer.toString(), typeGenerator.getType(), typeGenerator.getMax(), (typeGenerator.isPercentage() || typeGenerator.isIncludeTotal()), null);
|
||||
long totalCount = -1;
|
||||
if(typeGenerator.isPercentage() && 0 < topCounts.length){
|
||||
//Retrieve the total required to calculate the percentage
|
||||
totalCount = topCounts[topCounts.length - 1].getCount();
|
||||
//Remove the total count from view !
|
||||
topCounts = (ObjectCount[]) ArrayUtils.subarray(topCounts, 0, topCounts.length - 1);
|
||||
}
|
||||
|
||||
int nrColumns = 2;
|
||||
if(typeGenerator.isPercentage()){
|
||||
nrColumns++;
|
||||
}
|
||||
if(typeGenerator.isRetrievePageViews()){
|
||||
nrColumns++;
|
||||
}
|
||||
|
||||
dataset = new Dataset(topCounts.length, nrColumns);
|
||||
dataset.setColLabel(0, "search-terms");
|
||||
dataset.setColLabel(1, "searches");
|
||||
if(typeGenerator.isPercentage()){
|
||||
dataset.setColLabel(2, "percent-total");
|
||||
}
|
||||
if(typeGenerator.isRetrievePageViews()){
|
||||
dataset.setColLabel(3, "views-search");
|
||||
}
|
||||
for (int i = 0; i < topCounts.length; i++) {
|
||||
ObjectCount queryCount = topCounts[i];
|
||||
|
||||
dataset.setRowLabel(i, String.valueOf(i + 1));
|
||||
String displayedValue = queryCount.getValue();
|
||||
if(new DSpace().getConfigurationService().getPropertyAsType("usage-statistics.search.statistics.unescape.queries", Boolean.TRUE)){
|
||||
displayedValue = displayedValue.replace("\\", "");
|
||||
}
|
||||
dataset.addValueToMatrix(i, 0, displayedValue);
|
||||
dataset.addValueToMatrix(i, 1, queryCount.getCount());
|
||||
if(typeGenerator.isPercentage()){
|
||||
//Calculate our percentage from the total !
|
||||
dataset.addValueToMatrix(i, 2, percentageFormat.format(((float) queryCount.getCount() / totalCount)));
|
||||
}
|
||||
if(typeGenerator.isRetrievePageViews()){
|
||||
String queryString = ClientUtils.escapeQueryChars(queryCount.getValue());
|
||||
if(queryString.equals("")){
|
||||
queryString = "\"\"";
|
||||
}
|
||||
|
||||
ObjectCount totalPageViews = getTotalPageViews("query:" + queryString, defaultFilterQuery);
|
||||
dataset.addValueToMatrix(i, 3, pageViewFormat.format((float) totalPageViews.getCount() / queryCount.getCount()));
|
||||
}
|
||||
}
|
||||
}else
|
||||
if(typeGenerator.getMode() == DatasetSearchGenerator.Mode.SEARCH_OVERVIEW_TOTAL){
|
||||
//Retrieve the total counts !
|
||||
ObjectCount totalCount = SolrLogger.queryTotal(query, getSearchFilterQuery());
|
||||
|
||||
//Retrieve the filtered count by using the default filter query
|
||||
StringBuilder fqBuffer = new StringBuilder(defaultFilterQuery);
|
||||
if(0 < fqBuffer.length())
|
||||
{
|
||||
fqBuffer.append(" AND ");
|
||||
}
|
||||
fqBuffer.append(getSearchFilterQuery());
|
||||
|
||||
ObjectCount totalFiltered = SolrLogger.queryTotal(query, fqBuffer.toString());
|
||||
|
||||
|
||||
fqBuffer = new StringBuilder(defaultFilterQuery);
|
||||
if(0 < fqBuffer.length())
|
||||
{
|
||||
fqBuffer.append(" AND ");
|
||||
}
|
||||
fqBuffer.append("statistics_type:").append(SolrLogger.StatisticsType.SEARCH_RESULT.text());
|
||||
|
||||
ObjectCount totalPageViews = getTotalPageViews(query, defaultFilterQuery);
|
||||
|
||||
dataset = new Dataset(1, 3);
|
||||
dataset.setRowLabel(0, "");
|
||||
|
||||
|
||||
dataset.setColLabel(0, "searches");
|
||||
dataset.addValueToMatrix(0, 0, totalFiltered.getCount());
|
||||
dataset.setColLabel(1, "percent-total");
|
||||
//Ensure that we do NOT divide by 0
|
||||
float percentTotal;
|
||||
if(totalCount.getCount() == 0){
|
||||
percentTotal = 0;
|
||||
}else{
|
||||
percentTotal = (float) totalFiltered.getCount() / totalCount.getCount();
|
||||
}
|
||||
|
||||
|
||||
dataset.addValueToMatrix(0, 1, percentageFormat.format(percentTotal));
|
||||
dataset.setColLabel(2, "views-search");
|
||||
//Ensure that we do NOT divide by 0
|
||||
float pageViews;
|
||||
if(totalFiltered.getCount() == 0){
|
||||
pageViews = 0;
|
||||
}else{
|
||||
pageViews = (float) totalPageViews.getCount() / totalFiltered.getCount();
|
||||
}
|
||||
|
||||
dataset.addValueToMatrix(0, 2, pageViewFormat.format(pageViews));
|
||||
}
|
||||
}else{
|
||||
throw new IllegalArgumentException("Data generator with class" + datasetGenerator.getClass().getName() + " is not supported by the statistics search engine !");
|
||||
}
|
||||
}
|
||||
|
||||
return dataset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the query to be used in solr
|
||||
* in case of a dso a scopeDso query will be returned otherwise the default *:* query will be used
|
||||
* @return the query as a string
|
||||
*/
|
||||
protected String getQuery() {
|
||||
String query;
|
||||
if(currentDso != null){
|
||||
query = "scopeType: " + currentDso.getType() + " AND scopeId: " + currentDso.getID();
|
||||
|
||||
}else{
|
||||
query = "*:*";
|
||||
}
|
||||
return query;
|
||||
}
|
||||
|
||||
private ObjectCount getTotalPageViews(String query, String defaultFilterQuery) throws SolrServerException {
|
||||
StringBuilder fqBuffer;
|
||||
fqBuffer = new StringBuilder(defaultFilterQuery);
|
||||
if(0 < fqBuffer.length())
|
||||
{
|
||||
fqBuffer.append(" AND ");
|
||||
}
|
||||
fqBuffer.append("statistics_type:").append(SolrLogger.StatisticsType.SEARCH_RESULT.text());
|
||||
|
||||
|
||||
//Retrieve the number of page views by this query !
|
||||
return SolrLogger.queryTotal(query, fqBuffer.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a filter query that only allows new searches to pass
|
||||
* new searches are searches that haven't been paged through
|
||||
* @return a solr filterquery
|
||||
*/
|
||||
private String getSearchFilterQuery() {
|
||||
StringBuilder fqBuffer = new StringBuilder();
|
||||
fqBuffer.append("statistics_type:").append(SolrLogger.StatisticsType.SEARCH.text());
|
||||
//Also append a filter query to ensure that paging is left out !
|
||||
fqBuffer.append(" AND -page:[* TO *]");
|
||||
return fqBuffer.toString();
|
||||
}
|
||||
}
|
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
package org.dspace.statistics.content;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.dspace.content.*;
|
||||
import org.dspace.statistics.Dataset;
|
||||
import org.dspace.statistics.ObjectCount;
|
||||
@@ -148,12 +149,8 @@ public class StatisticsDataVisits extends StatisticsData
|
||||
}
|
||||
|
||||
// Determine our filterQuery
|
||||
String filterQuery = null;
|
||||
String filterQuery = "";
|
||||
for (int i = 0; i < getFilters().size(); i++) {
|
||||
if(filterQuery == null)
|
||||
{
|
||||
filterQuery = "";
|
||||
}
|
||||
StatisticsFilter filter = getFilters().get(i);
|
||||
|
||||
filterQuery += "(" + filter.toQuery() + ")";
|
||||
@@ -162,6 +159,14 @@ public class StatisticsDataVisits extends StatisticsData
|
||||
filterQuery += " AND ";
|
||||
}
|
||||
}
|
||||
if(StringUtils.isNotBlank(filterQuery)){
|
||||
filterQuery += " AND ";
|
||||
}
|
||||
//Only use the view type and make sure old data (where no view type is present) is also supported
|
||||
//Solr doesn't explicitly apply boolean logic, so this query cannot be simplified to an OR query
|
||||
filterQuery += "-(statistics_type:[* TO *] AND -statistics_type:" + SolrLogger.StatisticsType.VIEW.text() + ")";
|
||||
|
||||
|
||||
// System.out.println("FILTERQUERY: " + filterQuery);
|
||||
|
||||
// We determine our values on the queries resolved above
|
||||
|
@@ -0,0 +1,202 @@
|
||||
/**
|
||||
* 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.statistics.content;
|
||||
|
||||
import org.apache.commons.configuration.ConfigurationException;
|
||||
import org.apache.commons.configuration.PropertiesConfiguration;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.apache.solr.client.solrj.SolrServerException;
|
||||
import org.apache.solr.client.solrj.response.QueryResponse;
|
||||
import org.apache.solr.common.SolrDocument;
|
||||
import org.dspace.content.DCDate;
|
||||
import org.dspace.content.DSpaceObject;
|
||||
import org.dspace.core.Constants;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.services.ConfigurationService;
|
||||
import org.dspace.statistics.Dataset;
|
||||
import org.dspace.statistics.ObjectCount;
|
||||
import org.dspace.statistics.SolrLogger;
|
||||
import org.dspace.statistics.content.filter.StatisticsFilter;
|
||||
import org.dspace.utils.DSpace;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.text.ParseException;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* A workflow data implementation that will query the statistics backend for workflow information
|
||||
*
|
||||
* @author Kevin Van de Velde (kevin at atmire dot com)
|
||||
* @author Ben Bosman (ben at atmire dot com)
|
||||
* @author Mark Diggory (markd at atmire dot com)
|
||||
*/
|
||||
public class StatisticsDataWorkflow extends StatisticsData {
|
||||
|
||||
private static final Logger log = Logger.getLogger(StatisticsDataWorkflow.class);
|
||||
|
||||
/** Current DSpaceObject for which to generate the statistics. */
|
||||
private DSpaceObject currentDso;
|
||||
/** Variable used to indicate of how many months an average is required (-1 is inactive) **/
|
||||
private int averageMonths = -1;
|
||||
|
||||
public StatisticsDataWorkflow(DSpaceObject dso, int averageMonths) {
|
||||
super();
|
||||
this.currentDso = dso;
|
||||
this.averageMonths = averageMonths;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Dataset createDataset(Context context) throws SQLException, SolrServerException, IOException, ParseException {
|
||||
// Check if we already have one.
|
||||
// If we do then give it back.
|
||||
if(getDataset() != null)
|
||||
{
|
||||
return getDataset();
|
||||
}
|
||||
|
||||
List<StatisticsFilter> filters = getFilters();
|
||||
List<String> defaultFilters = new ArrayList<String>();
|
||||
for (StatisticsFilter statisticsFilter : filters) {
|
||||
defaultFilters.add(statisticsFilter.toQuery());
|
||||
}
|
||||
|
||||
String defaultFilterQuery = StringUtils.join(defaultFilters.iterator(), " AND ");
|
||||
|
||||
String query = getQuery();
|
||||
|
||||
Dataset dataset = new Dataset(0,0);
|
||||
List<DatasetGenerator> datasetGenerators = getDatasetGenerators();
|
||||
if(0 < datasetGenerators.size()){
|
||||
//At the moment we can only have one dataset generator
|
||||
DatasetGenerator datasetGenerator = datasetGenerators.get(0);
|
||||
if(datasetGenerator instanceof DatasetTypeGenerator){
|
||||
DatasetTypeGenerator typeGenerator = (DatasetTypeGenerator) datasetGenerator;
|
||||
ObjectCount[] topCounts = SolrLogger.queryFacetField(query, defaultFilterQuery, typeGenerator.getType(), typeGenerator.getMax(), typeGenerator.isIncludeTotal(), null);
|
||||
|
||||
//Retrieve our total field counts
|
||||
Map<String, Long> totalFieldCounts = new HashMap<String, Long>();
|
||||
if(averageMonths != -1){
|
||||
totalFieldCounts = getTotalFacetCounts(typeGenerator);
|
||||
}
|
||||
long monthDifference = 1;
|
||||
if(getOldestWorkflowItemDate() != null){
|
||||
monthDifference = getMonthsDifference(new Date(), getOldestWorkflowItemDate());
|
||||
}
|
||||
|
||||
dataset = new Dataset(topCounts.length, (averageMonths != -1 ? 3 : 2));
|
||||
dataset.setColLabel(0, "step");
|
||||
dataset.setColLabel(1, "performed");
|
||||
if(averageMonths != -1){
|
||||
dataset.setColLabel(2, "average");
|
||||
}
|
||||
for (int i = 0; i < topCounts.length; i++) {
|
||||
ObjectCount topCount = topCounts[i];
|
||||
dataset.setRowLabel(i, String.valueOf(i + 1));
|
||||
dataset.addValueToMatrix(i, 0, topCount.getValue());
|
||||
dataset.addValueToMatrix(i, 1, topCount.getCount());
|
||||
if(averageMonths != -1){
|
||||
//Calculate the average of one month
|
||||
long monthlyAverage = 0;
|
||||
if(totalFieldCounts.get(topCount.getValue()) != null){
|
||||
monthlyAverage = totalFieldCounts.get(topCount.getValue()) / monthDifference;
|
||||
}
|
||||
//We multiple our average for one month by the number of
|
||||
dataset.addValueToMatrix(i, 2, (monthlyAverage * averageMonths));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dataset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the query to be used in solr
|
||||
* in case of a dso a scopeDso query will be returned otherwise the default *:* query will be used
|
||||
* @return the query as a string
|
||||
*/
|
||||
protected String getQuery() {
|
||||
String query = "statistics_type:" + SolrLogger.StatisticsType.WORKFLOW.text();
|
||||
query += " AND NOT(previousWorkflowStep: SUBMIT)";
|
||||
if(currentDso != null){
|
||||
if(currentDso.getType() == Constants.COMMUNITY){
|
||||
query += " AND owningComm:";
|
||||
|
||||
}else
|
||||
if(currentDso.getType() == Constants.COLLECTION){
|
||||
query += " AND owningColl:";
|
||||
}
|
||||
query += currentDso.getID();
|
||||
}
|
||||
return query;
|
||||
}
|
||||
|
||||
private int getMonthsDifference(Date date1, Date date2) {
|
||||
int m1 = date1.getYear() * 12 + date1.getMonth();
|
||||
int m2 = date2.getYear() * 12 + date2.getMonth();
|
||||
return m2 - m1 + 1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the total counts for the facets (total count is same query but none of the filter queries
|
||||
* @param typeGenerator the type generator
|
||||
* @return as a key the
|
||||
* @throws org.apache.solr.client.solrj.SolrServerException
|
||||
*/
|
||||
protected Map<String, Long> getTotalFacetCounts(DatasetTypeGenerator typeGenerator) throws SolrServerException {
|
||||
ObjectCount[] objectCounts = SolrLogger.queryFacetField(getQuery(), null, typeGenerator.getType(), -1, false, null);
|
||||
Map<String, Long> result = new HashMap<String, Long>();
|
||||
for (ObjectCount objectCount : objectCounts) {
|
||||
result.put(objectCount.getValue(), objectCount.getCount());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected Date getOldestWorkflowItemDate() throws SolrServerException {
|
||||
ConfigurationService configurationService = new DSpace().getConfigurationService();
|
||||
String workflowStartDate = configurationService.getProperty("usage-statistics.workflow-start-date");
|
||||
if(workflowStartDate == null){
|
||||
//Query our solr for it !
|
||||
QueryResponse oldestRecord = SolrLogger.query(getQuery(), null, null, 1, 0, null, null, null, null, "time", true);
|
||||
if(0 < oldestRecord.getResults().getNumFound()){
|
||||
SolrDocument solrDocument = oldestRecord.getResults().get(0);
|
||||
Date oldestDate = (Date) solrDocument.getFieldValue("time");
|
||||
//Store the date, we only need to retrieve this once !
|
||||
try {
|
||||
//Also store it in the solr-statics configuration file, the reason for this being that the sort query
|
||||
//can be very time consuming & we do not want this delay each time we want to see workflow statistics
|
||||
String solrConfigDir = configurationService.getProperty("dspace.dir") + File.separator + "config"
|
||||
+ File.separator + "modules" + File.separator + "usage-statistics.cfg";
|
||||
PropertiesConfiguration config = new PropertiesConfiguration(solrConfigDir);
|
||||
config.setProperty("workflow-start-date", new DCDate(oldestDate));
|
||||
config.save();
|
||||
} catch (ConfigurationException e) {
|
||||
log.error("Error while storing workflow start date", e);
|
||||
}
|
||||
//ALso store it in our local config !
|
||||
configurationService.setProperty("usage-statistics.workflow-start-date", new DCDate(oldestDate).toString());
|
||||
|
||||
//Write to file
|
||||
return oldestDate;
|
||||
}else{
|
||||
return null;
|
||||
}
|
||||
|
||||
}else{
|
||||
return new DCDate(workflowStartDate).toDate();
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,155 +1,160 @@
|
||||
/**
|
||||
* 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.statistics.util;
|
||||
|
||||
import org.apache.commons.cli.*;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.apache.tools.ant.taskdefs.Get;
|
||||
import org.dspace.core.ConfigurationManager;
|
||||
import org.dspace.statistics.SolrLogger;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* Class to load intermediate statistics files into solr
|
||||
*
|
||||
* @author Stuart Lewis
|
||||
*/
|
||||
public class StatisticsClient
|
||||
{
|
||||
private static final Logger log = Logger.getLogger(StatisticsClient.class);
|
||||
|
||||
/**
|
||||
* Print the help message
|
||||
*
|
||||
* @param options The command line options the user gave
|
||||
* @param exitCode the system exit code to use
|
||||
*/
|
||||
private static void printHelp(Options options, int exitCode)
|
||||
{
|
||||
// print the help message
|
||||
HelpFormatter myhelp = new HelpFormatter();
|
||||
myhelp.printHelp("StatisticsClient\n", options);
|
||||
System.exit(exitCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Main method to run the statistics importer.
|
||||
*
|
||||
* @param args The command line arguments
|
||||
* @throws Exception If something goes wrong
|
||||
*/
|
||||
public static void main(String[] args) throws Exception
|
||||
{
|
||||
CommandLineParser parser = new PosixParser();
|
||||
|
||||
Options options = new Options();
|
||||
|
||||
options.addOption("u", "update-spider-files", false,
|
||||
"Update Spider IP Files from internet into " +
|
||||
ConfigurationManager.getProperty("dspace.dir") + "/config/spiders");
|
||||
|
||||
options.addOption("m", "mark-spiders", false, "Update isBot Flag in Solr");
|
||||
options.addOption("f", "delete-spiders-by-flag", false, "Delete Spiders in Solr By isBot Flag");
|
||||
options.addOption("i", "delete-spiders-by-ip", false, "Delete Spiders in Solr By IP Address");
|
||||
options.addOption("o", "optimize", false, "Run maintenance on the SOLR index");
|
||||
options.addOption("b", "reindex-bitstreams", false, "Reindex the bitstreams to ensure we have the bundle name");
|
||||
options.addOption("r", "remove-deleted-bitstreams", false, "While indexing the bundle names remove the statistics about deleted bitstreams");
|
||||
options.addOption("h", "help", false, "help");
|
||||
|
||||
CommandLine line = parser.parse(options, args);
|
||||
|
||||
// Did the user ask to see the help?
|
||||
if (line.hasOption('h'))
|
||||
{
|
||||
printHelp(options, 0);
|
||||
}
|
||||
|
||||
if(line.hasOption("u"))
|
||||
{
|
||||
StatisticsClient.updateSpiderFiles();
|
||||
}
|
||||
else if (line.hasOption('m'))
|
||||
{
|
||||
SolrLogger.markRobotsByIP();
|
||||
}
|
||||
else if(line.hasOption('f'))
|
||||
{
|
||||
SolrLogger.deleteRobotsByIsBotFlag();
|
||||
}
|
||||
else if(line.hasOption('i'))
|
||||
{
|
||||
SolrLogger.deleteRobotsByIP();
|
||||
}
|
||||
else if(line.hasOption('o'))
|
||||
{
|
||||
SolrLogger.optimizeSOLR();
|
||||
}
|
||||
else if(line.hasOption('b'))
|
||||
{
|
||||
SolrLogger.reindexBitstreamHits(line.hasOption('r'));
|
||||
}
|
||||
else
|
||||
{
|
||||
printHelp(options, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to update Spiders in config directory.
|
||||
*/
|
||||
private static void updateSpiderFiles()
|
||||
{
|
||||
try
|
||||
{
|
||||
System.out.println("Downloading latest spider IP addresses:");
|
||||
|
||||
// Get the list URLs to download from
|
||||
String urls = ConfigurationManager.getProperty("solr-statistics", "spiderips.urls");
|
||||
if ((urls == null) || ("".equals(urls)))
|
||||
{
|
||||
System.err.println(" - Missing setting from dspace.cfg: solr.spiderips.urls");
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
// Get the location of spiders directory
|
||||
File spiders = new File(ConfigurationManager.getProperty("dspace.dir"),"config/spiders");
|
||||
|
||||
if (!spiders.exists() && !spiders.mkdirs())
|
||||
{
|
||||
log.error("Unable to create spiders directory");
|
||||
}
|
||||
|
||||
String[] values = urls.split(",");
|
||||
for (String value : values)
|
||||
{
|
||||
value = value.trim();
|
||||
System.out.println(" Downloading: " + value);
|
||||
|
||||
URL url = new URL(value);
|
||||
|
||||
Get get = new Get();
|
||||
get.setDest(new File(spiders, url.getHost() + url.getPath().replace("/","-")));
|
||||
get.setSrc(url);
|
||||
get.setUseTimestamp(true);
|
||||
get.execute();
|
||||
|
||||
}
|
||||
|
||||
|
||||
} catch (Exception e)
|
||||
{
|
||||
System.err.println(" - Error: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
/**
|
||||
* 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.statistics.util;
|
||||
|
||||
import org.apache.commons.cli.*;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.apache.tools.ant.taskdefs.Get;
|
||||
import org.dspace.core.ConfigurationManager;
|
||||
import org.dspace.statistics.SolrLogger;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* Class to load intermediate statistics files into solr
|
||||
*
|
||||
* @author Stuart Lewis
|
||||
*/
|
||||
public class StatisticsClient
|
||||
{
|
||||
private static final Logger log = Logger.getLogger(StatisticsClient.class);
|
||||
|
||||
/**
|
||||
* Print the help message
|
||||
*
|
||||
* @param options The command line options the user gave
|
||||
* @param exitCode the system exit code to use
|
||||
*/
|
||||
private static void printHelp(Options options, int exitCode)
|
||||
{
|
||||
// print the help message
|
||||
HelpFormatter myhelp = new HelpFormatter();
|
||||
myhelp.printHelp("StatisticsClient\n", options);
|
||||
System.exit(exitCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Main method to run the statistics importer.
|
||||
*
|
||||
* @param args The command line arguments
|
||||
* @throws Exception If something goes wrong
|
||||
*/
|
||||
public static void main(String[] args) throws Exception
|
||||
{
|
||||
CommandLineParser parser = new PosixParser();
|
||||
|
||||
Options options = new Options();
|
||||
|
||||
options.addOption("u", "update-spider-files", false,
|
||||
"Update Spider IP Files from internet into " +
|
||||
ConfigurationManager.getProperty("dspace.dir") + "/config/spiders");
|
||||
|
||||
options.addOption("m", "mark-spiders", false, "Update isBot Flag in Solr");
|
||||
options.addOption("f", "delete-spiders-by-flag", false, "Delete Spiders in Solr By isBot Flag");
|
||||
options.addOption("i", "delete-spiders-by-ip", false, "Delete Spiders in Solr By IP Address");
|
||||
options.addOption("o", "optimize", false, "Run maintenance on the SOLR index");
|
||||
options.addOption("b", "reindex-bitstreams", false, "Reindex the bitstreams to ensure we have the bundle name");
|
||||
options.addOption("r", "remove-deleted-bitstreams", false, "While indexing the bundle names remove the statistics about deleted bitstreams");
|
||||
options.addOption("s", "shard-solr-index", false, "Split the data from the main Solr core into separate Solr cores per year");
|
||||
options.addOption("h", "help", false, "help");
|
||||
|
||||
CommandLine line = parser.parse(options, args);
|
||||
|
||||
// Did the user ask to see the help?
|
||||
if (line.hasOption('h'))
|
||||
{
|
||||
printHelp(options, 0);
|
||||
}
|
||||
|
||||
if(line.hasOption("u"))
|
||||
{
|
||||
StatisticsClient.updateSpiderFiles();
|
||||
}
|
||||
else if (line.hasOption('m'))
|
||||
{
|
||||
SolrLogger.markRobotsByIP();
|
||||
}
|
||||
else if(line.hasOption('f'))
|
||||
{
|
||||
SolrLogger.deleteRobotsByIsBotFlag();
|
||||
}
|
||||
else if(line.hasOption('i'))
|
||||
{
|
||||
SolrLogger.deleteRobotsByIP();
|
||||
}
|
||||
else if(line.hasOption('o'))
|
||||
{
|
||||
SolrLogger.optimizeSOLR();
|
||||
}
|
||||
else if(line.hasOption('b'))
|
||||
{
|
||||
SolrLogger.reindexBitstreamHits(line.hasOption('r'));
|
||||
}
|
||||
else if(line.hasOption('s'))
|
||||
{
|
||||
SolrLogger.shardSolrIndex();
|
||||
}
|
||||
else
|
||||
{
|
||||
printHelp(options, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to update Spiders in config directory.
|
||||
*/
|
||||
private static void updateSpiderFiles()
|
||||
{
|
||||
try
|
||||
{
|
||||
System.out.println("Downloading latest spider IP addresses:");
|
||||
|
||||
// Get the list URLs to download from
|
||||
String urls = ConfigurationManager.getProperty("solr-statistics", "spiderips.urls");
|
||||
if ((urls == null) || ("".equals(urls)))
|
||||
{
|
||||
System.err.println(" - Missing setting from dspace.cfg: solr.spiderips.urls");
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
// Get the location of spiders directory
|
||||
File spiders = new File(ConfigurationManager.getProperty("dspace.dir"),"config/spiders");
|
||||
|
||||
if (!spiders.exists() && !spiders.mkdirs())
|
||||
{
|
||||
log.error("Unable to create spiders directory");
|
||||
}
|
||||
|
||||
String[] values = urls.split(",");
|
||||
for (String value : values)
|
||||
{
|
||||
value = value.trim();
|
||||
System.out.println(" Downloading: " + value);
|
||||
|
||||
URL url = new URL(value);
|
||||
|
||||
Get get = new Get();
|
||||
get.setDest(new File(spiders, url.getHost() + url.getPath().replace("/","-")));
|
||||
get.setSrc(url);
|
||||
get.setUseTimestamp(true);
|
||||
get.execute();
|
||||
|
||||
}
|
||||
|
||||
|
||||
} catch (Exception e)
|
||||
{
|
||||
System.err.println(" - Error: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@@ -10,14 +10,12 @@ package org.dspace.app.xmlui.aspect.artifactbrowser;
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
import org.apache.cocoon.caching.CacheableProcessingComponent;
|
||||
import org.apache.cocoon.environment.ObjectModelHelper;
|
||||
import org.apache.cocoon.environment.Request;
|
||||
import org.apache.oro.text.perl.Perl5Util;
|
||||
import org.dspace.app.xmlui.utils.HandleUtil;
|
||||
import org.dspace.app.xmlui.utils.UIException;
|
||||
import org.dspace.app.xmlui.wing.Message;
|
||||
@@ -98,10 +96,9 @@ public class AdvancedSearch extends AbstractSearch implements CacheableProcessin
|
||||
|
||||
/** How many conjunction fields to display */
|
||||
private static final int FIELD_DISPLAY_COUNT = 3;
|
||||
private static final int FIELD_MAX_COUNT = 12;
|
||||
|
||||
|
||||
/** A cache of extracted search fields */
|
||||
private java.util.List<SearchField> fields;
|
||||
private java.util.List<AdvancedSearchUtils.SearchField> fields;
|
||||
|
||||
/**
|
||||
* Add Page metadata.
|
||||
@@ -167,7 +164,7 @@ public class AdvancedSearch extends AbstractSearch implements CacheableProcessin
|
||||
buildQueryField(i, row.addCell());
|
||||
}
|
||||
|
||||
for (SearchField field : fields)
|
||||
for (AdvancedSearchUtils.SearchField field : fields)
|
||||
{
|
||||
// Skip over all the fields we've displayed.
|
||||
int i = field.getIndex();
|
||||
@@ -315,7 +312,7 @@ public class AdvancedSearch extends AbstractSearch implements CacheableProcessin
|
||||
parameters.put("scope", scope);
|
||||
}
|
||||
|
||||
for (SearchField searchField : getSearchFields(request))
|
||||
for (AdvancedSearchUtils.SearchField searchField : getSearchFields(request))
|
||||
{
|
||||
int index = searchField.getIndex();
|
||||
String field = searchField.getField();
|
||||
@@ -363,188 +360,13 @@ public class AdvancedSearch extends AbstractSearch implements CacheableProcessin
|
||||
protected String getQuery() throws UIException
|
||||
{
|
||||
Request request = ObjectModelHelper.getRequest(objectModel);
|
||||
return buildQuery(getSearchFields(request));
|
||||
return AdvancedSearchUtils.buildQuery(getSearchFields(request));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Given a list of search fields buld a lucene search query string.
|
||||
*
|
||||
* @param fields The search fields
|
||||
* @return A string
|
||||
*/
|
||||
private String buildQuery(java.util.List<SearchField> fields)
|
||||
{
|
||||
Perl5Util util = new Perl5Util();
|
||||
|
||||
StringBuilder query = new StringBuilder();
|
||||
query.append("(");
|
||||
|
||||
// Loop through the fields building the search query as we go.
|
||||
for (SearchField field : fields)
|
||||
{
|
||||
// if the field is empty, then skip it and try a later one.
|
||||
if (field.getQuery() == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add the conjunction for everything but the first field.
|
||||
if (fields.indexOf(field) > 0)
|
||||
{
|
||||
query.append(" ").append(field.getConjunction()).append(" ").toString();
|
||||
}
|
||||
|
||||
// Two cases, one if a specific search field is specified or if
|
||||
// ANY is given then just a general search is performed.
|
||||
if ("ANY".equals(field.getField()))
|
||||
{
|
||||
// No field specified,
|
||||
query.append("(").append(field.getQuery()).append(")").toString();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Specific search field specified, add the field specific field.
|
||||
|
||||
// Replace single quotes with double quotes (only if they match)
|
||||
String subQuery = util.substitute("s/\'(.*)\'/\"$1\"/g", field.getQuery());
|
||||
|
||||
// If the field is not quoted ...
|
||||
if (!util.match("/\".*\"/", subQuery))
|
||||
{
|
||||
// ... then separate each word and re-specify the search field.
|
||||
subQuery = util.substitute("s/[ ]+/ " + field.getField() + ":/g", subQuery);
|
||||
}
|
||||
|
||||
// Put the subQuery into the general query
|
||||
query.append("(").append(field.getField()).append(":").append(subQuery).append(")").toString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (query.length() == 1)
|
||||
{
|
||||
return "";
|
||||
private java.util.List<AdvancedSearchUtils.SearchField> getSearchFields(Request request) throws UIException {
|
||||
if (fields == null){
|
||||
fields = AdvancedSearchUtils.getSearchFields(request);
|
||||
}
|
||||
|
||||
return query.append(")").toString();
|
||||
return fields;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a list of search fields from the request object
|
||||
* and parse them into a linear array of fileds. The field's
|
||||
* index is preserved, so if it comes in as index 17 it will
|
||||
* be outputted as field 17.
|
||||
*
|
||||
* @param request The http request object
|
||||
* @return Array of search fields
|
||||
* @throws UIException
|
||||
*/
|
||||
public java.util.List<SearchField> getSearchFields(Request request) throws UIException
|
||||
{
|
||||
if (this.fields != null)
|
||||
{
|
||||
return this.fields;
|
||||
}
|
||||
|
||||
// Get how many fields to search
|
||||
int numSearchField;
|
||||
try {
|
||||
String numSearchFieldStr = request.getParameter("num_search_field");
|
||||
numSearchField = Integer.valueOf(numSearchFieldStr);
|
||||
}
|
||||
catch (NumberFormatException nfe)
|
||||
{
|
||||
numSearchField = FIELD_MAX_COUNT;
|
||||
}
|
||||
|
||||
// Iterate over all the possible fields and add each one to the list of fields.
|
||||
ArrayList<SearchField> fields = new ArrayList<SearchField>();
|
||||
for (int i = 1; i <= numSearchField; i++)
|
||||
{
|
||||
String field = request.getParameter("field"+i);
|
||||
String query = decodeFromURL(request.getParameter("query"+i));
|
||||
String conjunction = request.getParameter("conjunction"+i);
|
||||
|
||||
if (field != null)
|
||||
{
|
||||
field = field.trim();
|
||||
if (field.length() == 0)
|
||||
{
|
||||
field = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (query != null)
|
||||
{
|
||||
query = query.trim();
|
||||
if (query.length() == 0)
|
||||
{
|
||||
query = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (conjunction != null)
|
||||
{
|
||||
conjunction = conjunction.trim();
|
||||
if (conjunction.length() == 0)
|
||||
{
|
||||
conjunction = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (field == null)
|
||||
{
|
||||
field = "ANY";
|
||||
}
|
||||
if (conjunction == null)
|
||||
{
|
||||
conjunction = "AND";
|
||||
}
|
||||
|
||||
if (query != null)
|
||||
{
|
||||
fields.add(new SearchField(i, field, query, conjunction));
|
||||
}
|
||||
}
|
||||
|
||||
this.fields = fields;
|
||||
|
||||
return this.fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* A private record keeping class to relate the various
|
||||
* components of a search field together.
|
||||
*/
|
||||
private static class SearchField {
|
||||
|
||||
/** What index the search field is, typically there are just three - but the theme may expand this number */
|
||||
private int index;
|
||||
|
||||
/** The field to search, ANY if none specified */
|
||||
private String field;
|
||||
|
||||
/** The query string to search for */
|
||||
private String query;
|
||||
|
||||
/** the conjunction: either "AND" or "OR" */
|
||||
private String conjuction;
|
||||
|
||||
public SearchField(int index, String field, String query, String conjunction)
|
||||
{
|
||||
this.index = index;
|
||||
this.field = field;
|
||||
this.query = query;
|
||||
this.conjuction = conjunction;
|
||||
}
|
||||
|
||||
public int getIndex() { return this.index;}
|
||||
public String getField() { return this.field;}
|
||||
public String getQuery() { return this.query;}
|
||||
public String getConjunction() { return this.conjuction;}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* 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.artifactbrowser;
|
||||
|
||||
import org.apache.cocoon.environment.Request;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.dspace.app.xmlui.cocoon.SearchLoggerAction;
|
||||
import org.dspace.app.xmlui.utils.UIException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Returns the query for the advanced search so our SearchLoggerAction can log this
|
||||
*
|
||||
* @author Kevin Van de Velde (kevin at atmire dot com)
|
||||
* @author Ben Bosman (ben at atmire dot com)
|
||||
* @author Mark Diggory (markd at atmire dot com)
|
||||
*/
|
||||
public class AdvancedSearchLoggerAction extends SearchLoggerAction {
|
||||
|
||||
private static final Logger log = Logger.getLogger(SearchLoggerAction.class);
|
||||
|
||||
@Override
|
||||
protected List<String> getQueries(Request request) {
|
||||
try {
|
||||
String advancedSearchQuery = AdvancedSearchUtils.buildQuery(AdvancedSearchUtils.getSearchFields(request));
|
||||
if(!StringUtils.isBlank(advancedSearchQuery))
|
||||
{
|
||||
return Arrays.asList(advancedSearchQuery);
|
||||
}
|
||||
} catch (UIException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
return new ArrayList<String>();
|
||||
}
|
||||
}
|
@@ -0,0 +1,233 @@
|
||||
/**
|
||||
* 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.artifactbrowser;
|
||||
|
||||
import org.apache.cocoon.environment.Request;
|
||||
import org.apache.oro.text.perl.Perl5Util;
|
||||
import org.dspace.app.xmlui.utils.UIException;
|
||||
import org.dspace.core.Constants;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Central place where advanced search queries can be built since these are built on several places
|
||||
*
|
||||
* @author Kevin Van de Velde (kevin at atmire dot com)
|
||||
* @author Ben Bosman (ben at atmire dot com)
|
||||
* @author Mark Diggory (markd at atmire dot com)
|
||||
*/
|
||||
public class AdvancedSearchUtils {
|
||||
|
||||
private static final int FIELD_MAX_COUNT = 12;
|
||||
|
||||
/**
|
||||
* Given a list of search fields build a lucene search query string.
|
||||
*
|
||||
* @param fields The search fields
|
||||
* @return A string
|
||||
*/
|
||||
public static String buildQuery(java.util.List<SearchField> fields)
|
||||
{
|
||||
Perl5Util util = new Perl5Util();
|
||||
|
||||
StringBuilder query = new StringBuilder();
|
||||
query.append("(");
|
||||
|
||||
// Loop through the fields building the search query as we go.
|
||||
for (SearchField field : fields)
|
||||
{
|
||||
// if the field is empty, then skip it and try a later one.
|
||||
if (field.getQuery() == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add the conjunction for everything but the first field.
|
||||
if (fields.indexOf(field) > 0)
|
||||
{
|
||||
query.append(" ").append(field.getConjunction()).append(" ");
|
||||
}
|
||||
|
||||
// Two cases, one if a specific search field is specified or if
|
||||
// ANY is given then just a general search is performed.
|
||||
if ("ANY".equals(field.getField()))
|
||||
{
|
||||
// No field specified,
|
||||
query.append("(").append(field.getQuery()).append(")");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Specific search field specified, add the field specific field.
|
||||
|
||||
// Replace single quotes with double quotes (only if they match)
|
||||
String subQuery = util.substitute("s/\'(.*)\'/\"$1\"/g", field.getQuery());
|
||||
|
||||
// If the field is not quoted ...
|
||||
if (!util.match("/\".*\"/", subQuery))
|
||||
{
|
||||
// ... then separate each word and re-specify the search field.
|
||||
subQuery = util.substitute("s/[ ]+/ " + field.getField() + ":/g", subQuery);
|
||||
}
|
||||
|
||||
// Put the subQuery into the general query
|
||||
query.append("(").append(field.getField()).append(":").append(subQuery).append(")");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (query.length() == 1)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
return query.append(")").toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a list of search fields from the request object
|
||||
* and parse them into a linear array of fileds. The field's
|
||||
* index is preserved, so if it comes in as index 17 it will
|
||||
* be outputted as field 17.
|
||||
*
|
||||
* @param request The http request object
|
||||
* @return Array of search fields
|
||||
* @throws org.dspace.app.xmlui.utils.UIException
|
||||
*/
|
||||
public static java.util.List<SearchField> getSearchFields(Request request) throws UIException
|
||||
{
|
||||
// Get how many fields to search
|
||||
int numSearchField;
|
||||
try {
|
||||
String numSearchFieldStr = request.getParameter("num_search_field");
|
||||
numSearchField = Integer.valueOf(numSearchFieldStr);
|
||||
}
|
||||
catch (NumberFormatException nfe)
|
||||
{
|
||||
numSearchField = FIELD_MAX_COUNT;
|
||||
}
|
||||
|
||||
// Iterate over all the possible fields and add each one to the list of fields.
|
||||
ArrayList<SearchField> fields = new ArrayList<SearchField>();
|
||||
for (int i = 1; i <= numSearchField; i++)
|
||||
{
|
||||
String field = request.getParameter("field"+i);
|
||||
String query = decodeFromURL(request.getParameter("query"+i));
|
||||
String conjunction = request.getParameter("conjunction"+i);
|
||||
|
||||
if (field != null)
|
||||
{
|
||||
field = field.trim();
|
||||
if (field.length() == 0)
|
||||
{
|
||||
field = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (query != null)
|
||||
{
|
||||
query = query.trim();
|
||||
if (query.length() == 0)
|
||||
{
|
||||
query = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (conjunction != null)
|
||||
{
|
||||
conjunction = conjunction.trim();
|
||||
if (conjunction.length() == 0)
|
||||
{
|
||||
conjunction = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (field == null)
|
||||
{
|
||||
field = "ANY";
|
||||
}
|
||||
if (conjunction == null)
|
||||
{
|
||||
conjunction = "AND";
|
||||
}
|
||||
|
||||
if (query != null)
|
||||
{
|
||||
fields.add(new SearchField(i, field, query, conjunction));
|
||||
}
|
||||
}
|
||||
|
||||
return fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* A private record keeping class to relate the various
|
||||
* components of a search field together.
|
||||
*/
|
||||
public static class SearchField {
|
||||
|
||||
/** What index the search field is, typically there are just three - but the theme may expand this number */
|
||||
private int index;
|
||||
|
||||
/** The field to search, ANY if none specified */
|
||||
private String field;
|
||||
|
||||
/** The query string to search for */
|
||||
private String query;
|
||||
|
||||
/** the conjunction: either "AND" or "OR" */
|
||||
private String conjuction;
|
||||
|
||||
public SearchField(int index, String field, String query, String conjunction)
|
||||
{
|
||||
this.index = index;
|
||||
this.field = field;
|
||||
this.query = query;
|
||||
this.conjuction = conjunction;
|
||||
}
|
||||
|
||||
public int getIndex() { return this.index;}
|
||||
public String getField() { return this.field;}
|
||||
public String getQuery() { return this.query;}
|
||||
public String getConjunction() { return this.conjuction;}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode the given string from URL transmission.
|
||||
*
|
||||
* @param encodedString
|
||||
* The encoded string.
|
||||
* @return The unencoded string
|
||||
*/
|
||||
private static String decodeFromURL(String encodedString) throws UIException
|
||||
{
|
||||
if (encodedString == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Percent(%) is a special character, and must first be escaped as %25
|
||||
if (encodedString.contains("%"))
|
||||
{
|
||||
encodedString = encodedString.replace("%", "%25");
|
||||
}
|
||||
|
||||
return URLDecoder.decode(encodedString, Constants.DEFAULT_ENCODING);
|
||||
}
|
||||
catch (UnsupportedEncodingException uee)
|
||||
{
|
||||
throw new UIException(uee);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* 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.artifactbrowser;
|
||||
|
||||
import org.apache.cocoon.environment.Request;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.dspace.app.xmlui.cocoon.SearchLoggerAction;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Returns the query for a simple search so our SearchLoggerAction can log this
|
||||
*
|
||||
* @author Kevin Van de Velde (kevin at atmire dot com)
|
||||
* @author Ben Bosman (ben at atmire dot com)
|
||||
* @author Mark Diggory (markd at atmire dot com)
|
||||
*/
|
||||
public class SimpleSearchLoggerAction extends SearchLoggerAction{
|
||||
|
||||
@Override
|
||||
protected List<String> getQueries(Request request) {
|
||||
String query = request.getParameter("query");
|
||||
if(!StringUtils.isBlank(query)){
|
||||
return Arrays.asList(request.getParameter("query"));
|
||||
}else{
|
||||
return new ArrayList<String>();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,155 @@
|
||||
/**
|
||||
* 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.statistics;
|
||||
|
||||
import org.apache.cocoon.environment.ObjectModelHelper;
|
||||
import org.apache.cocoon.environment.Request;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.solr.client.solrj.SolrServerException;
|
||||
import org.dspace.app.xmlui.cocoon.AbstractDSpaceTransformer;
|
||||
import org.dspace.app.xmlui.wing.Message;
|
||||
import org.dspace.app.xmlui.wing.WingException;
|
||||
import org.dspace.app.xmlui.wing.element.*;
|
||||
import org.dspace.statistics.Dataset;
|
||||
import org.dspace.statistics.content.StatisticsTable;
|
||||
import org.dspace.statistics.content.filter.StatisticsSolrDateFilter;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.text.ParseException;
|
||||
|
||||
/**
|
||||
* @author Kevin Van de Velde (kevin at atmire dot com)
|
||||
* @author Ben Bosman (ben at atmire dot com)
|
||||
* @author Mark Diggory (markd at atmire dot com)
|
||||
*/
|
||||
public abstract class AbstractStatisticsDataTransformer extends AbstractDSpaceTransformer {
|
||||
|
||||
private static final Message T_time_filter_last_month = message("xmlui.statistics.StatisticsSearchTransformer.time-filter.last-month");
|
||||
private static final Message T_time_filter_overall = message("xmlui.statistics.StatisticsSearchTransformer.time-filter.overall");
|
||||
private static final Message T_time_filter_last_year = message("xmlui.statistics.StatisticsSearchTransformer.time-filter.last-year");
|
||||
private static final Message T_time_filter_last6_months = message("xmlui.statistics.StatisticsSearchTransformer.time-filter.last-6-months");
|
||||
|
||||
|
||||
protected void addTimeFilter(Division mainDivision) throws WingException {
|
||||
Request request = ObjectModelHelper.getRequest(objectModel);
|
||||
String selectedTimeFilter = request.getParameter("time_filter");
|
||||
|
||||
Select timeFilter = mainDivision.addPara().addSelect("time_filter");
|
||||
timeFilter.addOption(StringUtils.equals(selectedTimeFilter, "-1"), "-1", T_time_filter_last_month);
|
||||
timeFilter.addOption(StringUtils.equals(selectedTimeFilter, "-6"), "-6", T_time_filter_last6_months);
|
||||
timeFilter.addOption(StringUtils.equals(selectedTimeFilter, "-12"), "-12", T_time_filter_last_year);
|
||||
timeFilter.addOption(StringUtils.isBlank(selectedTimeFilter), "", T_time_filter_overall);
|
||||
}
|
||||
|
||||
protected StatisticsSolrDateFilter getDateFilter(String timeFilter){
|
||||
if(StringUtils.isNotEmpty(timeFilter))
|
||||
{
|
||||
StatisticsSolrDateFilter dateFilter = new StatisticsSolrDateFilter();
|
||||
dateFilter.setStartStr(timeFilter);
|
||||
dateFilter.setEndStr("0");
|
||||
dateFilter.setTypeStr("month");
|
||||
return dateFilter;
|
||||
}else{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a table layout to the page
|
||||
*
|
||||
* @param mainDiv
|
||||
* the div to add the table to
|
||||
* @param display the statistics table containing our data
|
||||
* @throws org.xml.sax.SAXException
|
||||
* @throws org.dspace.app.xmlui.wing.WingException
|
||||
* @throws java.text.ParseException
|
||||
* @throws java.io.IOException
|
||||
* @throws org.apache.solr.client.solrj.SolrServerException
|
||||
* @throws java.sql.SQLException
|
||||
*/
|
||||
protected void addDisplayTable(Division mainDiv, StatisticsTable display, boolean addRowTitles, String []valueMessagePrefixes)
|
||||
throws SAXException, WingException, SQLException,
|
||||
SolrServerException, IOException, ParseException {
|
||||
|
||||
String title = display.getTitle();
|
||||
|
||||
Dataset dataset = display.getDataset();
|
||||
|
||||
if (dataset == null)
|
||||
{
|
||||
/** activate dataset query */
|
||||
dataset = display.getDataset(context);
|
||||
}
|
||||
|
||||
if (dataset != null)
|
||||
{
|
||||
|
||||
String[][] matrix = dataset.getMatrix();
|
||||
|
||||
if(matrix.length == 0){
|
||||
//If no results are found alert the user of this !
|
||||
mainDiv.addPara(getNoResultsMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
/** Generate Table */
|
||||
Division wrapper = mainDiv.addDivision("tablewrapper");
|
||||
Table table = wrapper.addTable("list-table", 1, 1,
|
||||
title == null ? "detailtable" : "tableWithTitle detailtable");
|
||||
if (title != null)
|
||||
{
|
||||
table.setHead(message(title));
|
||||
}
|
||||
|
||||
/** Generate Header Row */
|
||||
Row headerRow = table.addRow();
|
||||
if(addRowTitles)
|
||||
{
|
||||
headerRow.addCell("spacer", Cell.ROLE_HEADER, "labelcell");
|
||||
}
|
||||
|
||||
String[] cLabels = dataset.getColLabels().toArray(new String[0]);
|
||||
for (int row = 0; row < cLabels.length; row++)
|
||||
{
|
||||
Cell cell = headerRow.addCell(0 + "-" + row + "-h", Cell.ROLE_HEADER, "labelcell");
|
||||
cell.addContent(message("xmlui.statistics.display.table.column-label." + cLabels[row]));
|
||||
}
|
||||
|
||||
/** Generate Table Body */
|
||||
for (int row = 0; row < matrix.length; row++) {
|
||||
Row valListRow = table.addRow();
|
||||
|
||||
if(addRowTitles){
|
||||
/** Add Row Title */
|
||||
valListRow.addCell("" + row, Cell.ROLE_DATA, "labelcell")
|
||||
.addContent(dataset.getRowLabels().get(row));
|
||||
}
|
||||
|
||||
/** Add Rest of Row */
|
||||
for (int col = 0; col < matrix[row].length; col++) {
|
||||
Cell cell = valListRow.addCell(row + "-" + col,
|
||||
Cell.ROLE_DATA, "datacell");
|
||||
String messagePrefix = null;
|
||||
if(valueMessagePrefixes != null && col < valueMessagePrefixes.length){
|
||||
messagePrefix = valueMessagePrefixes[col];
|
||||
}
|
||||
|
||||
if(messagePrefix != null){
|
||||
cell.addContent(message(messagePrefix + matrix[row][col]));
|
||||
}else{
|
||||
cell.addContent(matrix[row][col]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract Message getNoResultsMessage();
|
||||
}
|
@@ -15,10 +15,15 @@ import org.dspace.app.xmlui.wing.Message;
|
||||
import org.dspace.app.xmlui.utils.UIException;
|
||||
import org.dspace.app.xmlui.utils.HandleUtil;
|
||||
import org.dspace.authorize.AuthorizeException;
|
||||
import org.dspace.authorize.AuthorizeManager;
|
||||
import org.dspace.content.DSpaceObject;
|
||||
import org.apache.cocoon.caching.CacheableProcessingComponent;
|
||||
import org.apache.excalibur.source.SourceValidity;
|
||||
import org.apache.excalibur.source.impl.validity.NOPValidity;
|
||||
import org.dspace.core.Constants;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.services.ConfigurationService;
|
||||
import org.dspace.utils.DSpace;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import java.io.Serializable;
|
||||
@@ -35,7 +40,9 @@ import java.sql.SQLException;
|
||||
public class Navigation extends AbstractDSpaceTransformer implements CacheableProcessingComponent {
|
||||
|
||||
private static final Message T_statistics_head = message("xmlui.statistics.Navigation.title");
|
||||
private static final Message T_statistics_view = message("xmlui.statistics.Navigation.view");
|
||||
private static final Message T_statistics_usage_view = message("xmlui.statistics.Navigation.usage.view");
|
||||
private static final Message T_statistics_search_view = message("xmlui.statistics.Navigation.search.view");
|
||||
private static final Message T_statistics_workflow_view = message("xmlui.statistics.Navigation.workflow.view");
|
||||
|
||||
public Serializable getKey() {
|
||||
//TODO: DO THIS
|
||||
@@ -67,17 +74,46 @@ public class Navigation extends AbstractDSpaceTransformer implements CacheablePr
|
||||
List statistics = options.addList("statistics");
|
||||
|
||||
DSpaceObject dso = HandleUtil.obtainHandle(objectModel);
|
||||
boolean displayUsageStats = displayStatsType(context, "usage", dso);
|
||||
boolean displaySearchStats = displayStatsType(context, "search", dso);
|
||||
boolean displayWorkflowStats = displayStatsType(context, "workflow", dso);
|
||||
|
||||
|
||||
if(dso != null && dso.getHandle() != null){
|
||||
statistics.setHead(T_statistics_head);
|
||||
statistics.addItemXref(contextPath + "/handle/" + dso.getHandle() + "/statistics", T_statistics_view);
|
||||
if(displayUsageStats){
|
||||
statistics.addItemXref(contextPath + "/handle/" + dso.getHandle() + "/statistics", T_statistics_usage_view);
|
||||
}
|
||||
//Items cannot have search statistics
|
||||
if(displaySearchStats && dso.getType() != Constants.ITEM){
|
||||
statistics.addItemXref(contextPath + "/handle/" + dso.getHandle() + "/search-statistics", T_statistics_search_view);
|
||||
}
|
||||
//Items cannot have workflow statistics
|
||||
if(displayWorkflowStats && dso.getType() != Constants.ITEM){
|
||||
statistics.addItemXref(contextPath + "/handle/" + dso.getHandle() + "/workflow-statistics", T_statistics_workflow_view);
|
||||
}
|
||||
|
||||
}else{
|
||||
// This Navigation is only called either on a DSO related page, or the homepage
|
||||
// If on the home page: add statistics link for the home page
|
||||
statistics.setHead(T_statistics_head);
|
||||
statistics.addItemXref(contextPath + "/statistics-home", T_statistics_view);
|
||||
if(displayUsageStats){
|
||||
statistics.addItemXref(contextPath + "/statistics-home", T_statistics_usage_view.parameterize());
|
||||
}
|
||||
if(displaySearchStats){
|
||||
statistics.addItemXref(contextPath + "/search-statistics", T_statistics_search_view);
|
||||
}
|
||||
if(displayWorkflowStats){
|
||||
statistics.addItemXref(contextPath + "/workflow-statistics", T_statistics_workflow_view);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
protected boolean displayStatsType(Context context, String type, DSpaceObject dso) throws SQLException {
|
||||
ConfigurationService cs = new DSpace().getConfigurationService();
|
||||
return !cs.getPropertyAsType("usage-statistics.authorization.admin." + type, Boolean.TRUE) || AuthorizeManager.isAdmin(context, dso);
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* 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.statistics;
|
||||
|
||||
import org.apache.avalon.framework.parameters.Parameters;
|
||||
import org.apache.cocoon.acting.AbstractAction;
|
||||
import org.apache.cocoon.environment.ObjectModelHelper;
|
||||
import org.apache.cocoon.environment.Redirector;
|
||||
import org.apache.cocoon.environment.Request;
|
||||
import org.apache.cocoon.environment.SourceResolver;
|
||||
import org.apache.cocoon.environment.http.HttpEnvironment;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.dspace.app.xmlui.utils.ContextUtil;
|
||||
import org.dspace.app.xmlui.utils.HandleUtil;
|
||||
import org.dspace.content.DSpaceObject;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.handle.HandleManager;
|
||||
import org.dspace.usage.UsageEvent;
|
||||
import org.dspace.usage.UsageSearchEvent;
|
||||
import org.dspace.utils.DSpace;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Action that fires a search event & redirect the user to the view page of the object clicked on in the search results
|
||||
*
|
||||
* @author Kevin Van de Velde (kevin at atmire dot com)
|
||||
* @author Ben Bosman (ben at atmire dot com)
|
||||
* @author Mark Diggory (markd at atmire dot com)
|
||||
*/
|
||||
public class SearchResultLogAction extends AbstractAction {
|
||||
|
||||
@Override
|
||||
public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception {
|
||||
Request request = ObjectModelHelper.getRequest(objectModel);
|
||||
Context context = ContextUtil.obtainContext(objectModel);
|
||||
DSpaceObject scope = HandleUtil.obtainHandle(objectModel);
|
||||
|
||||
String redirectUrl = request.getParameter("redirectUrl");
|
||||
String resultHandle = StringUtils.substringAfter(redirectUrl, "/handle/");
|
||||
DSpaceObject result = HandleManager.resolveToObject(ContextUtil.obtainContext(request), resultHandle);
|
||||
|
||||
//Fire an event to log our search result
|
||||
UsageSearchEvent searchEvent = new UsageSearchEvent(
|
||||
UsageEvent.Action.SEARCH,
|
||||
request,
|
||||
context,
|
||||
result,
|
||||
Arrays.asList(request.getParameterValues("query")), scope);
|
||||
|
||||
if(!StringUtils.isBlank(request.getParameter("rpp"))){
|
||||
searchEvent.setRpp(Integer.parseInt(request.getParameter("rpp")));
|
||||
}
|
||||
if(!StringUtils.isBlank(request.getParameter("sort_by"))){
|
||||
searchEvent.setSortBy(request.getParameter("sort_by"));
|
||||
}
|
||||
if(!StringUtils.isBlank(request.getParameter("order"))){
|
||||
searchEvent.setSortOrder(request.getParameter("order"));
|
||||
}
|
||||
if(!StringUtils.isBlank(request.getParameter("page"))){
|
||||
searchEvent.setPage(Integer.parseInt(request.getParameter("page")));
|
||||
}
|
||||
|
||||
new DSpace().getEventService().fireEvent(
|
||||
searchEvent);
|
||||
|
||||
HttpServletResponse httpResponse = (HttpServletResponse) objectModel.get(HttpEnvironment.HTTP_RESPONSE_OBJECT);
|
||||
httpResponse.sendRedirect(redirectUrl);
|
||||
|
||||
return new HashMap();
|
||||
}
|
||||
}
|
@@ -1,95 +1,111 @@
|
||||
/**
|
||||
* 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.statistics;
|
||||
|
||||
import org.apache.cocoon.matching.Matcher;
|
||||
import org.apache.cocoon.sitemap.PatternException;
|
||||
import org.apache.avalon.framework.parameters.Parameters;
|
||||
import org.apache.avalon.framework.logger.AbstractLogEnabled;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.core.ConfigurationManager;
|
||||
import org.dspace.core.Constants;
|
||||
import org.dspace.app.xmlui.utils.ContextUtil;
|
||||
import org.dspace.app.xmlui.utils.HandleUtil;
|
||||
import org.dspace.content.DSpaceObject;
|
||||
import org.dspace.authorize.AuthorizeManager;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* User: @author kevinvandevelde (kevin at atmire.com)
|
||||
* Date: 19-nov-2009
|
||||
* Time: 17:19:56
|
||||
*/
|
||||
public class StatisticsAuthorizedMatcher extends AbstractLogEnabled implements Matcher{
|
||||
|
||||
|
||||
public Map match(String pattern, Map objectModel, Parameters parameters) throws PatternException {
|
||||
// Are we checking for *NOT* the action or the action.
|
||||
boolean not = false;
|
||||
int action = Constants.READ; // the action to check
|
||||
|
||||
if (pattern.startsWith("!"))
|
||||
{
|
||||
not = true;
|
||||
pattern = pattern.substring(1);
|
||||
}
|
||||
|
||||
if(!pattern.equals("READ"))
|
||||
{
|
||||
getLogger().warn("Invalid action: '"+pattern+"'");
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Context context = ContextUtil.obtainContext(objectModel);
|
||||
DSpaceObject dso = HandleUtil.obtainHandle(objectModel);
|
||||
|
||||
//We have always got rights to view stats on the home page (admin rights will be checked later)
|
||||
boolean authorized = dso == null || AuthorizeManager.authorizeActionBoolean(context, dso, action, false);
|
||||
//If we are authorized check for any other authorization actions present
|
||||
if(authorized && ConfigurationManager.getBooleanProperty("usage-statistics", "authorization.admin"))
|
||||
{
|
||||
//If we have no user, we cannot be admin
|
||||
if(context.getCurrentUser() == null)
|
||||
{
|
||||
authorized = false;
|
||||
}
|
||||
|
||||
if(authorized){
|
||||
//Check for admin
|
||||
authorized = AuthorizeManager.isAdmin(context);
|
||||
if(!authorized)
|
||||
{
|
||||
//Check if we have authorization for the owning colls, comms, ...
|
||||
authorized = AuthorizeManager.isAdmin(context, dso);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// XOR
|
||||
if (not ^ authorized)
|
||||
{
|
||||
return new HashMap();
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
catch (SQLException sqle)
|
||||
{
|
||||
throw new PatternException("Unable to obtain DSpace Context", sqle);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 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.statistics;
|
||||
|
||||
import org.apache.cocoon.matching.Matcher;
|
||||
import org.apache.cocoon.sitemap.PatternException;
|
||||
import org.apache.avalon.framework.parameters.Parameters;
|
||||
import org.apache.avalon.framework.logger.AbstractLogEnabled;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.core.ConfigurationManager;
|
||||
import org.dspace.core.Constants;
|
||||
import org.dspace.app.xmlui.utils.ContextUtil;
|
||||
import org.dspace.app.xmlui.utils.HandleUtil;
|
||||
import org.dspace.content.DSpaceObject;
|
||||
import org.dspace.authorize.AuthorizeManager;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* User: @author kevinvandevelde (kevin at atmire.com)
|
||||
* Date: 19-nov-2009
|
||||
* Time: 17:19:56
|
||||
*/
|
||||
public class StatisticsAuthorizedMatcher extends AbstractLogEnabled implements Matcher{
|
||||
|
||||
|
||||
public Map match(String pattern, Map objectModel, Parameters parameters) throws PatternException {
|
||||
String[] statisticsDisplayTypes = parameters.getParameter("type", "").split(",");
|
||||
|
||||
// Are we checking for *NOT* the action or the action.
|
||||
boolean not = false;
|
||||
int action = Constants.READ; // the action to check
|
||||
|
||||
if (pattern.startsWith("!"))
|
||||
{
|
||||
not = true;
|
||||
pattern = pattern.substring(1);
|
||||
}
|
||||
|
||||
if(!pattern.equals("READ"))
|
||||
{
|
||||
getLogger().warn("Invalid action: '"+pattern+"'");
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Context context = ContextUtil.obtainContext(objectModel);
|
||||
DSpaceObject dso = HandleUtil.obtainHandle(objectModel);
|
||||
|
||||
//We have always got rights to view stats on the home page (admin rights will be checked later)
|
||||
boolean authorized = dso == null || AuthorizeManager.authorizeActionBoolean(context, dso, action, false);
|
||||
//Check if (one of our) display type is admin only
|
||||
//If one of the given ones isn't admin only, no need to check !
|
||||
boolean adminCheckNeeded = true;
|
||||
for (String statisticsDisplayType : statisticsDisplayTypes) {
|
||||
//Only usage statics are available on an item level
|
||||
if(!"usage".equals(statisticsDisplayType) && dso != null && dso.getType() == Constants.ITEM){
|
||||
continue;
|
||||
}
|
||||
//If one isn't admin enabled no need to check for admin
|
||||
if(!ConfigurationManager.getBooleanProperty("usage-statistics", "authorization.admin." + statisticsDisplayType, true)){
|
||||
adminCheckNeeded = false;
|
||||
}
|
||||
}
|
||||
|
||||
//If we are authorized check for any other authorization actions present
|
||||
if(authorized && adminCheckNeeded)
|
||||
{
|
||||
//If we have no user, we cannot be admin
|
||||
if(context.getCurrentUser() == null)
|
||||
{
|
||||
authorized = false;
|
||||
}
|
||||
|
||||
if(authorized){
|
||||
//Check for admin
|
||||
authorized = AuthorizeManager.isAdmin(context);
|
||||
if(!authorized)
|
||||
{
|
||||
//Check if we have authorization for the owning colls, comms, ...
|
||||
authorized = AuthorizeManager.isAdmin(context, dso);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// XOR
|
||||
if (not ^ authorized)
|
||||
{
|
||||
return new HashMap();
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
catch (SQLException sqle)
|
||||
{
|
||||
throw new PatternException("Unable to obtain DSpace Context", sqle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* 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.statistics;
|
||||
|
||||
import org.apache.cocoon.ProcessingException;
|
||||
import org.apache.cocoon.environment.ObjectModelHelper;
|
||||
import org.apache.cocoon.environment.Request;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.dspace.app.xmlui.aspect.artifactbrowser.AdvancedSearchUtils;
|
||||
import org.dspace.app.xmlui.cocoon.AbstractDSpaceTransformer;
|
||||
import org.dspace.app.xmlui.utils.HandleUtil;
|
||||
import org.dspace.app.xmlui.utils.UIException;
|
||||
import org.dspace.app.xmlui.wing.WingException;
|
||||
import org.dspace.app.xmlui.wing.element.Body;
|
||||
import org.dspace.app.xmlui.wing.element.Division;
|
||||
import org.dspace.authorize.AuthorizeException;
|
||||
import org.dspace.content.DSpaceObject;
|
||||
import org.dspace.handle.HandleManager;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* Transformer that adds a hidden form
|
||||
* which will be submitted each time an dspace object link is clicked on a lucene search page
|
||||
* This form will ensure that the results clicked after each search are logged for the search statistics
|
||||
*
|
||||
* @author Kevin Van de Velde (kevin at atmire dot com)
|
||||
* @author Ben Bosman (ben at atmire dot com)
|
||||
* @author Mark Diggory (markd at atmire dot com)
|
||||
*/
|
||||
public class StatisticsSearchResultTransformer extends AbstractDSpaceTransformer {
|
||||
|
||||
|
||||
@Override
|
||||
public void addBody(Body body) throws SAXException, WingException, SQLException, IOException, AuthorizeException, ProcessingException {
|
||||
Request request = ObjectModelHelper.getRequest(objectModel);
|
||||
StringBuilder formUrl = new StringBuilder();
|
||||
formUrl.append(request.getContextPath());
|
||||
DSpaceObject scope = getScope();
|
||||
if(scope != null){
|
||||
formUrl.append("/handle/").append(scope.getHandle());
|
||||
}
|
||||
formUrl.append("/dso-display");
|
||||
|
||||
Division mainForm = body.addInteractiveDivision("dso-display", formUrl.toString(), Division.METHOD_POST, "");
|
||||
|
||||
mainForm.addHidden("query").setValue(getQuery());
|
||||
if(!StringUtils.isBlank(request.getParameter("rpp"))){
|
||||
mainForm.addHidden("rpp").setValue(Integer.parseInt(request.getParameter("rpp")));
|
||||
}
|
||||
if(!StringUtils.isBlank(request.getParameter("sort_by"))){
|
||||
mainForm.addHidden("sort_by").setValue(request.getParameter("sort_by"));
|
||||
}
|
||||
if(!StringUtils.isBlank(request.getParameter("order"))){
|
||||
mainForm.addHidden("order").setValue(request.getParameter("order"));
|
||||
}
|
||||
if(!StringUtils.isBlank(request.getParameter("page"))){
|
||||
mainForm.addHidden("page").setValue(Integer.parseInt(request.getParameter("page")));
|
||||
}
|
||||
|
||||
|
||||
//This hidden input will contain the resulting url to which we redirect once our work has been completed
|
||||
mainForm.addHidden("redirectUrl");
|
||||
}
|
||||
|
||||
private String getQuery() throws UIException {
|
||||
Request request = ObjectModelHelper.getRequest(objectModel);
|
||||
if(parameters.getParameterAsBoolean("advanced-search", false)){
|
||||
return AdvancedSearchUtils.buildQuery(AdvancedSearchUtils.getSearchFields(request));
|
||||
}else{
|
||||
String query = decodeFromURL(request.getParameter("query"));
|
||||
if (query == null)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
return query;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the current scope. This may be derived from the current url
|
||||
* handle if present or the scope parameter is given. If no scope is
|
||||
* specified then null is returned.
|
||||
*
|
||||
* @return The current scope.
|
||||
*/
|
||||
protected DSpaceObject getScope() throws SQLException
|
||||
{
|
||||
Request request = ObjectModelHelper.getRequest(objectModel);
|
||||
String scopeString = request.getParameter("scope");
|
||||
|
||||
// Are we in a community or collection?
|
||||
DSpaceObject dso;
|
||||
if (scopeString == null || "".equals(scopeString))
|
||||
{
|
||||
// get the search scope from the url handle
|
||||
dso = HandleUtil.obtainHandle(objectModel);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get the search scope from the location parameter
|
||||
dso = HandleManager.resolveToObject(context, scopeString);
|
||||
}
|
||||
|
||||
return dso;
|
||||
}
|
||||
}
|
@@ -0,0 +1,136 @@
|
||||
/**
|
||||
* 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.statistics;
|
||||
|
||||
import org.apache.cocoon.ProcessingException;
|
||||
import org.apache.cocoon.environment.ObjectModelHelper;
|
||||
import org.apache.cocoon.environment.Request;
|
||||
import org.dspace.app.xmlui.utils.HandleUtil;
|
||||
import org.dspace.app.xmlui.utils.UIException;
|
||||
import org.dspace.app.xmlui.wing.Message;
|
||||
import org.dspace.app.xmlui.wing.WingException;
|
||||
import org.dspace.app.xmlui.wing.element.Body;
|
||||
import org.dspace.app.xmlui.wing.element.Division;
|
||||
import org.dspace.app.xmlui.wing.element.PageMeta;
|
||||
import org.dspace.authorize.AuthorizeException;
|
||||
import org.dspace.content.DSpaceObject;
|
||||
import org.dspace.statistics.content.DatasetSearchGenerator;
|
||||
import org.dspace.statistics.content.StatisticsDataSearches;
|
||||
import org.dspace.statistics.content.StatisticsTable;
|
||||
import org.dspace.statistics.content.filter.StatisticsSolrDateFilter;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* @author Kevin Van de Velde (kevin at atmire dot com)
|
||||
* @author Ben Bosman (ben at atmire dot com)
|
||||
* @author Mark Diggory (markd at atmire dot com)
|
||||
*/
|
||||
public class StatisticsSearchTransformer extends AbstractStatisticsDataTransformer {
|
||||
|
||||
private static final Message T_dspace_home = message("xmlui.general.dspace_home");
|
||||
private static final Message T_search_terms_head = message("xmlui.statistics.StatisticsSearchTransformer.search-terms.head");
|
||||
private static final Message T_search_total_head = message("xmlui.statistics.StatisticsSearchTransformer.search-total.head");
|
||||
private static final Message T_trail = message("xmlui.statistics.trail-search");
|
||||
private static final Message T_head_title = message("xmlui.statistics.search.title");
|
||||
private static final Message T_retrieval_error = message("xmlui.statistics.search.error");
|
||||
private static final Message T_search_head = message("xmlui.statistics.search.head");
|
||||
private static final Message T_search_head_dso = message("xmlui.statistics.search.head-dso");
|
||||
private static final Message T_no_results = message("xmlui.statistics.search.no-results");
|
||||
|
||||
/**
|
||||
* Add a page title and trail links
|
||||
*/
|
||||
public void addPageMeta(PageMeta pageMeta) throws SAXException, WingException, SQLException, IOException, AuthorizeException {
|
||||
//Try to find our dspace object
|
||||
DSpaceObject dso = HandleUtil.obtainHandle(objectModel);
|
||||
|
||||
pageMeta.addTrailLink(contextPath + "/",T_dspace_home);
|
||||
|
||||
if(dso != null)
|
||||
{
|
||||
HandleUtil.buildHandleTrail(dso, pageMeta, contextPath);
|
||||
}
|
||||
pageMeta.addTrailLink(contextPath + (dso != null && dso.getHandle() != null ? "/handle/" + dso.getHandle() : "") + "/search-statistics", T_trail);
|
||||
|
||||
// Add the page title
|
||||
pageMeta.addMetadata("title").addContent(T_head_title);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void addBody(Body body) throws SAXException, WingException, SQLException, IOException, AuthorizeException, ProcessingException {
|
||||
//Try to find our dspace object
|
||||
DSpaceObject dso = HandleUtil.obtainHandle(objectModel);
|
||||
Request request = ObjectModelHelper.getRequest(objectModel);
|
||||
String selectedTimeFilter = request.getParameter("time_filter");
|
||||
|
||||
StringBuilder actionPath = new StringBuilder().append(request.getContextPath());
|
||||
if(dso != null){
|
||||
actionPath.append("/handle/").append(dso.getHandle());
|
||||
}
|
||||
actionPath.append("/search-statistics");
|
||||
|
||||
Division mainDivision = body.addInteractiveDivision("search-statistics", actionPath.toString(), Division.METHOD_POST, null);
|
||||
if(dso != null){
|
||||
mainDivision.setHead(T_search_head_dso.parameterize(dso.getName()));
|
||||
}else{
|
||||
mainDivision.setHead(T_search_head);
|
||||
}
|
||||
try {
|
||||
//Add the time filter box
|
||||
Division searchTermsDivision = mainDivision.addDivision("search-terms");
|
||||
searchTermsDivision.setHead(T_search_terms_head);
|
||||
addTimeFilter(searchTermsDivision);
|
||||
|
||||
//Retrieve the optional time filter
|
||||
StatisticsSolrDateFilter dateFilter = getDateFilter(selectedTimeFilter);
|
||||
|
||||
|
||||
StatisticsTable statisticsTable = new StatisticsTable(new StatisticsDataSearches(dso));
|
||||
|
||||
DatasetSearchGenerator queryGenerator = new DatasetSearchGenerator();
|
||||
queryGenerator.setType("query");
|
||||
queryGenerator.setMax(10);
|
||||
queryGenerator.setMode(DatasetSearchGenerator.Mode.SEARCH_OVERVIEW);
|
||||
queryGenerator.setPercentage(true);
|
||||
queryGenerator.setRetrievePageViews(true);
|
||||
statisticsTable.addDatasetGenerator(queryGenerator);
|
||||
if(dateFilter != null){
|
||||
statisticsTable.addFilter(dateFilter);
|
||||
}
|
||||
|
||||
addDisplayTable(searchTermsDivision, statisticsTable, true, null);
|
||||
|
||||
|
||||
Division totalDivision = mainDivision.addDivision("search-total");
|
||||
totalDivision.setHead(T_search_total_head);
|
||||
statisticsTable = new StatisticsTable(new StatisticsDataSearches(dso));
|
||||
|
||||
queryGenerator = new DatasetSearchGenerator();
|
||||
queryGenerator.setMode(DatasetSearchGenerator.Mode.SEARCH_OVERVIEW_TOTAL);
|
||||
queryGenerator.setPercentage(true);
|
||||
queryGenerator.setRetrievePageViews(true);
|
||||
statisticsTable.addDatasetGenerator(queryGenerator);
|
||||
if(dateFilter != null){
|
||||
statisticsTable.addFilter(dateFilter);
|
||||
}
|
||||
|
||||
addDisplayTable(totalDivision, statisticsTable, false, null);
|
||||
} catch (Exception e) {
|
||||
mainDivision.addPara().addContent(T_retrieval_error);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Message getNoResultsMessage() {
|
||||
return T_no_results;
|
||||
}
|
||||
}
|
@@ -326,7 +326,7 @@ public class StatisticsTransformer extends AbstractDSpaceTransformer {
|
||||
|
||||
if (dataset != null) {
|
||||
|
||||
String[][] matrix = dataset.getMatrixFormatted();
|
||||
String[][] matrix = dataset.getMatrix();
|
||||
|
||||
/** Generate Table */
|
||||
Division wrapper = mainDiv.addDivision("tablewrapper");
|
||||
@@ -339,12 +339,12 @@ public class StatisticsTransformer extends AbstractDSpaceTransformer {
|
||||
|
||||
/** Generate Header Row */
|
||||
Row headerRow = table.addRow();
|
||||
headerRow.addCell("spacer", Cell.ROLE_DATA, "labelcell");
|
||||
headerRow.addCell("spacer", Cell.ROLE_HEADER, "labelcell");
|
||||
|
||||
String[] cLabels = dataset.getColLabels().toArray(new String[0]);
|
||||
for (int row = 0; row < cLabels.length; row++) {
|
||||
Cell cell = headerRow.addCell(0 + "-" + row + "-h",
|
||||
Cell.ROLE_DATA, "labelcell");
|
||||
Cell.ROLE_HEADER, "labelcell");
|
||||
cell.addContent(cLabels[row]);
|
||||
}
|
||||
|
||||
@@ -382,7 +382,7 @@ public class StatisticsTransformer extends AbstractDSpaceTransformer {
|
||||
|
||||
if (dataset != null) {
|
||||
|
||||
String[][] matrix = dataset.getMatrixFormatted();
|
||||
String[][] matrix = dataset.getMatrix();
|
||||
|
||||
// String[] rLabels = dataset.getRowLabels().toArray(new String[0]);
|
||||
|
||||
@@ -395,9 +395,9 @@ public class StatisticsTransformer extends AbstractDSpaceTransformer {
|
||||
|
||||
Row headerRow = table.addRow();
|
||||
|
||||
headerRow.addCell("", Cell.ROLE_DATA, "labelcell");
|
||||
headerRow.addCell("", Cell.ROLE_HEADER, "labelcell");
|
||||
|
||||
headerRow.addCell("", Cell.ROLE_DATA, "labelcell").addContent(message(T_head_visits_views));
|
||||
headerRow.addCell("", Cell.ROLE_HEADER, "labelcell").addContent(message(T_head_visits_views));
|
||||
|
||||
/** Generate Table Body */
|
||||
for (int col = 0; col < matrix[0].length; col++) {
|
||||
|
@@ -0,0 +1,126 @@
|
||||
/**
|
||||
* 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.statistics;
|
||||
|
||||
import org.apache.cocoon.ProcessingException;
|
||||
import org.apache.cocoon.environment.ObjectModelHelper;
|
||||
import org.apache.cocoon.environment.Request;
|
||||
import org.dspace.app.util.Util;
|
||||
import org.dspace.app.xmlui.utils.HandleUtil;
|
||||
import org.dspace.app.xmlui.utils.UIException;
|
||||
import org.dspace.app.xmlui.wing.Message;
|
||||
import org.dspace.app.xmlui.wing.WingException;
|
||||
import org.dspace.app.xmlui.wing.element.*;
|
||||
import org.dspace.authorize.AuthorizeException;
|
||||
import org.dspace.content.DSpaceObject;
|
||||
import org.dspace.statistics.content.DatasetTypeGenerator;
|
||||
import org.dspace.statistics.content.StatisticsDataWorkflow;
|
||||
import org.dspace.statistics.content.StatisticsTable;
|
||||
import org.dspace.statistics.content.filter.StatisticsSolrDateFilter;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* @author Kevin Van de Velde (kevin at atmire dot com)
|
||||
* @author Ben Bosman (ben at atmire dot com)
|
||||
* @author Mark Diggory (markd at atmire dot com)
|
||||
*/
|
||||
public class StatisticsWorkflowTransformer extends AbstractStatisticsDataTransformer {
|
||||
|
||||
private static final Message T_dspace_home = message("xmlui.general.dspace_home");
|
||||
private static final Message T_trail = message("xmlui.statistics.trail-workflow");
|
||||
private static final Message T_head_title = message("xmlui.statistics.workflow.title");
|
||||
private static final Message T_title = message("xmlui.statistics.workflow.title");
|
||||
private static final Message T_retrieval_error = message("xmlui.statistics.workflow.error");
|
||||
private static final Message T_no_results = message("xmlui.statistics.workflow.no-results");
|
||||
private static final Message T_workflow_head = message("xmlui.statistics.workflow.head");
|
||||
private static final Message T_workflow_head_dso = message("xmlui.statistics.workflow.head-dso");
|
||||
|
||||
/**
|
||||
* Add a page title and trail links
|
||||
*/
|
||||
public void addPageMeta(PageMeta pageMeta) throws SAXException, WingException, UIException, SQLException, IOException, AuthorizeException {
|
||||
//Try to find our dspace object
|
||||
DSpaceObject dso = HandleUtil.obtainHandle(objectModel);
|
||||
|
||||
pageMeta.addTrailLink(contextPath + "/",T_dspace_home);
|
||||
|
||||
if(dso != null)
|
||||
{
|
||||
HandleUtil.buildHandleTrail(dso, pageMeta, contextPath);
|
||||
}
|
||||
pageMeta.addTrailLink(contextPath + (dso != null && dso.getHandle() != null ? "/handle/" + dso.getHandle() : "") + "/workflow-statistics", T_trail);
|
||||
|
||||
// Add the page title
|
||||
pageMeta.addMetadata("title").addContent(T_head_title);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void addBody(Body body) throws SAXException, WingException, SQLException, IOException, AuthorizeException, ProcessingException {
|
||||
//Try to find our dspace object
|
||||
DSpaceObject dso = HandleUtil.obtainHandle(objectModel);
|
||||
Request request = ObjectModelHelper.getRequest(objectModel);
|
||||
String selectedTimeFilter = request.getParameter("time_filter");
|
||||
|
||||
|
||||
StringBuilder actionPath = new StringBuilder().append(contextPath);
|
||||
if(dso != null){
|
||||
actionPath.append("/handle/").append(dso.getHandle());
|
||||
}
|
||||
actionPath.append("/workflow-statistics");
|
||||
|
||||
Division mainDivision = body.addInteractiveDivision("workflow-statistics", actionPath.toString(), Division.METHOD_POST, null);
|
||||
if(dso != null){
|
||||
mainDivision.setHead(T_workflow_head_dso.parameterize(dso.getName()));
|
||||
}else{
|
||||
mainDivision.setHead(T_workflow_head);
|
||||
}
|
||||
try {
|
||||
|
||||
//Add the time filter box
|
||||
Division workflowTermsDivision = mainDivision.addDivision("workflow-terms");
|
||||
workflowTermsDivision.setHead(T_title);
|
||||
addTimeFilter(workflowTermsDivision);
|
||||
|
||||
//Retrieve the optional time filter
|
||||
StatisticsSolrDateFilter dateFilter = getDateFilter(selectedTimeFilter);
|
||||
|
||||
|
||||
|
||||
int time_filter = -1;
|
||||
if(request.getParameter("time_filter") != null && !"".equals(request.getParameter("time_filter"))){
|
||||
//Our time filter is a negative value if present
|
||||
time_filter = Math.abs(Util.getIntParameter(request, "time_filter"));
|
||||
|
||||
}
|
||||
StatisticsTable statisticsTable = new StatisticsTable(new StatisticsDataWorkflow(dso, time_filter));
|
||||
|
||||
DatasetTypeGenerator queryGenerator = new DatasetTypeGenerator();
|
||||
//Set our type to previousworkflow step (indicates our performed actions !)
|
||||
queryGenerator.setType("previousWorkflowStep");
|
||||
queryGenerator.setMax(10);
|
||||
statisticsTable.addDatasetGenerator(queryGenerator);
|
||||
if(dateFilter != null){
|
||||
statisticsTable.addFilter(dateFilter);
|
||||
}
|
||||
|
||||
addDisplayTable(workflowTermsDivision, statisticsTable, true, new String[]{"xmlui.statistics.display.table.workflow.step."});
|
||||
} catch (Exception e) {
|
||||
mainDivision.addPara().addContent(T_retrieval_error);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Message getNoResultsMessage() {
|
||||
return T_no_results;
|
||||
}
|
||||
}
|
@@ -0,0 +1,104 @@
|
||||
/**
|
||||
* 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.cocoon;
|
||||
|
||||
import org.apache.avalon.framework.parameters.Parameters;
|
||||
import org.apache.cocoon.acting.AbstractAction;
|
||||
import org.apache.cocoon.environment.ObjectModelHelper;
|
||||
import org.apache.cocoon.environment.Redirector;
|
||||
import org.apache.cocoon.environment.Request;
|
||||
import org.apache.cocoon.environment.SourceResolver;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.dspace.app.xmlui.utils.ContextUtil;
|
||||
import org.dspace.app.xmlui.utils.HandleUtil;
|
||||
import org.dspace.content.DSpaceObject;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.handle.HandleManager;
|
||||
import org.dspace.usage.UsageEvent;
|
||||
import org.dspace.usage.UsageSearchEvent;
|
||||
import org.dspace.utils.DSpace;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Fires an event each time a search occurs
|
||||
*
|
||||
* @author Kevin Van de Velde (kevin at atmire dot com)
|
||||
* @author Ben Bosman (ben at atmire dot com)
|
||||
* @author Mark Diggory (markd at atmire dot com)
|
||||
*/
|
||||
public abstract class SearchLoggerAction extends AbstractAction {
|
||||
|
||||
@Override
|
||||
public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception {
|
||||
Request request = ObjectModelHelper.getRequest(objectModel);
|
||||
Context context = ContextUtil.obtainContext(objectModel);
|
||||
DSpaceObject scope = getScope(context, objectModel);
|
||||
|
||||
UsageSearchEvent searchEvent = new UsageSearchEvent(
|
||||
UsageEvent.Action.SEARCH,
|
||||
request,
|
||||
context,
|
||||
null, getQueries(request), scope);
|
||||
|
||||
if(!StringUtils.isBlank(request.getParameter("rpp"))){
|
||||
searchEvent.setRpp(Integer.parseInt(request.getParameter("rpp")));
|
||||
}
|
||||
if(!StringUtils.isBlank(request.getParameter("sort_by"))){
|
||||
searchEvent.setSortBy(request.getParameter("sort_by"));
|
||||
}
|
||||
if(!StringUtils.isBlank(request.getParameter("order"))){
|
||||
searchEvent.setSortOrder(request.getParameter("order"));
|
||||
}
|
||||
if(!StringUtils.isBlank(request.getParameter("page"))){
|
||||
searchEvent.setPage(Integer.parseInt(request.getParameter("page")));
|
||||
}
|
||||
|
||||
//Fire our event
|
||||
new DSpace().getEventService().fireEvent(searchEvent);
|
||||
|
||||
|
||||
// Finished, allow to pass.
|
||||
return null;
|
||||
}
|
||||
|
||||
protected abstract List<String> getQueries(Request request) throws SQLException;
|
||||
|
||||
|
||||
/**
|
||||
* Determine the current scope. This may be derived from the current url
|
||||
* handle if present or the scope parameter is given. If no scope is
|
||||
* specified then null is returned.
|
||||
*
|
||||
* @return The current scope.
|
||||
*/
|
||||
protected DSpaceObject getScope(Context context, Map objectModel) throws SQLException
|
||||
{
|
||||
Request request = ObjectModelHelper.getRequest(objectModel);
|
||||
String scopeString = request.getParameter("scope");
|
||||
|
||||
// Are we in a community or collection?
|
||||
DSpaceObject dso;
|
||||
if (scopeString == null || "".equals(scopeString))
|
||||
{
|
||||
// get the search scope from the url handle
|
||||
dso = HandleUtil.obtainHandle(objectModel);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get the search scope from the location parameter
|
||||
dso = HandleManager.resolveToObject(context, scopeString);
|
||||
}
|
||||
|
||||
return dso;
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -51,6 +51,10 @@ and searching the repository.
|
||||
<map:selector name="IfModifiedSinceSelector"
|
||||
src="org.dspace.app.xmlui.aspect.general.IfModifiedSinceSelector"/>
|
||||
</map:selectors>
|
||||
<map:actions>
|
||||
<map:action name="SimpleSearchLoggerAction" src="org.dspace.app.xmlui.aspect.artifactbrowser.SimpleSearchLoggerAction" />
|
||||
<map:action name="AdvancedSearchLoggerAction" src="org.dspace.app.xmlui.aspect.artifactbrowser.AdvancedSearchLoggerAction" />
|
||||
</map:actions>
|
||||
|
||||
</map:components>
|
||||
<map:pipelines>
|
||||
@@ -83,16 +87,19 @@ and searching the repository.
|
||||
|
||||
<!-- Search -->
|
||||
<map:match pattern="search">
|
||||
<map:transform type="SimpleSearch"/>
|
||||
<map:serialize type="xml"/>
|
||||
<map:act type="SimpleSearchLoggerAction"/>
|
||||
<map:transform type="SimpleSearch"/>
|
||||
<map:serialize type="xml"/>
|
||||
</map:match>
|
||||
<map:match pattern="simple-search">
|
||||
<map:transform type="SimpleSearch"/>
|
||||
<map:serialize type="xml"/>
|
||||
<map:act type="SimpleSearchLoggerAction"/>
|
||||
<map:transform type="SimpleSearch"/>
|
||||
<map:serialize type="xml"/>
|
||||
</map:match>
|
||||
<map:match pattern="advanced-search">
|
||||
<map:transform type="AdvancedSearch"/>
|
||||
<map:serialize type="xml"/>
|
||||
<map:act type="AdvancedSearchLoggerAction"/>
|
||||
<map:transform type="AdvancedSearch"/>
|
||||
<map:serialize type="xml"/>
|
||||
</map:match>
|
||||
|
||||
|
||||
@@ -119,20 +126,23 @@ and searching the repository.
|
||||
|
||||
<!-- Simple search -->
|
||||
<map:match pattern="handle/*/*/search">
|
||||
<map:transform type="SimpleSearch"/>
|
||||
<map:serialize type="xml"/>
|
||||
<map:act type="SimpleSearchLoggerAction"/>
|
||||
<map:transform type="SimpleSearch"/>
|
||||
<map:serialize type="xml"/>
|
||||
</map:match>
|
||||
|
||||
<!-- Simple search again, but this time they said it -->
|
||||
<map:match pattern="handle/*/*/simple-search">
|
||||
<map:transform type="SimpleSearch"/>
|
||||
<map:serialize type="xml"/>
|
||||
<map:act type="SimpleSearchLoggerAction"/>
|
||||
<map:transform type="SimpleSearch"/>
|
||||
<map:serialize type="xml"/>
|
||||
</map:match>
|
||||
|
||||
<!-- Advanced search -->
|
||||
<map:match pattern="handle/*/*/advanced-search">
|
||||
<map:transform type="AdvancedSearch"/>
|
||||
<map:serialize type="xml"/>
|
||||
<map:act type="AdvancedSearchLoggerAction"/>
|
||||
<map:transform type="AdvancedSearch"/>
|
||||
<map:serialize type="xml"/>
|
||||
</map:match>
|
||||
|
||||
</map:match> <!-- End match community or collection -->
|
||||
|
@@ -1,81 +1,289 @@
|
||||
<?xml version="1.0"?>
|
||||
<!--
|
||||
|
||||
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/
|
||||
|
||||
-->
|
||||
<map:sitemap xmlns:map="http://apache.org/cocoon/sitemap/1.0">
|
||||
<map:components>
|
||||
<map:transformers>
|
||||
<map:transformer name="StatisticsTransformer" src="org.dspace.app.xmlui.aspect.statistics.StatisticsTransformer"/>
|
||||
<map:transformer name="Navigation" src="org.dspace.app.xmlui.aspect.statistics.Navigation"/>
|
||||
<map:transformer name="RestrictedItem" src="org.dspace.app.xmlui.aspect.artifactbrowser.RestrictedItem"/>
|
||||
</map:transformers>
|
||||
<map:matchers default="wildcard">
|
||||
<map:matcher name="HandleTypeMatcher" src="org.dspace.app.xmlui.aspect.general.HandleTypeMatcher"/>
|
||||
<map:matcher name="StatisticsAuthorizedMatcher" src="org.dspace.app.xmlui.aspect.statistics.StatisticsAuthorizedMatcher"/>
|
||||
</map:matchers>
|
||||
<map:selectors>
|
||||
<map:selector name="AuthenticatedSelector" src="org.dspace.app.xmlui.aspect.general.AuthenticatedSelector"/>
|
||||
</map:selectors>
|
||||
</map:components>
|
||||
|
||||
<map:pipelines>
|
||||
<map:pipeline>
|
||||
|
||||
<map:generate/>
|
||||
|
||||
<!--Make sure that the home page also receives statistics-->
|
||||
<map:match pattern="">
|
||||
<map:match type="StatisticsAuthorizedMatcher" pattern="READ">
|
||||
<map:transform type="Navigation"/>
|
||||
</map:match>
|
||||
</map:match>
|
||||
|
||||
<!--Home page statistics-->
|
||||
<map:match pattern="statistics-home">
|
||||
<map:match type="StatisticsAuthorizedMatcher" pattern="READ">
|
||||
<map:transform type="StatisticsTransformer"/>
|
||||
</map:match>
|
||||
</map:match>
|
||||
|
||||
<!--Only show the stats link if we have read rights-->
|
||||
<map:match pattern="handle/*/**">
|
||||
<map:match type="StatisticsAuthorizedMatcher" pattern="READ">
|
||||
<map:transform type="Navigation"/>
|
||||
</map:match>
|
||||
</map:match>
|
||||
|
||||
<!--Match our statistics-->
|
||||
<map:match pattern="handle/*/*/statistics">
|
||||
<map:match type="StatisticsAuthorizedMatcher" pattern="READ">
|
||||
<map:transform type="StatisticsTransformer"/>
|
||||
</map:match>
|
||||
|
||||
<map:match type="StatisticsAuthorizedMatcher" pattern="!READ">
|
||||
<map:select type="AuthenticatedSelector">
|
||||
<map:when test="eperson">
|
||||
<map:transform type="RestrictedItem"/>
|
||||
<map:serialize/>
|
||||
</map:when>
|
||||
<map:otherwise>
|
||||
<map:act type="StartAuthentication">
|
||||
<map:parameter name="header" value="xmlui.ArtifactBrowser.RestrictedItem.auth_header"/>
|
||||
<map:parameter name="message" value="xmlui.ArtifactBrowser.RestrictedItem.auth_message"/>
|
||||
</map:act>
|
||||
<map:serialize/>
|
||||
</map:otherwise>
|
||||
</map:select>
|
||||
</map:match>
|
||||
</map:match>
|
||||
|
||||
|
||||
<map:serialize type="xml"/>
|
||||
|
||||
</map:pipeline>
|
||||
</map:pipelines>
|
||||
</map:sitemap>
|
||||
<?xml version="1.0"?>
|
||||
<!--
|
||||
|
||||
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/
|
||||
|
||||
-->
|
||||
<map:sitemap xmlns:map="http://apache.org/cocoon/sitemap/1.0">
|
||||
<map:components>
|
||||
<map:transformers>
|
||||
<map:transformer name="StatisticsTransformer" src="org.dspace.app.xmlui.aspect.statistics.StatisticsTransformer"/>
|
||||
<map:transformer name="Navigation" src="org.dspace.app.xmlui.aspect.statistics.Navigation"/>
|
||||
<map:transformer name="RestrictedItem" src="org.dspace.app.xmlui.aspect.artifactbrowser.RestrictedItem"/>
|
||||
<map:transformer name="StatisticsSearchResultTransformer" src="org.dspace.app.xmlui.aspect.statistics.StatisticsSearchResultTransformer"/>
|
||||
<map:transformer name="StatisticsSearchTransformer" src="org.dspace.app.xmlui.aspect.statistics.StatisticsSearchTransformer"/>
|
||||
<map:transformer name="StatisticsWorkflowTransformer" src="org.dspace.app.xmlui.aspect.statistics.StatisticsWorkflowTransformer"/>
|
||||
</map:transformers>
|
||||
<map:matchers default="wildcard">
|
||||
<map:matcher name="HandleAuthorizedMatcher" src="org.dspace.app.xmlui.aspect.general.HandleAuthorizedMatcher"/>
|
||||
<map:matcher name="HandleTypeMatcher" src="org.dspace.app.xmlui.aspect.general.HandleTypeMatcher"/>
|
||||
<map:matcher name="StatisticsAuthorizedMatcher" src="org.dspace.app.xmlui.aspect.statistics.StatisticsAuthorizedMatcher"/>
|
||||
</map:matchers>
|
||||
<map:selectors>
|
||||
<map:selector name="AuthenticatedSelector" src="org.dspace.app.xmlui.aspect.general.AuthenticatedSelector"/>
|
||||
</map:selectors>
|
||||
<map:actions>
|
||||
<map:action name="SearchResultLogAction" src="org.dspace.app.xmlui.aspect.statistics.SearchResultLogAction" />
|
||||
</map:actions>
|
||||
</map:components>
|
||||
|
||||
<map:pipelines>
|
||||
<map:pipeline>
|
||||
|
||||
<map:generate/>
|
||||
|
||||
<!--Make sure that the home page also receives statistics-->
|
||||
<map:match pattern="">
|
||||
<map:match type="StatisticsAuthorizedMatcher" pattern="READ">
|
||||
<map:parameter name="type" value="usage,search,workflow"/>
|
||||
|
||||
<map:transform type="Navigation"/>
|
||||
</map:match>
|
||||
</map:match>
|
||||
|
||||
<!--Home page statistics-->
|
||||
<map:match pattern="statistics-home">
|
||||
<map:match type="StatisticsAuthorizedMatcher" pattern="READ">
|
||||
<map:parameter name="type" value="usage"/>
|
||||
|
||||
<map:transform type="StatisticsTransformer"/>
|
||||
<map:transform type="Navigation"/>
|
||||
</map:match>
|
||||
</map:match>
|
||||
|
||||
<map:match pattern="search-statistics">
|
||||
<map:match type="StatisticsAuthorizedMatcher" pattern="READ">
|
||||
<map:parameter name="type" value="search"/>
|
||||
|
||||
<map:transform type="IncludePageMeta">
|
||||
<map:parameter name="javascript.static.statistics#1" value="loadJQuery.js"/>
|
||||
<map:parameter name="javascript.static.statistics#2" value="static/js/usage-statistics.js"/>
|
||||
</map:transform>
|
||||
<map:transform type="StatisticsSearchTransformer"/>
|
||||
<map:transform type="Navigation"/>
|
||||
</map:match>
|
||||
</map:match>
|
||||
|
||||
|
||||
<map:match pattern="workflow-statistics">
|
||||
<map:match type="StatisticsAuthorizedMatcher" pattern="READ">
|
||||
<map:parameter name="type" value="workflow"/>
|
||||
|
||||
<map:transform type="IncludePageMeta">
|
||||
<map:parameter name="javascript.static.statistics#1" value="loadJQuery.js"/>
|
||||
<map:parameter name="javascript.static.statistics#2" value="static/js/usage-statistics.js"/>
|
||||
</map:transform>
|
||||
<map:transform type="StatisticsWorkflowTransformer"/>
|
||||
<map:transform type="Navigation"/>
|
||||
</map:match>
|
||||
</map:match>
|
||||
|
||||
<!--Only show the stats link if we have read rights-->
|
||||
<map:match pattern="handle/*/*">
|
||||
<map:match type="StatisticsAuthorizedMatcher" pattern="READ">
|
||||
<map:parameter name="type" value="usage,search,workflow"/>
|
||||
|
||||
<map:transform type="Navigation"/>
|
||||
</map:match>
|
||||
</map:match>
|
||||
|
||||
<!--Match our statistics-->
|
||||
<map:match pattern="handle/*/*/statistics">
|
||||
<map:match type="StatisticsAuthorizedMatcher" pattern="READ">
|
||||
<map:parameter name="type" value="usage"/>
|
||||
|
||||
<map:transform type="StatisticsTransformer"/>
|
||||
<map:transform type="Navigation"/>
|
||||
</map:match>
|
||||
|
||||
<map:match type="StatisticsAuthorizedMatcher" pattern="!READ">
|
||||
<map:parameter name="type" value="usage"/>
|
||||
|
||||
<map:select type="AuthenticatedSelector">
|
||||
<map:when test="eperson">
|
||||
<map:transform type="RestrictedItem"/>
|
||||
<map:serialize/>
|
||||
</map:when>
|
||||
<map:otherwise>
|
||||
<map:act type="StartAuthentication">
|
||||
<map:parameter name="header" value="xmlui.ArtifactBrowser.RestrictedItem.auth_header"/>
|
||||
<map:parameter name="message" value="xmlui.ArtifactBrowser.RestrictedItem.auth_message"/>
|
||||
</map:act>
|
||||
<map:serialize/>
|
||||
</map:otherwise>
|
||||
</map:select>
|
||||
</map:match>
|
||||
</map:match>
|
||||
|
||||
<!--Match for search statistics-->
|
||||
<map:match pattern="handle/*/*/search-statistics">
|
||||
<map:match type="StatisticsAuthorizedMatcher" pattern="READ">
|
||||
<map:parameter name="type" value="search"/>
|
||||
|
||||
<map:transform type="IncludePageMeta">
|
||||
<map:parameter name="javascript.static.statistics#1" value="loadJQuery.js"/>
|
||||
<map:parameter name="javascript.static.statistics#2" value="static/js/usage-statistics.js"/>
|
||||
</map:transform>
|
||||
<map:transform type="StatisticsSearchTransformer"/>
|
||||
<map:transform type="Navigation"/>
|
||||
</map:match>
|
||||
|
||||
<map:match type="StatisticsAuthorizedMatcher" pattern="!READ">
|
||||
<map:parameter name="type" value="search"/>
|
||||
|
||||
<map:select type="AuthenticatedSelector">
|
||||
<map:when test="eperson">
|
||||
<map:transform type="RestrictedItem"/>
|
||||
<map:serialize/>
|
||||
</map:when>
|
||||
<map:otherwise>
|
||||
<map:act type="StartAuthentication">
|
||||
<map:parameter name="header" value="xmlui.ArtifactBrowser.RestrictedItem.auth_header"/>
|
||||
<map:parameter name="message" value="xmlui.ArtifactBrowser.RestrictedItem.auth_message"/>
|
||||
</map:act>
|
||||
<map:serialize/>
|
||||
</map:otherwise>
|
||||
</map:select>
|
||||
</map:match>
|
||||
</map:match>
|
||||
|
||||
|
||||
<map:match pattern="handle/*/*/workflow-statistics">
|
||||
<map:match type="StatisticsAuthorizedMatcher" pattern="READ">
|
||||
<map:parameter name="type" value="workflow"/>
|
||||
|
||||
<map:transform type="IncludePageMeta">
|
||||
<map:parameter name="javascript.static.statistics#1" value="loadJQuery.js"/>
|
||||
<map:parameter name="javascript.static.statistics#2" value="static/js/usage-statistics.js"/>
|
||||
</map:transform>
|
||||
<map:transform type="StatisticsWorkflowTransformer"/>
|
||||
<map:transform type="Navigation"/>
|
||||
</map:match>
|
||||
|
||||
<map:match type="StatisticsAuthorizedMatcher" pattern="!READ">
|
||||
<map:parameter name="type" value="workflow"/>
|
||||
|
||||
<map:select type="AuthenticatedSelector">
|
||||
<map:when test="eperson">
|
||||
<map:transform type="RestrictedItem"/>
|
||||
<map:serialize/>
|
||||
</map:when>
|
||||
<map:otherwise>
|
||||
<map:act type="StartAuthentication">
|
||||
<map:parameter name="header" value="xmlui.ArtifactBrowser.RestrictedItem.auth_header"/>
|
||||
<map:parameter name="message" value="xmlui.ArtifactBrowser.RestrictedItem.auth_message"/>
|
||||
</map:act>
|
||||
<map:serialize/>
|
||||
</map:otherwise>
|
||||
</map:select>
|
||||
</map:match>
|
||||
</map:match>
|
||||
|
||||
<map:match pattern="dso-display">
|
||||
<map:act type="SearchResultLogAction"/>
|
||||
</map:match>
|
||||
|
||||
|
||||
|
||||
<map:match pattern="search">
|
||||
<map:transform type="IncludePageMeta">
|
||||
<map:parameter name="javascript.static.statistics#1" value="loadJQuery.js"/>
|
||||
<map:parameter name="javascript.static.statistics#2" value="static/js/search-results.js"/>
|
||||
</map:transform>
|
||||
<map:transform type="StatisticsSearchResultTransformer">
|
||||
<map:parameter name="advanced-search" value="false"/>
|
||||
</map:transform>
|
||||
</map:match>
|
||||
|
||||
<map:match pattern="simple-search">
|
||||
<map:transform type="IncludePageMeta">
|
||||
<map:parameter name="javascript.static.statistics#1" value="loadJQuery.js"/>
|
||||
<map:parameter name="javascript.static.statistics#2" value="static/js/search-results.js"/>
|
||||
</map:transform>
|
||||
<map:transform type="StatisticsSearchResultTransformer">
|
||||
<map:parameter name="advanced-search" value="false"/>
|
||||
</map:transform>
|
||||
</map:match>
|
||||
|
||||
<map:match pattern="advanced-search">
|
||||
<map:transform type="IncludePageMeta">
|
||||
<map:parameter name="javascript.static.statistics#1" value="loadJQuery.js"/>
|
||||
<map:parameter name="javascript.static.statistics#2" value="static/js/search-results.js"/>
|
||||
</map:transform>
|
||||
<map:transform type="StatisticsSearchResultTransformer">
|
||||
<map:parameter name="advanced-search" value="true"/>
|
||||
</map:transform>
|
||||
</map:match>
|
||||
|
||||
<map:match pattern="discover">
|
||||
<map:transform type="IncludePageMeta">
|
||||
<map:parameter name="javascript.static.statistics#100" value="static/js/discovery/discovery-results.js"/>
|
||||
</map:transform>
|
||||
</map:match>
|
||||
|
||||
<!-- Handle specific features -->
|
||||
<map:match pattern="handle/*/**">
|
||||
<!-- Scoped browse by features -->
|
||||
<map:match type="HandleAuthorizedMatcher" pattern="READ">
|
||||
<map:match type="HandleTypeMatcher" pattern="community,collection">
|
||||
|
||||
<map:match pattern="handle/*/*/dso-display">
|
||||
<map:act type="SearchResultLogAction"/>
|
||||
</map:match>
|
||||
|
||||
<!-- Simple search -->
|
||||
<map:match pattern="handle/*/*/search">
|
||||
<map:transform type="IncludePageMeta">
|
||||
<map:parameter name="javascript.static.statistics#1" value="loadJQuery.js"/>
|
||||
<map:parameter name="javascript.static.statistics#2" value="static/js/search-results.js"/>
|
||||
</map:transform>
|
||||
<map:transform type="StatisticsSearchResultTransformer">
|
||||
<map:parameter name="advanced-search" value="false"/>
|
||||
</map:transform>
|
||||
</map:match>
|
||||
|
||||
<!-- Simple search again, but this time they said it -->
|
||||
<map:match pattern="handle/*/*/simple-search">
|
||||
<map:transform type="IncludePageMeta">
|
||||
<map:parameter name="javascript.static.statistics#1" value="loadJQuery.js"/>
|
||||
<map:parameter name="javascript.static.statistics#2" value="static/js/search-results.js"/>
|
||||
</map:transform>
|
||||
<map:transform type="StatisticsSearchResultTransformer">
|
||||
<map:parameter name="advanced-search" value="false"/>
|
||||
</map:transform>
|
||||
</map:match>
|
||||
|
||||
<!-- Advanced search -->
|
||||
<map:match pattern="handle/*/*/advanced-search">
|
||||
<map:transform type="IncludePageMeta">
|
||||
<map:parameter name="javascript.static.statistics#1" value="loadJQuery.js"/>
|
||||
<map:parameter name="javascript.static.statistics#2" value="static/js/search-results.js"/>
|
||||
</map:transform>
|
||||
<map:transform type="StatisticsSearchResultTransformer">
|
||||
<map:parameter name="advanced-search" value="true"/>
|
||||
</map:transform>
|
||||
</map:match>
|
||||
|
||||
<!--Optional discovery statistics-->
|
||||
<map:match pattern="handle/*/*/discover">
|
||||
<map:transform type="IncludePageMeta">
|
||||
<map:parameter name="javascript.static.statistics#100" value="static/js/discovery/discovery-results.js"/>
|
||||
</map:transform>
|
||||
</map:match>
|
||||
</map:match>
|
||||
<!-- End match community or collection -->
|
||||
</map:match>
|
||||
<!-- End match can read -->
|
||||
</map:match>
|
||||
<!-- End match handle/*/** -->
|
||||
|
||||
|
||||
<map:serialize type="xml"/>
|
||||
|
||||
</map:pipeline>
|
||||
</map:pipelines>
|
||||
</map:sitemap>
|
||||
|
@@ -1898,6 +1898,12 @@
|
||||
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!-->
|
||||
<message key="xmlui.statistics.title">Statistics</message>
|
||||
<message key="xmlui.statistics.search.title">Search Statistics</message>
|
||||
<message key="xmlui.statistics.search.head">Search Statistics</message>
|
||||
<message key="xmlui.statistics.search.head-dso">Search Statistics for {0}</message>
|
||||
<message key="xmlui.statistics.search.error">There was an error while generating the search statistics, please try again later.</message>
|
||||
<message key="xmlui.statistics.search.no-results">No search statistics available for the selected period.</message>
|
||||
<message key="xmlui.statistics.workflow.title">Workflow Statistics</message>
|
||||
<message key="xmlui.statistics.visits.total">Total Visits</message>
|
||||
<message key="xmlui.statistics.visits.month">Total Visits Per Month</message>
|
||||
<message key="xmlui.statistics.visits.views">Views</message>
|
||||
@@ -1905,8 +1911,53 @@
|
||||
<message key="xmlui.statistics.visits.cities">Top cities views</message>
|
||||
<message key="xmlui.statistics.visits.bitstreams">File Visits</message>
|
||||
<message key="xmlui.statistics.Navigation.title">Statistics</message>
|
||||
<message key="xmlui.statistics.Navigation.view">View Statistics</message>
|
||||
<message key="xmlui.statistics.Navigation.usage.view">View Usage Statistics</message>
|
||||
<message key="xmlui.statistics.Navigation.search.view">View Search Statistics</message>
|
||||
<message key="xmlui.statistics.Navigation.workflow.view">View Workflow Statistics</message>
|
||||
<message key="xmlui.statistics.trail">Statistics</message>
|
||||
<message key="xmlui.statistics.trail-search">Search Statistics</message>
|
||||
<message key="xmlui.statistics.trail-workflow">Workflow Statistics</message>
|
||||
<message key="xmlui.statistics.workflow.no-results">No workflow statistics available for the selected period.</message>
|
||||
<message key="xmlui.statistics.workflow.error">There was an error while generating the workflow statistics, please try again later.</message>
|
||||
<message key="xmlui.statistics.workflow.head">Workflow Statistics</message>
|
||||
<message key="xmlui.statistics.workflow.head-dso">Workflow Statistics for {0}</message>
|
||||
|
||||
|
||||
<message key="xmlui.statistics.StatisticsSearchTransformer.search-terms.head">Top Search Terms</message>
|
||||
<message key="xmlui.statistics.StatisticsSearchTransformer.search-total.head">Total</message>
|
||||
<message key="xmlui.statistics.StatisticsSearchTransformer.time-filter.last-month">Previous month</message>
|
||||
<message key="xmlui.statistics.StatisticsSearchTransformer.time-filter.last-6-months">Previous 6 months</message>
|
||||
<message key="xmlui.statistics.StatisticsSearchTransformer.time-filter.last-year">Previous year</message>
|
||||
<message key="xmlui.statistics.StatisticsSearchTransformer.time-filter.overall">Overall</message>
|
||||
|
||||
|
||||
<message key="xmlui.statistics.display.table.column-label.search-terms">Search Term</message>
|
||||
<message key="xmlui.statistics.display.table.column-label.searches">Searches</message>
|
||||
<message key="xmlui.statistics.display.table.column-label.percent-total">% of Total</message>
|
||||
<message key="xmlui.statistics.display.table.column-label.views-search">Pageviews / Search</message>
|
||||
<message key="xmlui.statistics.display.table.column-label.step">Step</message>
|
||||
<message key="xmlui.statistics.display.table.column-label.performed">Performed</message>
|
||||
<message key="xmlui.statistics.display.table.column-label.average">Average</message>
|
||||
<message key="xmlui.statistics.display.table.workflow.step.STEP1POOL">Accept/Reject Step Pool</message>
|
||||
<message key="xmlui.statistics.display.table.workflow.step.STEP1">Accept/Reject Step</message>
|
||||
<message key="xmlui.statistics.display.table.workflow.step.STEP2POOL">Accept/Reject/Edit Metadata Step Pool</message>
|
||||
<message key="xmlui.statistics.display.table.workflow.step.STEP2">Accept/Reject/Edit Metadata Step</message>
|
||||
<message key="xmlui.statistics.display.table.workflow.step.STEP3POOL">Edit Metadata Step Pool</message>
|
||||
<message key="xmlui.statistics.display.table.workflow.step.STEP3">Edit Metadata Step</message>
|
||||
<message key="xmlui.statistics.display.table.workflow.step.default.reviewstep.claimaction">Accept/Reject Step Pool</message>
|
||||
<message key="xmlui.statistics.display.table.workflow.step.default.reviewstep.reviewaction">Accept/Reject Step</message>
|
||||
<message key="xmlui.statistics.display.table.workflow.step.default.editstep.claimaction">Accept/Reject/Edit Metadata Step Pool</message>
|
||||
<message key="xmlui.statistics.display.table.workflow.step.default.editstep.editaction">Accept/Reject/Edit Metadata Step</message>
|
||||
<message key="xmlui.statistics.display.table.workflow.step.default.finaleditstep.claimaction">Edit Metadata Step Pool</message>
|
||||
<message key="xmlui.statistics.display.table.workflow.step.default.finaleditstep.finaleditaction">Edit Metadata Step</message>
|
||||
<message key="xmlui.statistics.display.table.workflow.step.default.finaleditstep.finaleditaction">Edit Metadata Step</message>
|
||||
<message key="xmlui.statistics.display.table.workflow.step.scoreReview.scoreReviewStep.claimaction">Score Review Pool</message>
|
||||
<message key="xmlui.statistics.display.table.workflow.step.scoreReview.scoreReviewStep.scorereviewaction">Score Review</message>
|
||||
<message key="xmlui.statistics.display.table.workflow.step.scoreReview.evaluationStep.evaluationaction">Score Review Evaluation</message>
|
||||
<message key="xmlui.statistics.display.table.workflow.step.scoreReview.evaluationStep.noUserSelectionAction">Score Review Evaluation Configuration</message>
|
||||
<message key="xmlui.statistics.display.table.workflow.step.selectSingleReviewer.selectReviewerStep.claimaction">Single User Review Pool</message>
|
||||
<message key="xmlui.statistics.display.table.workflow.step.selectSingleReviewer.singleUserReviewStep.autoassignAction">Single User Auto Assign Action</message>
|
||||
<message key="xmlui.statistics.display.table.workflow.step.selectSingleReviewer.singleUserReviewStep.singleuserreviewaction">Single User Review Action</message>
|
||||
|
||||
|
||||
|
||||
|
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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/
|
||||
*/
|
||||
(function ($) {
|
||||
|
||||
/**
|
||||
* Function ensures that all the links clicked in our results pass through the internal logging mechanism
|
||||
*/
|
||||
$(document).ready(function() {
|
||||
|
||||
|
||||
//Retrieve all links with handles attached (comm/coll/item links)
|
||||
var urls = $('div#aspect_artifactbrowser_SimpleSearch_div_search-results,' +
|
||||
'div#aspect_artifactbrowser_AdvancedSearch_div_search-results').find('a');
|
||||
|
||||
urls.click(function(){
|
||||
var $this = $(this);
|
||||
//Instead of redirecting us to the page, first send us to the statistics logger
|
||||
//By doing this we ensure that we register the query to the result
|
||||
var form = $('form#aspect_statistics_StatisticsSearchResultTransformer_div_dso-display');
|
||||
form.find('input[name="redirectUrl"]').val($this.attr('href'));
|
||||
form.submit();
|
||||
return false;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
})(jQuery);
|
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* 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/
|
||||
*/
|
||||
(function ($) {
|
||||
|
||||
/**
|
||||
* Function ensures that when a new time filter is selected the form is submitted
|
||||
*/
|
||||
$(document).ready(function() {
|
||||
$('select[name="time_filter"]').change(function(){
|
||||
$(this).parents('form:first').submit();
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
})(jQuery);
|
@@ -1237,6 +1237,20 @@ div.vocabulary-container li.error{
|
||||
border: 1px solid lightgray;
|
||||
}
|
||||
|
||||
table.detailtable {
|
||||
background-color: #D8E8EB;
|
||||
}
|
||||
|
||||
table.detailtable th{
|
||||
background-color: #F0F2F5;
|
||||
word-wrap: normal;
|
||||
}
|
||||
|
||||
table.detailtable td{
|
||||
background-color: #FFFFFF;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
|
||||
span.highlight{
|
||||
font-weight: bold;
|
||||
|
@@ -13,7 +13,12 @@ resolver.timeout = 200
|
||||
# view the statistics.
|
||||
# If disabled, anyone with READ permissions on the DSpaceObject will be able
|
||||
# to view the statistics.
|
||||
authorization.admin=true
|
||||
#View/download statistics
|
||||
authorization.admin.usage=true
|
||||
#Search/search result statistics
|
||||
authorization.admin.search=true
|
||||
#Workflow result statistics
|
||||
authorization.admin.workflow=true
|
||||
|
||||
# Enable/disable logging of spiders in solr statistics.
|
||||
# If false, and IP matches an address in spiderips.urls, event is not logged.
|
||||
|
@@ -21,6 +21,27 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-war-plugin</artifactId>
|
||||
<!--Upgraded version required since we need to use the exclude option !-->
|
||||
<version>2.1-alpha-2</version>
|
||||
<configuration>
|
||||
<!-- This 'dspace-solr' WAR overlays the Apache Solr Web Application
|
||||
available in Maven Central -->
|
||||
<overlays>
|
||||
<overlay>
|
||||
<groupId>org.apache.solr</groupId>
|
||||
<artifactId>solr</artifactId>
|
||||
<!--
|
||||
Exclude the solr core named apache-solr-core, this is needed because the dspace-solr changes
|
||||
need to take precedence over the solr-core, the solr-core will still be loaded in the solr-core.jar
|
||||
-->
|
||||
<excludes>
|
||||
<exclude>WEB-INF/lib/apache-solr-core-3.5.0.jar</exclude>
|
||||
<!--Also ensure we use the DSpace solr web.xml file else our localhost filter will not work !-->
|
||||
<exclude>WEB-INF/web.xml</exclude>
|
||||
</excludes>
|
||||
</overlay>
|
||||
</overlays>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>prepare-package</phase>
|
||||
@@ -33,7 +54,7 @@
|
||||
<dependency>
|
||||
<groupId>org.dspace</groupId>
|
||||
<artifactId>dspace-solr</artifactId>
|
||||
<version>3.3.0.0</version>
|
||||
<version>3.5.0.1</version>
|
||||
<classifier>skinny</classifier>
|
||||
<type>war</type>
|
||||
</dependency>
|
||||
@@ -41,24 +62,11 @@
|
||||
<dependency>
|
||||
<groupId>org.dspace</groupId>
|
||||
<artifactId>dspace-solr</artifactId>
|
||||
<version>3.3.0.0</version>
|
||||
<version>3.5.0.1</version>
|
||||
<classifier>classes</classifier>
|
||||
<type>jar</type>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>1.5.6</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-jdk14</artifactId>
|
||||
<version>1.5.6</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- support odd cases where JAXP cannot be found in JVM -->
|
||||
<dependency>
|
||||
<groupId>xalan</groupId>
|
||||
|
@@ -32,6 +32,15 @@
|
||||
-->
|
||||
<abortOnConfigurationError>${solr.abortOnConfigurationError:true}</abortOnConfigurationError>
|
||||
|
||||
|
||||
<!-- Controls what version of Lucene various components of Solr
|
||||
adhere to. Generally, you want to use the latest version to
|
||||
get all bug fixes and improvements. It is highly recommended
|
||||
that you fully re-index after changing this setting as it can
|
||||
affect both how text is indexed and queried.
|
||||
-->
|
||||
<luceneMatchVersion>LUCENE_35</luceneMatchVersion>
|
||||
|
||||
<!-- lib directives can be used to instruct Solr to load an Jars identified
|
||||
and use them to resolve any "plugins" specified in your solrconfig.xml or
|
||||
schema.xml (ie: Analyzers, Request Handlers, etc...).
|
||||
@@ -47,14 +56,21 @@
|
||||
classpath, this is useful for including all jars in a directory.
|
||||
-->
|
||||
<lib dir="../../contrib/extraction/lib" />
|
||||
<!-- When a regex is specified in addition to a directory, only the files in that
|
||||
directory which completely match the regex (anchored on both ends)
|
||||
will be included.
|
||||
<lib dir="../../contrib/clustering/lib/" />
|
||||
<lib dir="../../contrib/velocity/lib" />
|
||||
|
||||
<!-- When a regex is specified in addition to a directory, only the
|
||||
files in that directory which completely match the regex
|
||||
(anchored on both ends) will be included.
|
||||
-->
|
||||
<lib dir="../../dist/" regex="apache-solr-cell-\d.*\.jar" />
|
||||
<lib dir="../../dist/" regex="apache-solr-clustering-\d.*\.jar" />
|
||||
<!-- If a dir option (with or without a regex) is used and nothing is found
|
||||
that matches, it will be ignored
|
||||
<lib dir="../../dist/" regex="apache-solr-dataimporthandler-\d.*\.jar" />
|
||||
<lib dir="../../dist/" regex="apache-solr-langid-\d.*\.jar" />
|
||||
<lib dir="../../dist/" regex="apache-solr-velocity-\d.*\.jar" />
|
||||
|
||||
<!-- If a dir option (with or without a regex) is used and nothing
|
||||
is found that matches, it will be ignored
|
||||
-->
|
||||
<lib dir="../../contrib/clustering/lib/downloads/" />
|
||||
<lib dir="../../contrib/clustering/lib/" />
|
||||
@@ -71,9 +87,29 @@
|
||||
<!--<dataDir>${solr.data.dir:./solr/data}</dataDir>-->
|
||||
|
||||
|
||||
<!-- WARNING: this <indexDefaults> section only provides defaults for index writers
|
||||
in general. See also the <mainIndex> section after that when changing parameters
|
||||
for Solr's main Lucene index. -->
|
||||
<!-- The DirectoryFactory to use for indexes.
|
||||
|
||||
solr.StandardDirectoryFactory, the default, is filesystem
|
||||
based and tries to pick the best implementation for the current
|
||||
JVM and platform. One can force a particular implementation
|
||||
via solr.MMapDirectoryFactory, solr.NIOFSDirectoryFactory, or
|
||||
solr.SimpleFSDirectoryFactory.
|
||||
|
||||
solr.RAMDirectoryFactory is memory based, not
|
||||
persistent, and doesn't work with replication.
|
||||
-->
|
||||
<directoryFactory name="DirectoryFactory"
|
||||
class="${solr.directoryFactory:solr.StandardDirectoryFactory}"/>
|
||||
|
||||
|
||||
<!-- Index Defaults
|
||||
|
||||
Values here affect all index writers and act as a default
|
||||
unless overridden.
|
||||
|
||||
WARNING: See also the <mainIndex> section below for parameters
|
||||
that overfor Solr's main Lucene index.
|
||||
-->
|
||||
<indexDefaults>
|
||||
<!-- Values here affect all index writers and act as a default unless overridden. -->
|
||||
<useCompoundFile>false</useCompoundFile>
|
||||
@@ -87,74 +123,86 @@
|
||||
for buffering added documents and deletions before they are
|
||||
flushed to the Directory. -->
|
||||
<ramBufferSizeMB>32</ramBufferSizeMB>
|
||||
<!-- <maxMergeDocs>2147483647</maxMergeDocs> -->
|
||||
<!-- If both ramBufferSizeMB and maxBufferedDocs is set, then
|
||||
Lucene will flush based on whichever limit is hit first.
|
||||
-->
|
||||
<!-- <maxBufferedDocs>1000</maxBufferedDocs> -->
|
||||
|
||||
<maxFieldLength>10000</maxFieldLength>
|
||||
<writeLockTimeout>1000</writeLockTimeout>
|
||||
<commitLockTimeout>10000</commitLockTimeout>
|
||||
|
||||
<!--
|
||||
Expert: Turn on Lucene's auto commit capability. This causes intermediate
|
||||
segment flushes to write a new lucene index descriptor, enabling it to be
|
||||
opened by an external IndexReader. This can greatly slow down indexing
|
||||
speed. NOTE: Despite the name, this value does not have any relation to
|
||||
Solr's autoCommit functionality
|
||||
<!-- Expert: Merge Policy
|
||||
|
||||
The Merge Policy in Lucene controls how merging is handled by
|
||||
Lucene. The default in Solr 3.3 is TieredMergePolicy.
|
||||
|
||||
The default in 2.3 was the LogByteSizeMergePolicy,
|
||||
previous versions used LogDocMergePolicy.
|
||||
|
||||
LogByteSizeMergePolicy chooses segments to merge based on
|
||||
their size. The Lucene 2.2 default, LogDocMergePolicy chose
|
||||
when to merge based on number of documents
|
||||
|
||||
Other implementations of MergePolicy must have a no-argument
|
||||
constructor
|
||||
-->
|
||||
<!--<luceneAutoCommit>false</luceneAutoCommit>-->
|
||||
|
||||
<!--
|
||||
Expert: The Merge Policy in Lucene controls how merging is handled by
|
||||
Lucene. The default in 2.3 is the LogByteSizeMergePolicy, previous
|
||||
versions used LogDocMergePolicy.
|
||||
|
||||
LogByteSizeMergePolicy chooses segments to merge based on their size. The
|
||||
Lucene 2.2 default, LogDocMergePolicy chose when to merge based on number
|
||||
of documents
|
||||
|
||||
Other implementations of MergePolicy must have a no-argument constructor
|
||||
<mergePolicy class="org.apache.lucene.index.TieredMergePolicy"/>
|
||||
-->
|
||||
<!--<mergePolicy class="org.apache.lucene.index.LogByteSizeMergePolicy"/>-->
|
||||
|
||||
<!--
|
||||
Expert:
|
||||
The Merge Scheduler in Lucene controls how merges are performed. The
|
||||
ConcurrentMergeScheduler (Lucene 2.3 default) can perform merges in the
|
||||
background using separate threads. The SerialMergeScheduler (Lucene 2.2
|
||||
default) does not.
|
||||
<!-- Expert: Merge Scheduler
|
||||
|
||||
The Merge Scheduler in Lucene controls how merges are
|
||||
performed. The ConcurrentMergeScheduler (Lucene 2.3 default)
|
||||
can perform merges in the background using separate threads.
|
||||
The SerialMergeScheduler (Lucene 2.2 default) does not.
|
||||
-->
|
||||
<!--<mergeScheduler class="org.apache.lucene.index.ConcurrentMergeScheduler"/>-->
|
||||
|
||||
|
||||
<!--
|
||||
This option specifies which Lucene LockFactory implementation to use.
|
||||
<mergeScheduler class="org.apache.lucene.index.ConcurrentMergeScheduler"/>
|
||||
-->
|
||||
|
||||
single = SingleInstanceLockFactory - suggested for a read-only index
|
||||
or when there is no possibility of another process trying
|
||||
to modify the index.
|
||||
native = NativeFSLockFactory - uses OS native file locking
|
||||
<!-- LockFactory
|
||||
|
||||
This option specifies which Lucene LockFactory implementation
|
||||
to use.
|
||||
|
||||
single = SingleInstanceLockFactory - suggested for a
|
||||
read-only index or when there is no possibility of
|
||||
another process trying to modify the index.
|
||||
native = NativeFSLockFactory - uses OS native file locking.
|
||||
Do not use when multiple solr webapps in the same
|
||||
JVM are attempting to share a single index.
|
||||
simple = SimpleFSLockFactory - uses a plain file for locking
|
||||
|
||||
(For backwards compatibility with Solr 1.2, 'simple' is the default
|
||||
if not specified.)
|
||||
|
||||
More details on the nuances of each LockFactory...
|
||||
http://wiki.apache.org/lucene-java/AvailableLockFactories
|
||||
-->
|
||||
<lockType>native</lockType>
|
||||
<!--
|
||||
Expert:
|
||||
Controls how often Lucene loads terms into memory -->
|
||||
|
||||
<!-- Expert: Controls how often Lucene loads terms into memory
|
||||
Default is 128 and is likely good for most everyone.
|
||||
-->
|
||||
<!--<termIndexInterval>256</termIndexInterval>-->
|
||||
</indexDefaults>
|
||||
|
||||
<!-- Main Index
|
||||
|
||||
Values here override the values in the <indexDefaults> section
|
||||
for the main on disk index.
|
||||
-->
|
||||
<mainIndex>
|
||||
<!-- options specific to the main on-disk lucene index -->
|
||||
<useCompoundFile>false</useCompoundFile>
|
||||
<ramBufferSizeMB>32</ramBufferSizeMB>
|
||||
<mergeFactor>10</mergeFactor>
|
||||
<!-- Deprecated -->
|
||||
<!--<maxBufferedDocs>1000</maxBufferedDocs>-->
|
||||
<!--<maxMergeDocs>2147483647</maxMergeDocs>-->
|
||||
|
||||
<!-- inherit from indexDefaults <maxFieldLength>10000</maxFieldLength> -->
|
||||
<!-- Unlock On Startup
|
||||
|
||||
<!-- If true, unlock any held write or commit locks on startup.
|
||||
If true, unlock any held write or commit locks on startup.
|
||||
This defeats the locking mechanism that allows multiple
|
||||
processes to safely access a lucene index, and should be
|
||||
used with care.
|
||||
@@ -166,16 +214,12 @@
|
||||
of closed and then opened. -->
|
||||
<reopenReaders>true</reopenReaders>
|
||||
|
||||
<!--
|
||||
Expert:
|
||||
Controls how often Lucene loads terms into memory. Default is 128 and is likely good for most everyone. -->
|
||||
<!--<termIndexInterval>256</termIndexInterval>-->
|
||||
<!-- Commit Deletion Policy
|
||||
|
||||
<!--
|
||||
Custom deletion policies can specified here. The class must
|
||||
implement org.apache.lucene.index.IndexDeletionPolicy.
|
||||
|
||||
http://lucene.apache.org/java/2_3_2/api/org/apache/lucene/index/IndexDeletionPolicy.html
|
||||
http://lucene.apache.org/java/2_9_1/api/all/org/apache/lucene/index/IndexDeletionPolicy.html
|
||||
|
||||
The standard Solr IndexDeletionPolicy implementation supports deleting
|
||||
index commit points on number of commits, age of commit point and
|
||||
@@ -198,39 +242,55 @@
|
||||
-->
|
||||
</deletionPolicy>
|
||||
|
||||
<!-- To aid in advanced debugging, you may turn on IndexWriter debug logging.
|
||||
Setting to true will set the file that the underlying Lucene IndexWriter
|
||||
will write its debug infostream to. -->
|
||||
<!-- Lucene Infostream
|
||||
|
||||
To aid in advanced debugging, Lucene provides an "InfoStream"
|
||||
of detailed information when indexing.
|
||||
|
||||
Setting The value to true will instruct the underlying Lucene
|
||||
IndexWriter to write its debugging info the specified file
|
||||
-->
|
||||
<infoStream file="INFOSTREAM.txt">false</infoStream>
|
||||
|
||||
</mainIndex>
|
||||
|
||||
<!-- Enables JMX if and only if an existing MBeanServer is found, use this
|
||||
if you want to configure JMX through JVM parameters. Remove this to disable
|
||||
exposing Solr configuration and statistics to JMX.
|
||||
<!-- JMX
|
||||
|
||||
If you want to connect to a particular server, specify the agentId
|
||||
e.g. <jmx agentId="myAgent" />
|
||||
|
||||
If you want to start a new MBeanServer, specify the serviceUrl
|
||||
e.g <jmx serviceUrl="service:jmx:rmi:///jndi/rmi://localhost:9999/solr"/>
|
||||
This example enables JMX if and only if an existing MBeanServer
|
||||
is found, use this if you want to configure JMX through JVM
|
||||
parameters. Remove this to disable exposing Solr configuration
|
||||
and statistics to JMX.
|
||||
|
||||
For more details see http://wiki.apache.org/solr/SolrJmx
|
||||
-->
|
||||
<jmx />
|
||||
<!-- If you want to connect to a particular server, specify the
|
||||
agentId
|
||||
-->
|
||||
<!-- <jmx agentId="myAgent" /> -->
|
||||
<!-- If you want to start a new MBeanServer, specify the serviceUrl -->
|
||||
<!-- <jmx serviceUrl="service:jmx:rmi:///jndi/rmi://localhost:9999/solr"/>
|
||||
-->
|
||||
|
||||
<!-- the default high-performance update handler -->
|
||||
<!-- The default high-performance update handler -->
|
||||
<updateHandler class="solr.DirectUpdateHandler2">
|
||||
<!-- A prefix of "solr." for class names is an alias that
|
||||
causes solr to search appropriate packages, including
|
||||
org.apache.solr.(search|update|request|core|analysis)
|
||||
-->
|
||||
|
||||
<!-- Perform a <commit/> automatically under certain conditions:
|
||||
maxDocs - number of updates since last commit is greater than this
|
||||
maxTime - oldest uncommited update (in ms) is this long ago
|
||||
<!-- AutoCommit
|
||||
|
||||
Perform a <commit/> automatically under certain conditions.
|
||||
Instead of enabling autoCommit, consider using "commitWithin"
|
||||
when adding documents. http://wiki.apache.org/solr/UpdateXmlMessages
|
||||
when adding documents.
|
||||
|
||||
http://wiki.apache.org/solr/UpdateXmlMessages
|
||||
|
||||
maxDocs - Maximum number of documents to add since the last
|
||||
commit before automatically triggering a new commit.
|
||||
|
||||
maxTime - Maximum amount of time that is allowed to pass
|
||||
since a document was added before automaticly
|
||||
triggering a new commit.
|
||||
-->
|
||||
<!--
|
||||
<autoCommit>
|
||||
<maxDocs>10000</maxDocs>
|
||||
<maxTime>1000</maxTime>
|
||||
@@ -241,16 +301,29 @@
|
||||
<maxTime>10000</maxTime> <!--Commit every 10 seconds-->
|
||||
</autoCommit>
|
||||
|
||||
<!-- Update Related Event Listeners
|
||||
|
||||
Various IndexWriter related events can trigger Listeners to
|
||||
take actions.
|
||||
|
||||
postCommit - fired after every commit or optimize command
|
||||
postOptimize - fired after every optimize command
|
||||
-->
|
||||
<!-- The RunExecutableListener executes an external command from a
|
||||
hook such as postCommit or postOptimize.
|
||||
|
||||
exe - the name of the executable to run
|
||||
dir - dir to use as the current working directory. default="."
|
||||
wait - the calling thread waits until the executable returns. default="true"
|
||||
args - the arguments to pass to the program. default=nothing
|
||||
env - environment variables to set. default=nothing
|
||||
dir - dir to use as the current working directory. (default=".")
|
||||
wait - the calling thread waits until the executable returns.
|
||||
(default="true")
|
||||
args - the arguments to pass to the program. (default is none)
|
||||
env - environment variables to set. (default is none)
|
||||
-->
|
||||
<!-- A postCommit event is fired after every commit or optimize command
|
||||
<!-- This example shows how RunExecutableListener could be used
|
||||
with the script based replication...
|
||||
http://wiki.apache.org/solr/CollectionDistribution
|
||||
-->
|
||||
<!--
|
||||
<listener event="postCommit" class="solr.RunExecutableListener">
|
||||
<str name="exe">solr/bin/snapshooter</str>
|
||||
<str name="dir">.</str>
|
||||
@@ -259,64 +332,89 @@
|
||||
<arr name="env"> <str>MYVAR=val1</str> </arr>
|
||||
</listener>
|
||||
-->
|
||||
<!-- A postOptimize event is fired only after every optimize command
|
||||
<listener event="postOptimize" class="solr.RunExecutableListener">
|
||||
<str name="exe">snapshooter</str>
|
||||
<str name="dir">solr/bin</str>
|
||||
<bool name="wait">true</bool>
|
||||
</listener>
|
||||
-->
|
||||
|
||||
</updateHandler>
|
||||
|
||||
<!-- Use the following format to specify a custom IndexReaderFactory - allows for alternate
|
||||
IndexReader implementations.
|
||||
<!-- IndexReaderFactory
|
||||
|
||||
Use the following format to specify a custom IndexReaderFactory,
|
||||
which allows for alternate IndexReader implementations.
|
||||
|
||||
** Experimental Feature **
|
||||
Please note - Using a custom IndexReaderFactory may prevent certain other features
|
||||
from working. The API to IndexReaderFactory may change without warning or may even
|
||||
be removed from future releases if the problems cannot be resolved.
|
||||
|
||||
Please note - Using a custom IndexReaderFactory may prevent
|
||||
certain other features from working. The API to
|
||||
IndexReaderFactory may change without warning or may even be
|
||||
removed from future releases if the problems cannot be
|
||||
resolved.
|
||||
|
||||
|
||||
** Features that may not work with custom IndexReaderFactory **
|
||||
The ReplicationHandler assumes a disk-resident index. Using a custom
|
||||
IndexReader implementation may cause incompatibility with ReplicationHandler and
|
||||
may cause replication to not work correctly. See SOLR-1366 for details.
|
||||
|
||||
The ReplicationHandler assumes a disk-resident index. Using a
|
||||
custom IndexReader implementation may cause incompatibility
|
||||
with ReplicationHandler and may cause replication to not work
|
||||
correctly. See SOLR-1366 for details.
|
||||
|
||||
-->
|
||||
<!--
|
||||
<indexReaderFactory name="IndexReaderFactory" class="package.class">
|
||||
Parameters as required by the implementation
|
||||
<str name="someArg">Some Value</str>
|
||||
</indexReaderFactory >
|
||||
-->
|
||||
<!-- To set the termInfosIndexDivisor, do this: -->
|
||||
<!--<indexReaderFactory name="IndexReaderFactory" class="org.apache.solr.core.StandardIndexReaderFactory">
|
||||
<int name="termInfosIndexDivisor">12</int>
|
||||
</indexReaderFactory >-->
|
||||
<!-- By explicitly declaring the Factory, the termIndexDivisor can
|
||||
be specified.
|
||||
-->
|
||||
<!--
|
||||
<indexReaderFactory name="IndexReaderFactory"
|
||||
class="solr.StandardIndexReaderFactory">
|
||||
<int name="setTermIndexDivisor">12</int>
|
||||
</indexReaderFactory >
|
||||
-->
|
||||
|
||||
|
||||
<query>
|
||||
<!-- Maximum number of clauses in a boolean query... in the past, this affected
|
||||
range or prefix queries that expanded to big boolean queries - built in Solr
|
||||
query parsers no longer create queries with this limitation.
|
||||
An exception is thrown if exceeded. -->
|
||||
<!-- Max Boolean Clauses
|
||||
|
||||
Maximum number of clauses in each BooleanQuery, an exception
|
||||
is thrown if exceeded.
|
||||
|
||||
** WARNING **
|
||||
|
||||
This option actually modifies a global Lucene property that
|
||||
will affect all SolrCores. If multiple solrconfig.xml files
|
||||
disagree on this property, the value at any given moment will
|
||||
be based on the last SolrCore to be initialized.
|
||||
|
||||
-->
|
||||
<maxBooleanClauses>1024</maxBooleanClauses>
|
||||
|
||||
|
||||
<!-- There are two implementations of cache available for Solr,
|
||||
<!-- Solr Internal Query Caches
|
||||
|
||||
There are two implementations of cache available for Solr,
|
||||
LRUCache, based on a synchronized LinkedHashMap, and
|
||||
FastLRUCache, based on a ConcurrentHashMap. FastLRUCache has faster gets
|
||||
and slower puts in single threaded operation and thus is generally faster
|
||||
than LRUCache when the hit ratio of the cache is high (> 75%), and may be
|
||||
faster under other scenarios on multi-cpu systems. -->
|
||||
<!-- Cache used by SolrIndexSearcher for filters (DocSets),
|
||||
FastLRUCache, based on a ConcurrentHashMap.
|
||||
|
||||
FastLRUCache has faster gets and slower puts in single
|
||||
threaded operation and thus is generally faster than LRUCache
|
||||
when the hit ratio of the cache is high (> 75%), and may be
|
||||
faster under other scenarios on multi-cpu systems.
|
||||
-->
|
||||
|
||||
<!-- Filter Cache
|
||||
|
||||
Cache used by SolrIndexSearcher for filters (DocSets),
|
||||
unordered sets of *all* documents that match a query.
|
||||
When a new searcher is opened, its caches may be prepopulated
|
||||
or "autowarmed" using data from caches in the old searcher.
|
||||
autowarmCount is the number of items to prepopulate. For LRUCache,
|
||||
the autowarmed items will be the most recently accessed items.
|
||||
Parameters:
|
||||
class - the SolrCache implementation LRUCache or FastLRUCache
|
||||
class - the SolrCache implementation LRUCache or
|
||||
(LRUCache or FastLRUCache)
|
||||
size - the maximum number of entries in the cache
|
||||
initialSize - the initial capacity (number of entries) of
|
||||
the cache. (seel java.util.HashMap)
|
||||
the cache. (see java.util.HashMap)
|
||||
autowarmCount - the number of entries to prepopulate from
|
||||
and old cache.
|
||||
-->
|
||||
@@ -326,65 +424,91 @@
|
||||
initialSize="512"
|
||||
autowarmCount="0"/>
|
||||
|
||||
<!-- Cache used to hold field values that are quickly accessible
|
||||
by document id. The fieldValueCache is created by default
|
||||
even if not configured here.
|
||||
<fieldValueCache
|
||||
class="solr.FastLRUCache"
|
||||
size="512"
|
||||
autowarmCount="128"
|
||||
showItems="32"
|
||||
/>
|
||||
-->
|
||||
<!-- Query Result Cache
|
||||
|
||||
<!-- queryResultCache caches results of searches - ordered lists of
|
||||
document ids (DocList) based on a query, a sort, and the range
|
||||
of documents requested. -->
|
||||
<queryResultCache
|
||||
class="solr.LRUCache"
|
||||
Caches results of searches - ordered lists of document ids
|
||||
(DocList) based on a query, a sort, and the range of documents requested.
|
||||
-->
|
||||
<queryResultCache class="solr.LRUCache"
|
||||
size="512"
|
||||
initialSize="512"
|
||||
autowarmCount="0"/>
|
||||
|
||||
<!-- documentCache caches Lucene Document objects (the stored fields for each document).
|
||||
Since Lucene internal document ids are transient, this cache will not be autowarmed. -->
|
||||
<!-- Document Cache
|
||||
|
||||
Caches Lucene Document objects (the stored fields for each
|
||||
document). Since Lucene internal document ids are transient,
|
||||
this cache will not be autowarmed.
|
||||
-->
|
||||
<documentCache
|
||||
class="solr.LRUCache"
|
||||
size="512"
|
||||
initialSize="512"
|
||||
autowarmCount="0"/>
|
||||
|
||||
<!-- If true, stored fields that are not requested will be loaded lazily.
|
||||
This can result in a significant speed improvement if the usual case is to
|
||||
not load all stored fields, especially if the skipped fields are large
|
||||
compressed text fields.
|
||||
-->
|
||||
<enableLazyFieldLoading>true</enableLazyFieldLoading>
|
||||
<!-- Field Value Cache
|
||||
|
||||
<!-- Example of a generic cache. These caches may be accessed by name
|
||||
through SolrIndexSearcher.getCache(),cacheLookup(), and cacheInsert().
|
||||
The purpose is to enable easy caching of user/application level data.
|
||||
The regenerator argument should be specified as an implementation
|
||||
of solr.search.CacheRegenerator if autowarming is desired. -->
|
||||
Cache used to hold field values that are quickly accessible
|
||||
by document id. The fieldValueCache is created by default
|
||||
even if not configured here.
|
||||
-->
|
||||
<!--
|
||||
<fieldValueCache class="solr.FastLRUCache"
|
||||
size="512"
|
||||
autowarmCount="128"
|
||||
showItems="32" />
|
||||
-->
|
||||
|
||||
<!-- Custom Cache
|
||||
|
||||
Example of a generic cache. These caches may be accessed by
|
||||
name through SolrIndexSearcher.getCache(),cacheLookup(), and
|
||||
cacheInsert(). The purpose is to enable easy caching of
|
||||
user/application level data. The regenerator argument should
|
||||
be specified as an implementation of solr.CacheRegenerator
|
||||
if autowarming is desired.
|
||||
-->
|
||||
<!--
|
||||
<cache name="myUserCache"
|
||||
class="solr.LRUCache"
|
||||
size="4096"
|
||||
initialSize="1024"
|
||||
autowarmCount="1024"
|
||||
regenerator="org.mycompany.mypackage.MyRegenerator"
|
||||
regenerator="com.mycompany.MyRegenerator"
|
||||
/>
|
||||
-->
|
||||
|
||||
<!-- An optimization that attempts to use a filter to satisfy a search.
|
||||
If the requested sort does not include score, then the filterCache
|
||||
will be checked for a filter matching the query. If found, the filter
|
||||
will be used as the source of document ids, and then the sort will be
|
||||
applied to that.
|
||||
|
||||
<!-- Lazy Field Loading
|
||||
|
||||
If true, stored fields that are not requested will be loaded
|
||||
lazily. This can result in a significant speed improvement
|
||||
if the usual case is to not load all stored fields,
|
||||
especially if the skipped fields are large compressed text
|
||||
fields.
|
||||
-->
|
||||
<enableLazyFieldLoading>true</enableLazyFieldLoading>
|
||||
|
||||
<!-- Use Filter For Sorted Query
|
||||
|
||||
A possible optimization that attempts to use a filter to
|
||||
satisfy a search. If the requested sort does not include
|
||||
score, then the filterCache will be checked for a filter
|
||||
matching the query. If found, the filter will be used as the
|
||||
source of document ids, and then the sort will be applied to
|
||||
that.
|
||||
|
||||
For most situations, this will not be useful unless you
|
||||
frequently get the same search repeatedly with different sort
|
||||
options, and none of them ever use "score"
|
||||
-->
|
||||
<!--
|
||||
<useFilterForSortedQuery>true</useFilterForSortedQuery>
|
||||
-->
|
||||
|
||||
<!-- An optimization for use with the queryResultCache. When a search
|
||||
<!-- Result Window Size
|
||||
|
||||
An optimization for use with the queryResultCache. When a search
|
||||
is requested, a superset of the requested number of document ids
|
||||
are collected. For example, if a search for a particular query
|
||||
requests matching documents 10 through 19, and queryWindowSize is 50,
|
||||
@@ -396,10 +520,21 @@
|
||||
queryResultCache. -->
|
||||
<queryResultMaxDocsCached>200</queryResultMaxDocsCached>
|
||||
|
||||
<!-- a newSearcher event is fired whenever a new searcher is being prepared
|
||||
<!-- Query Related Event Listeners
|
||||
|
||||
Various IndexSearcher related events can trigger Listeners to
|
||||
take actions.
|
||||
|
||||
newSearcher - fired whenever a new searcher is being prepared
|
||||
and there is a current searcher handling requests (aka registered).
|
||||
It can be used to prime certain caches to prevent long request times for
|
||||
certain requests.
|
||||
|
||||
firstSearcher - fired whenever a new searcher is being
|
||||
prepared but there is no current registered searcher to handle
|
||||
requests or to gain autowarming data from.
|
||||
|
||||
|
||||
-->
|
||||
<!-- QuerySenderListener takes an array of NamedList and executes a
|
||||
local query request for each NamedList in sequence. -->
|
||||
@@ -1027,7 +1162,7 @@
|
||||
|
||||
<!-- config for the admin interface -->
|
||||
<admin>
|
||||
<defaultQuery>solr</defaultQuery>
|
||||
<defaultQuery>*:*</defaultQuery>
|
||||
|
||||
<!-- configure a healthcheck file for servers behind a loadbalancer
|
||||
<healthcheck type="file">server-enabled</healthcheck>
|
||||
|
@@ -255,6 +255,7 @@
|
||||
-->
|
||||
<fieldtype name="ignored" stored="false" indexed="false" class="solr.StrField" />
|
||||
|
||||
<fieldType name="uuid" class="solr.UUIDField" indexed="true" />
|
||||
</types>
|
||||
|
||||
|
||||
@@ -277,8 +278,8 @@
|
||||
best performance.
|
||||
-->
|
||||
|
||||
<field name="type" type="integer" indexed="true" stored="true" required="true" />
|
||||
<field name="id" type="integer" indexed="true" stored="true" required="true" />
|
||||
<field name="type" type="integer" indexed="true" stored="true" required="false" />
|
||||
<field name="id" type="integer" indexed="true" stored="true" required="false" />
|
||||
<field name="ip" type="string" indexed="true" stored="true" required="false" />
|
||||
<field name="time" type="date" indexed="true" stored="true" required="true" />
|
||||
<field name="epersonid" type="integer" indexed="true" stored="true" required="false" />
|
||||
@@ -286,8 +287,8 @@
|
||||
<field name="country" type="string" indexed="true" stored="true" required="false" />
|
||||
<field name="countryCode" type="string" indexed="true" stored="true" required="false"/>
|
||||
<field name="city" type="string" indexed="true" stored="true" required="false"/>
|
||||
<field name="longitude" type="float" indexed="true" stored="true" required="false" />
|
||||
<field name="latitude" type="float" indexed="true" stored="true" required="false" />
|
||||
<field name="longitude" type="float" indexed="true" stored="true" required="false" />
|
||||
<field name="latitude" type="float" indexed="true" stored="true" required="false" />
|
||||
<field name="owningComm" type="integer" indexed="true" stored="true" required="false" multiValued="true" />
|
||||
<field name="owningColl" type="integer" indexed="true" stored="true" required="false" multiValued="true" />
|
||||
<field name="owningItem" type="integer" indexed="true" stored="true" required="false" multiValued="true" />
|
||||
@@ -295,9 +296,33 @@
|
||||
<field name="userAgent" type="string" indexed="true" stored="true" required="false"/>
|
||||
<field name="isBot" type="boolean" indexed="true" stored="true" required="false"/>
|
||||
<field name="bundleName" type="string" indexed="true" stored="true" required="false" multiValued="true" />
|
||||
<field name="referrer" type="string" indexed="true" stored="true" required="false"/>
|
||||
<field name="uid" type="uuid" indexed="true" stored="true" default="NEW" />
|
||||
|
||||
<!--Can either be view/search/search_result/workflow-->
|
||||
<field name="statistics_type" type="string" indexed="true" stored="true" required="true" default="view" />
|
||||
|
||||
<!-- Search specific -->
|
||||
<field name="query" type="string" indexed="true" stored="true" required="false" multiValued="true"/>
|
||||
<field name="scopeType" type="integer" indexed="true" stored="true" required="false" />
|
||||
<field name="scopeId" type="integer" indexed="true" stored="true" required="false" />
|
||||
<field name="rpp" type="integer" indexed="true" stored="true" required="false" />
|
||||
<field name="sortBy" type="string" indexed="true" stored="true" required="false" />
|
||||
<field name="sortOrder" type="string" indexed="true" stored="true" required="false" />
|
||||
<field name="page" type="integer" indexed="true" stored="true" required="false" />
|
||||
|
||||
<!--Workflow spefic-->
|
||||
<field name="workflowStep" type="string" indexed="true" stored="true" required="false" multiValued="true"/>
|
||||
<field name="previousWorkflowStep" type="string" indexed="true" stored="true" required="false" multiValued="true"/>
|
||||
<field name="owner" type="string" indexed="true" stored="true" required="false" multiValued="true"/>
|
||||
<field name="submitter" type="integer" indexed="true" stored="true" required="false" />
|
||||
<field name="actor" type="integer" indexed="true" stored="true" required="false" />
|
||||
<field name="workflowItemId" type="integer" indexed="true" stored="true" required="false" />
|
||||
|
||||
|
||||
</fields>
|
||||
|
||||
<uniqueKey>uid</uniqueKey>
|
||||
|
||||
<!-- field for the QueryParser to use when an explicit fieldname is absent -->
|
||||
<defaultSearchField>id</defaultSearchField>
|
||||
|
Reference in New Issue
Block a user