Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Add fedora:writable triple and disable HTML UI actions if it is false
  • Loading branch information
escowles authored and Andrew Woods committed May 28, 2014
1 parent b8ab960 commit 4851836
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 6 deletions.
13 changes: 7 additions & 6 deletions fcrepo-http-api/src/main/resources/views/common-node-actions.vsl
@@ -1,7 +1,8 @@
#set ($content = $helpers.getObjectsAsString($rdf, $topic, $rdfLexicon.HAS_CONTENT, false))
#set ($readonly = $helpers.isFrozenNode($rdf, $topic))
#set ($writable = $helpers.isWritable($rdf, $topic))
#set ($frozen = $helpers.isFrozenNode($rdf, $topic))

#if ($readonly == true)
#if ( $frozen == true )
<div class="alert alert-warning">
<span class="glyphicon glyphicon-warning-sign"></span>
This is a <strong>historic version</strong> and cannot be modified.
Expand All @@ -17,7 +18,7 @@
#if ($content != "")
<a href="$content" class="btn btn-success btn-lg"><span class="glyphicon glyphicon-download"></span> Download Content</a>

#if ($readonly == false)
#if ($writable == true)
<h3>Update Content</h3>
<form action="javascript:updateFile()">
<div class="form-group">
Expand All @@ -30,7 +31,7 @@
<hr />
#end

#if ($readonly == false)
#if ($writable == true)
<form id="action_create" name="action_create" method="POST" enctype="multipart/form-data">
<h3>Create New Node</h3>
<div class="form-group">
Expand Down Expand Up @@ -107,7 +108,7 @@ WHERE { }
#end


#if ($readonly == false)
#if ($writable == true)
#set ($serializations = $rdf.find($nodeany, $topic, $helpers.asNode($rdfLexicon.HAS_SERIALIZATION), $nodeany))

#if($serializations.hasNext())
Expand Down Expand Up @@ -160,7 +161,7 @@ WHERE { }
#end
#end

#if ($readonly == false)
#if ($writable == true)
#set ($fedoraresc = $rdf.find($nodeany, $topic, $helpers.asNode($rdfLexicon.HAS_MIXIN_TYPE), $helpers.asLiteralStringNode("fedora:resource")))
#if($fedoraresc.hasNext())
<h3>Access Roles</h3>
Expand Down
Expand Up @@ -238,6 +238,15 @@ public String getObjectTitle(final DatasetGraph dataset,

}

/**
* Determines whether the subject is writable
* true if node is writable
*/
public boolean isWritable(final DatasetGraph dataset, final Node subject) {
final Iterator<Quad> it = getObjects(dataset, subject, RdfLexicon.WRITABLE);
return it.hasNext() && it.next().getObject().getLiteralValue().toString().equals("true");
}

/**
* Determines whether the subject is of type nt:frozenNode.
* true if node has type nt:frozen
Expand Down
Expand Up @@ -32,6 +32,7 @@
import static org.fcrepo.kernel.RdfLexicon.LAST_MODIFIED_DATE;
import static org.fcrepo.kernel.RdfLexicon.HAS_VERSION;
import static org.fcrepo.kernel.RdfLexicon.HAS_CONTENT;
import static org.fcrepo.kernel.RdfLexicon.WRITABLE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
Expand Down Expand Up @@ -128,6 +129,27 @@ public void shouldRefuseToConvertAForeignUriToNodeBreadcrumbs() {
assertTrue(nodeBreadcrumbs.isEmpty());
}

@Test
public void testIsWritable() {
final DatasetGraph mem = createMem();
mem.add(createAnon(), createURI("a/b/c"), WRITABLE.asNode(), createLiteral(Boolean.TRUE.toString()));
assertTrue("Node is should be writable.", testObj.isWritable(mem, createURI("a/b/c")));
}

@Test
public void testIsWritableFalse() {
final DatasetGraph mem = createMem();
mem.add(createAnon(), createURI("a/b/c"), WRITABLE.asNode(), createLiteral(Boolean.FALSE.toString()));
assertFalse("Node should not be writable.", testObj.isWritable(mem, createURI("a/b/c")));
}

@Test
public void testIsWritableFalseJunk() {
final DatasetGraph mem = createMem();
mem.add(createAnon(), createURI("a/b/c"), HAS_CONTENT.asNode(), createLiteral("junk"));
assertFalse("Node should not be writable.", testObj.isWritable(mem, createURI("a/b/c")));
}

@Test
public void testIsFrozenNode() {
final DatasetGraph mem = createMem();
Expand Down
Expand Up @@ -16,12 +16,17 @@
package org.fcrepo.kernel.impl.rdf.impl;

import static com.google.common.base.Throwables.propagate;
import static com.hp.hpl.jena.datatypes.xsd.XSDDatatype.XSDboolean;
import static com.hp.hpl.jena.graph.NodeFactory.createLiteral;
import static com.hp.hpl.jena.graph.NodeFactory.createURI;
import static com.hp.hpl.jena.graph.Triple.create;
import static com.hp.hpl.jena.vocabulary.RDF.type;
import static org.fcrepo.kernel.impl.rdf.JcrRdfTools.getRDFNamespaceForJcrNamespace;
import static org.fcrepo.kernel.RdfLexicon.WRITABLE;
import static org.slf4j.LoggerFactory.getLogger;

import java.security.AccessControlException;

import java.util.Iterator;
import java.util.Set;

Expand Down Expand Up @@ -70,6 +75,9 @@ public NodeRdfContext(final Node node, final IdentifierTranslator graphSubjects)

//include rdf:type for primaryType, mixins, and their supertypes
concatRdfTypes();

// include writable status
concatWritable();
}

/**
Expand Down Expand Up @@ -155,4 +163,16 @@ private void concatRdfTypes() throws RepositoryException {
concat(Iterators.transform(nodeTypesIt,nodetype2triple()));
}

private void concatWritable() throws RepositoryException {
boolean writable = false;
try {
node.getSession().checkPermission( node.getPath(), "add_node,set_property,remove" );
writable = true;
} catch ( AccessControlException ex ) {
writable = false;
}

concat(create(subject(), WRITABLE.asNode(), createLiteral(String.valueOf(writable), XSDboolean)));
}

}
Expand Up @@ -15,19 +15,27 @@
*/
package org.fcrepo.kernel.impl.rdf.impl;

import static com.hp.hpl.jena.datatypes.xsd.XSDDatatype.XSDboolean;
import static com.hp.hpl.jena.rdf.model.ResourceFactory.createResource;
import static com.hp.hpl.jena.vocabulary.RDF.type;
import static org.fcrepo.kernel.RdfLexicon.JCR_NAMESPACE;
import static org.fcrepo.kernel.RdfLexicon.REPOSITORY_NAMESPACE;
import static org.fcrepo.kernel.RdfLexicon.WRITABLE;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.initMocks;
import static org.slf4j.LoggerFactory.getLogger;

import com.hp.hpl.jena.rdf.model.Literal;

import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;

import java.security.AccessControlException;

import javax.jcr.NamespaceRegistry;
import javax.jcr.Node;
import javax.jcr.Repository;
Expand Down Expand Up @@ -55,6 +63,9 @@ public class NodeRdfContextTest {
@Mock
private Node mockNode;

@Mock
private Node readOnlyNode;

@Mock
private NodeType mockPrimaryNodeType;

Expand Down Expand Up @@ -83,6 +94,7 @@ public class NodeRdfContextTest {
private Workspace mockWorkspace;

private static final String mockNodeName = "mockNode";
private static final String readOnlyNodeName = "readOnlyNode";

private static final String mockNodeTypePrefix = "jcr";

Expand All @@ -92,6 +104,7 @@ public class NodeRdfContextTest {
private static final String mockMixinSuperNodeTypeName = "someMixinSuperType";

private static final Resource mockNodeSubject = createResource();
private static final Resource readOnlyNodeSubject = createResource();

@Before
public void setUp() throws RepositoryException {
Expand Down Expand Up @@ -123,6 +136,15 @@ public void setUp() throws RepositoryException {
when(mockWorkspace.getNamespaceRegistry()).thenReturn(mockNamespaceRegistry);
when(mockNamespaceRegistry.getURI("jcr")).thenReturn(JCR_NAMESPACE);
when(mockGraphSubjects.getSubject(mockNode.getPath())).thenReturn(mockNodeSubject);

// read-only node mocks
when(readOnlyNode.getPrimaryNodeType()).thenReturn(mockPrimaryNodeType);
when(readOnlyNode.getName()).thenReturn(readOnlyNodeName);
when(readOnlyNode.getSession()).thenReturn(mockSession);
when(readOnlyNode.getPath()).thenReturn("/readOnlyNode");
when(mockGraphSubjects.getSubject(readOnlyNode.getPath())).thenReturn(readOnlyNodeSubject);
doThrow(new AccessControlException("permissions check failed")).when(mockSession).checkPermission(
eq("/readOnlyNode"), eq("add_node,set_property,remove"));
}

@Test
Expand All @@ -138,6 +160,7 @@ public void testRdfTypesForNodetypes() throws RepositoryException,
createResource(REPOSITORY_NAMESPACE + mockPrimarySuperNodeTypeName);
final Resource expectedRdfTypeMixinSuper =
createResource(REPOSITORY_NAMESPACE + mockMixinSuperNodeTypeName);
final Literal booleanTrue = actual.createTypedLiteral("true", XSDboolean);
logRdf("Constructed RDF: ", actual);
assertTrue("Didn't find RDF type triple for primarytype!", actual.contains(
mockNodeSubject, type, expectedRdfTypePrimary));
Expand All @@ -147,6 +170,7 @@ public void testRdfTypesForNodetypes() throws RepositoryException,
mockNodeSubject, type, expectedRdfTypePrimarySuper));
assertTrue("Didn't find RDF type triple for mixinsupertype!", actual.contains(
mockNodeSubject, type, expectedRdfTypeMixinSuper));
assertTrue("Didn't find writable triple!", actual.contains(mockNodeSubject, WRITABLE, booleanTrue));
}

@Test(expected = RuntimeException.class)
Expand All @@ -157,6 +181,14 @@ public void testBadRepository() throws RepositoryException {

}

@Test
public void testReadOnlyNode() throws RepositoryException, IOException {
final Model actual = new NodeRdfContext(readOnlyNode, mockGraphSubjects).asModel();
logRdf("Constructed RDF: ", actual);
final Literal booleanFalse = actual.createTypedLiteral(false, XSDboolean);
assertTrue("Didn't find writable triple!", actual.contains(readOnlyNodeSubject, WRITABLE, booleanFalse));
}

private static void logRdf(final String message, final Model model) throws IOException {
LOGGER.debug(message);
try (Writer w = new StringWriter()) {
Expand Down
2 changes: 2 additions & 0 deletions fcrepo-kernel/src/main/java/org/fcrepo/kernel/RdfLexicon.java
Expand Up @@ -161,6 +161,8 @@ public final class RdfLexicon {
createProperty("http://sindice.com/vocab/search#searchTerms");
public static final Property SEARCH_HAS_MORE =
createProperty(RESTAPI_NAMESPACE + "hasMoreResults");
public static final Property WRITABLE =
createProperty(RESTAPI_NAMESPACE + "writable");

public static final Set<Property> searchProperties = of(SEARCH_PAGE,
SEARCH_HAS_TOTAL_RESULTS, SEARCH_ITEMS_PER_PAGE, SEARCH_OFFSET,
Expand Down

0 comments on commit 4851836

Please sign in to comment.