/*
* (C) Copyright 2006-2007 Nuxeo SA (http://nuxeo.com/) and others.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Contributors:
* Nuxeo - initial API and implementation
*
* $Id: RelationActionsBean.java 28951 2008-01-11 13:35:15Z tdelprat $
*/
package org.nuxeo.ecm.platform.relations.web.listener.ejb;
import static org.jboss.seam.ScopeType.CONVERSATION;
import java.io.Serializable;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import javax.faces.event.ActionEvent;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.Factory;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.contexts.Context;
import org.jboss.seam.contexts.Contexts;
import org.jboss.seam.faces.FacesMessages;
import org.jboss.seam.international.StatusMessage;
import org.nuxeo.ecm.core.api.CoreSession;
import org.nuxeo.ecm.core.api.DocumentModel;
import org.nuxeo.ecm.platform.relations.api.DocumentRelationManager;
import org.nuxeo.ecm.platform.relations.api.Graph;
import org.nuxeo.ecm.platform.relations.api.Node;
import org.nuxeo.ecm.platform.relations.api.QNameResource;
import org.nuxeo.ecm.platform.relations.api.RelationManager;
import org.nuxeo.ecm.platform.relations.api.Resource;
import org.nuxeo.ecm.platform.relations.api.ResourceAdapter;
import org.nuxeo.ecm.platform.relations.api.Statement;
import org.nuxeo.ecm.platform.relations.api.Subject;
import org.nuxeo.ecm.platform.relations.api.exceptions.RelationAlreadyExistsException;
import org.nuxeo.ecm.platform.relations.api.impl.LiteralImpl;
import org.nuxeo.ecm.platform.relations.api.impl.QNameResourceImpl;
import org.nuxeo.ecm.platform.relations.api.impl.ResourceImpl;
import org.nuxeo.ecm.platform.relations.api.util.RelationConstants;
import org.nuxeo.ecm.platform.relations.jena.JenaGraph;
import org.nuxeo.ecm.platform.relations.web.NodeInfo;
import org.nuxeo.ecm.platform.relations.web.NodeInfoImpl;
import org.nuxeo.ecm.platform.relations.web.StatementInfo;
import org.nuxeo.ecm.platform.relations.web.StatementInfoComparator;
import org.nuxeo.ecm.platform.relations.web.StatementInfoImpl;
import org.nuxeo.ecm.platform.relations.web.listener.RelationActions;
import org.nuxeo.ecm.platform.ui.web.api.NavigationContext;
import org.nuxeo.ecm.platform.ui.web.invalidations.AutomaticDocumentBasedInvalidation;
import org.nuxeo.ecm.platform.ui.web.invalidations.DocumentContextBoundActionBean;
import org.nuxeo.ecm.webapp.helpers.ResourcesAccessor;
/**
* Seam component that manages statements involving current document as well as creation, edition and deletion of
* statements involving current document.
* <p>
* Current document is the subject of the relation. The predicate is resolved thanks to a list of predicates URIs. The
* object is resolved using a type (literal, resource, qname resource), an optional namespace (for qname resources) and
* a value.
*
* @author <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a>
*/
@Name("relationActions")
@Scope(CONVERSATION)
@AutomaticDocumentBasedInvalidation
public class RelationActionsBean extends DocumentContextBoundActionBean implements RelationActions, Serializable {
private static final long serialVersionUID = 2336539966097558178L;
private static final Log log = LogFactory.getLog(RelationActionsBean.class);
protected static boolean includeStatementsInEvents = false;
@In(create = true, required = false)
protected transient CoreSession documentManager;
@In(create = true)
protected RelationManager relationManager;
@In(create = true)
protected DocumentRelationManager documentRelationManager;
@In(create = true)
protected NavigationContext navigationContext;
@In(create = true)
protected transient ResourcesAccessor resourcesAccessor;
@In(create = true, required = false)
protected FacesMessages facesMessages;
@In(required = false)
protected transient Principal currentUser;
// statements lists
protected List<Statement> incomingStatements;
protected List<StatementInfo> incomingStatementsInfo;
protected List<Statement> outgoingStatements;
protected List<StatementInfo> outgoingStatementsInfo;
// fields for relation creation
protected String predicateUri;
protected String objectType;
protected String objectLiteralValue;
protected String objectUri;
protected String objectDocumentUid;
protected String objectDocumentTitle;
protected String comment;
protected Boolean showCreateForm = false;
// popupDisplayed flag for preventing relation_search content view execution
// until search button clicked
protected Boolean popupDisplayed = false;
@Override
public DocumentModel getDocumentModel(Node node) {
if (node.isQNameResource()) {
QNameResource resource = (QNameResource) node;
Map<String, Object> context = Collections.<String, Object> singletonMap(
ResourceAdapter.CORE_SESSION_CONTEXT_KEY, documentManager);
Object o = relationManager.getResourceRepresentation(resource.getNamespace(), resource, context);
if (o instanceof DocumentModel) {
return (DocumentModel) o;
}
}
return null;
}
// XXX AT: for BBB when repo name was not included in the resource uri
@Deprecated
private static QNameResource getOldDocumentResource(DocumentModel document) {
QNameResource documentResource = null;
if (document != null) {
documentResource = new QNameResourceImpl(RelationConstants.DOCUMENT_NAMESPACE, document.getId());
}
return documentResource;
}
@Override
public QNameResource getDocumentResource(DocumentModel document) {
QNameResource documentResource = null;
if (document != null) {
documentResource = (QNameResource) relationManager.getResource(RelationConstants.DOCUMENT_NAMESPACE,
document, null);
}
return documentResource;
}
protected List<StatementInfo> getStatementsInfo(List<Statement> statements) {
if (statements == null) {
return null;
}
List<StatementInfo> infoList = new ArrayList<StatementInfo>();
for (Statement statement : statements) {
Subject subject = statement.getSubject();
// TODO: filter on doc visibility (?)
NodeInfo subjectInfo = new NodeInfoImpl(subject, getDocumentModel(subject), true);
Resource predicate = statement.getPredicate();
Node object = statement.getObject();
NodeInfo objectInfo = new NodeInfoImpl(object, getDocumentModel(object), true);
StatementInfo info = new StatementInfoImpl(statement, subjectInfo, new NodeInfoImpl(predicate), objectInfo);
infoList.add(info);
}
return infoList;
}
protected void resetEventContext() {
Context evtCtx = Contexts.getEventContext();
if (evtCtx != null) {
evtCtx.remove("currentDocumentIncomingRelations");
evtCtx.remove("currentDocumentOutgoingRelations");
}
}
@Override
@Factory(value = "currentDocumentIncomingRelations", scope = ScopeType.EVENT)
public List<StatementInfo> getIncomingStatementsInfo() {
if (incomingStatementsInfo != null) {
return incomingStatementsInfo;
}
DocumentModel currentDoc = getCurrentDocument();
Resource docResource = getDocumentResource(currentDoc);
if (docResource == null) {
incomingStatements = Collections.emptyList();
incomingStatementsInfo = Collections.emptyList();
} else {
Graph graph = relationManager.getGraphByName(RelationConstants.GRAPH_NAME);
incomingStatements = graph.getStatements(null, null, docResource);
if (graph instanceof JenaGraph) {
// add old statements, BBB
Resource oldDocResource = getOldDocumentResource(currentDoc);
incomingStatements.addAll(graph.getStatements(null, null, oldDocResource));
}
incomingStatementsInfo = getStatementsInfo(incomingStatements);
// sort by modification date, reverse
Comparator<StatementInfo> comp = Collections.reverseOrder(new StatementInfoComparator());
Collections.sort(incomingStatementsInfo, comp);
}
return incomingStatementsInfo;
}
@Override
@Factory(value = "currentDocumentOutgoingRelations", scope = ScopeType.EVENT)
public List<StatementInfo> getOutgoingStatementsInfo() {
if (outgoingStatementsInfo != null) {
return outgoingStatementsInfo;
}
DocumentModel currentDoc = getCurrentDocument();
Resource docResource = getDocumentResource(currentDoc);
if (docResource == null) {
outgoingStatements = Collections.emptyList();
outgoingStatementsInfo = Collections.emptyList();
} else {
Graph graph = relationManager.getGraphByName(RelationConstants.GRAPH_NAME);
outgoingStatements = graph.getStatements(docResource, null, null);
if (graph instanceof JenaGraph) {
// add old statements, BBB
Resource oldDocResource = getOldDocumentResource(currentDoc);
outgoingStatements.addAll(graph.getStatements(oldDocResource, null, null));
}
outgoingStatementsInfo = getStatementsInfo(outgoingStatements);
// sort by modification date, reverse
Comparator<StatementInfo> comp = Collections.reverseOrder(new StatementInfoComparator());
Collections.sort(outgoingStatementsInfo, comp);
}
return outgoingStatementsInfo;
}
@Override
public void resetStatements() {
incomingStatements = null;
incomingStatementsInfo = null;
outgoingStatements = null;
outgoingStatementsInfo = null;
}
// getters & setters for creation items
@Override
public String getComment() {
return comment;
}
@Override
public void setComment(String comment) {
this.comment = comment;
}
@Override
public String getObjectDocumentTitle() {
return objectDocumentTitle;
}
@Override
public void setObjectDocumentTitle(String objectDocumentTitle) {
this.objectDocumentTitle = objectDocumentTitle;
}
@Override
public String getObjectDocumentUid() {
return objectDocumentUid;
}
@Override
public void setObjectDocumentUid(String objectDocumentUid) {
this.objectDocumentUid = objectDocumentUid;
}
@Override
public String getObjectLiteralValue() {
return objectLiteralValue;
}
@Override
public void setObjectLiteralValue(String objectLiteralValue) {
this.objectLiteralValue = objectLiteralValue;
}
@Override
public String getObjectType() {
return objectType;
}
@Override
public void setObjectType(String objectType) {
this.objectType = objectType;
}
@Override
public String getObjectUri() {
return objectUri;
}
@Override
public void setObjectUri(String objectUri) {
this.objectUri = objectUri;
}
@Override
public String getPredicateUri() {
return predicateUri;
}
@Override
public void setPredicateUri(String predicateUri) {
this.predicateUri = predicateUri;
}
@Override
public String addStatement() {
resetEventContext();
Node object = null;
if (objectType.equals("literal")) {
objectLiteralValue = objectLiteralValue.trim();
object = new LiteralImpl(objectLiteralValue);
} else if (objectType.equals("uri")) {
objectUri = objectUri.trim();
object = new ResourceImpl(objectUri);
} else if (objectType.equals("document")) {
objectDocumentUid = objectDocumentUid.trim();
String repositoryName = navigationContext.getCurrentServerLocation().getName();
String localName = repositoryName + "/" + objectDocumentUid;
object = new QNameResourceImpl(RelationConstants.DOCUMENT_NAMESPACE, localName);
}
try {
documentRelationManager.addRelation(documentManager, getCurrentDocument(), object, predicateUri, false,
includeStatementsInEvents, StringUtils.trim(comment));
facesMessages.add(StatusMessage.Severity.INFO,
resourcesAccessor.getMessages().get("label.relation.created"));
resetCreateFormValues();
} catch (RelationAlreadyExistsException e) {
facesMessages.add(StatusMessage.Severity.WARN,
resourcesAccessor.getMessages().get("label.relation.already.exists"));
}
resetStatements();
return null;
}
@Override
public void toggleCreateForm(ActionEvent event) {
showCreateForm = !showCreateForm;
}
private void resetCreateFormValues() {
predicateUri = "";
objectType = "";
objectLiteralValue = "";
objectUri = "";
objectDocumentUid = "";
objectDocumentTitle = "";
comment = "";
showCreateForm = false;
popupDisplayed = false;
}
@Override
public String deleteStatement(StatementInfo stmtInfo) {
resetEventContext();
documentRelationManager.deleteRelation(documentManager, stmtInfo.getStatement());
facesMessages.add(StatusMessage.Severity.INFO, resourcesAccessor.getMessages().get("label.relation.deleted"));
resetStatements();
return null;
}
@Override
public Boolean getShowCreateForm() {
return showCreateForm;
}
@Override
protected void resetBeanCache(DocumentModel newCurrentDocumentModel) {
resetStatements();
}
public Boolean getPopupDisplayed() {
return popupDisplayed;
}
public void setPopupDisplayed(Boolean popupDisplayed) {
this.popupDisplayed = popupDisplayed;
}
}