Skip to content

Commit

Permalink
SAK-43072 Add document preview to grader (sakaiproject#7930)
Browse files Browse the repository at this point in the history
  • Loading branch information
adrianfish authored Mar 1, 2020
1 parent f5c12b4 commit 18a64c7
Show file tree
Hide file tree
Showing 10 changed files with 276 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;

import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.sakaiproject.authz.api.SecurityService;
import org.sakaiproject.component.cover.ComponentManager;
Expand All @@ -42,6 +44,7 @@
import org.sakaiproject.content.api.ContentResource;
import org.sakaiproject.content.api.ResourceTypeRegistry;
import org.sakaiproject.content.tool.ListItem;
import org.sakaiproject.entitybroker.exception.EntityException;
import org.sakaiproject.entity.api.EntityManager;
import org.sakaiproject.entity.api.EntityPermissionException;
import org.sakaiproject.entity.api.Reference;
Expand Down Expand Up @@ -80,6 +83,7 @@
* Entity provider for the Content / Resources tool
*/
@Slf4j
@Setter
public class ContentEntityProvider extends AbstractEntityProvider implements EntityProvider, AutoRegisterEntityProvider, ActionsExecutable, Outputable, Describeable {

public final static String ENTITY_PREFIX = "content";
Expand All @@ -89,6 +93,13 @@ public class ContentEntityProvider extends AbstractEntityProvider implements Ent
private static final String PARAMETER_DEPTH = "depth";
private static final String PARAMETER_TIMESTAMP = "timestamp";

private ContentHostingService contentHostingService;
private SiteService siteService;
private ToolManager toolManager;
private SecurityService securityService;
private UserDirectoryService userDirectoryService;
private EntityManager entityManager;

@Override
public String getEntityPrefix() {
return ENTITY_PREFIX;
Expand Down Expand Up @@ -167,20 +178,17 @@ public List<ContentItem> getContentCollectionForSite(EntityView view) {
// get siteId
String siteId = view.getPathSegment(2);


if(log.isDebugEnabled()) {
log.debug("Content for site: " + siteId);
}
log.debug("Content for site: {}", siteId);

// check siteId supplied
if (StringUtils.isBlank(siteId)) {
throw new IllegalArgumentException("siteId a must be set in order to get the resources for a site, via the URL /content/site/siteId");
}

// return the ListItem list for the site
return getSiteListItems(siteId);

}

@EntityCustomAction(action="resources", viewKey=EntityView.VIEW_LIST)
public List<EntityContent> getResources(EntityView view, Map<String, Object> params)
throws EntityPermissionException {
Expand Down Expand Up @@ -240,6 +248,18 @@ public List<EntityContent> getResources(EntityView view, Map<String, Object> par
return resourceDetails;
}

@EntityCustomAction(action="htmlForRef", viewKey=EntityView.VIEW_SHOW)
public String getHtmlForRef(EntityView view, Map<String, Object> params) throws EntityPermissionException {

String ref = (String) params.get("ref");

if (StringUtils.isBlank(ref)) {
throw new EntityException("You need to supply the ref parameter.", null, HttpServletResponse.SC_BAD_REQUEST);
}

return contentHostingService.getHtmlForRef(ref).orElse("");
}

/**
*
* @param entity The entity to load details of.
Expand Down Expand Up @@ -703,32 +723,10 @@ private List<ContentItem> getResources(String siteId) {
return items;
}


@Override
public String[] getHandledOutputFormats() {
return new String[] { Formats.XML, Formats.JSON};
return new String[] { Formats.XML, Formats.JSON, Formats.HTML};
}

@Setter
private ContentHostingService contentHostingService;

@Setter
private SiteService siteService;

@Setter
private ToolManager toolManager;

@Setter
private SecurityService securityService;

@Setter
private UserDirectoryService userDirectoryService;

@Setter
private EntityManager entityManager;




/**
* Simplified helper class to represent an individual content item
Expand Down Expand Up @@ -807,6 +805,4 @@ private String getDisplayName(String uuid) {
return null;
}
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.Stack;
import java.util.TreeSet;
Expand Down Expand Up @@ -245,6 +246,10 @@ public interface ContentHostingService extends EntityProducer

static final String ID_LENGTH_EXCEPTION = "id_length_exception";

public static final String DOCX_MIMETYPE
= "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
public static final String ODT_MIMETYPE = "application/vnd.oasis.opendocument.text";

/**
* For a given id, return its UUID (creating it if it does not already exist)
*/
Expand Down Expand Up @@ -2081,4 +2086,5 @@ public void restoreResource(String id) throws PermissionException, IdUsedExcepti

public String expandMacros(String url);

public Optional<String> getHtmlForRef(String ref);
}
26 changes: 26 additions & 0 deletions kernel/kernel-impl/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,32 @@
<artifactId>poi-ooxml</artifactId>
<version>${sakai.poi.version}</version>
</dependency>
<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>fr.opensagres.xdocreport.document</artifactId>
<version>2.0.2</version>
</dependency>
<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>org.apache.poi.xwpf.converter.xhtml</artifactId>
<version>1.0.6</version>
</dependency>
<dependency>
<groupId>org.odftoolkit</groupId>
<artifactId>odftoolkit</artifactId>
<version>1.0.0-BETA1</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>fr.opensagres.xdocreport.converter.odt.odfdom</artifactId>
<version>2.0.2</version>
</dependency>
<dependency>
<groupId>org.zwobble.mammoth</groupId>
<artifactId>mammoth</artifactId>
<version>1.4.1</version>
</dependency>
<dependency>
<groupId>org.sakaiproject.kernel</groupId>
<artifactId>sakai-kernel-private</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.net.SocketException;
import java.net.URI;
Expand All @@ -46,6 +48,7 @@
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Random;
import java.util.Set;
import java.util.SortedSet;
Expand All @@ -71,14 +74,21 @@
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.tika.io.TikaInputStream;
import org.apache.tika.metadata.Metadata;
import fr.opensagres.odfdom.converter.xhtml.XHTMLConverter;

import org.apache.tika.detect.DefaultDetector;
import org.apache.tika.detect.Detector;
import org.apache.tika.io.TikaInputStream;
import org.apache.tika.metadata.Metadata;
import org.apache.tika.mime.MimeTypes;

import org.apache.tika.parser.txt.CharsetDetector;
import org.apache.tika.parser.txt.CharsetMatch;

import org.odftoolkit.odfdom.doc.OdfTextDocument;

import org.zwobble.mammoth.DocumentConverter;
import org.zwobble.mammoth.Result;

import org.sakaiproject.authz.api.AuthzRealmLockException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
Expand Down Expand Up @@ -14350,7 +14360,44 @@ public String expandMacros(String url) {

return url;
}


public Optional<String> getHtmlForRef(String ref) {

try {
ContentResource cr = getResource(ref);

byte[] content = cr.getContent();
String contentType = cr.getContentType();

switch (cr.getContentType()) {
case DOCX_MIMETYPE:
try (InputStream in = cr.streamContent()) {
Result<String> result = new DocumentConverter().convertToHtml(in);
String html = result.getValue();
if (log.isDebugEnabled()) {
result.getWarnings().forEach(w -> log.debug("Warning while converting {} to html: {}", ref, w));
}
return Optional.of(html);
}
case ODT_MIMETYPE:
try (InputStream in = cr.streamContent()) {
OdfTextDocument document = OdfTextDocument.loadDocument(in);
StringWriter sw = new StringWriter();
XHTMLConverter.getInstance().convert( document, sw, null );
return Optional.of(sw.toString());
} catch ( Throwable e ) {
e.printStackTrace();
}

return Optional.of("");
default:
}
} catch (Exception e) {
log.error("Failed to get html for ref {}", ref, e);
}
return Optional.empty();
}

/**
* Helper to get the value for a given macro.
* @param macroName
Expand Down
10 changes: 10 additions & 0 deletions library/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,16 @@
<artifactId>multiselect-two-sides</artifactId>
<version>2.5.5</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>pdf-js</artifactId>
<version>2.3.200</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>viewerjs</artifactId>
<version>0.5.8</version>
</dependency>
</dependencies>
<profiles>
<profile>
Expand Down
33 changes: 33 additions & 0 deletions library/src/morpheus-master/sass/modules/grader/_base.scss
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,39 @@ sakai-grader {
.inline-feedback-button {
margin-top: 8px;
}
.preview {
padding: 5px;
margin-top: 10px;
border: solid black 1px;

sakai-document-viewer {
.document-link { margin-bottom: 8px; font-weight: bold; }

.preview-outer {
background: gray;
text-align: center;
padding: 30px;
.preview-middle {
background: black;
background: white;
}
.preview-inner {
width: 75%;
background: white;
margin-top: 20px;
margin-bottom: 20px;
display: inline-block;
padding: 20 10 20 10;
text-align: left;
}
.nomargins {
width: 100%;
margin-top: 0px;
margin-bottom: 0px;
}
}
}
}
}

#grader-rubric-link {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
failed_to_load_document=Failed to load document. Please use the file link above.
viewing=Viewing
2 changes: 1 addition & 1 deletion webcomponents/bundle/src/main/bundle/grader.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
previewing=Previewing
attempt=Attempt
allow_resubmission=Allow Resubmission
number_resubmissions_allowed=Number of resubmissions allowed
Expand Down Expand Up @@ -64,3 +63,4 @@ checkgrade_label=Checkmark grade input box
comment_present=There is a comment on this submission
notes_present=There are private notes on this submission
profile_image='s profile image
inline_feedback_instruction=This is the submitted text, with your feedback. To add more feedback, click 'Add Feedback' at the bottom of the submission, then click 'Done' when you're finished. <strong>Your changes won't be saved until you click one of the save buttons in the grader.</strong>
10 changes: 5 additions & 5 deletions webcomponents/tool/src/main/frontend/grader/sakai-grader.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import "/webcomponents/fa-icon.js";
import "./sakai-grader-file-picker.js";
import "../sakai-date-picker.js";
import "../sakai-group-picker.js";
import "../sakai-document-viewer.js";
import {gradableDataMixin} from "./sakai-gradable-data-mixin.js";
import {Submission} from "./submission.js";
import "/rubrics-service/webcomponents/rubric-association-requirements.js";
Expand Down Expand Up @@ -147,15 +148,14 @@ class SakaiGrader extends gradableDataMixin(SakaiElement) {
<div class="gradable">
${this.submission.submittedTime ? html`
${this.submittedTextMode ? html`
<div class="sak-banner-info">This is the submitted text, with your feedback. To add more feedback, click 'Add Feedback' at
the bottom of the submission, then click 'Done' when you're finished. <strong>Your changes won't be saved until you click one of the save buttons in the grader.</strong></div>
<div class="sak-banner-info">${unsafeHTML(this.i18n["inline_feedback_instruction"])}</div>
<textarea id="grader-feedback-text-editor" style="display: none">${this.submission.feedbackText}</textarea>
<div id="grader-feedback-text">${unsafeHTML(this.submission.feedbackText)}</div>
<button id="edit-inline-feedback-button" class="inline-feedback-button" @click=${this.toggleInlineFeedback} aria-haspopup="true">${this.i18n["add_feedback"]}</button>
<button id="show-inline-feedback-button" class="inline-feedback-button" @click=${this.toggleInlineFeedback} aria-haspopup="true" style="display: none;">Done</button>
` : html`
${this.selectedAttachmentRef ? html`
<div>${this.i18n["previewing"]}: <a href="/access${this.selectedAttachmentRef}">${this.fileNameFromRef(this.selectedAttachmentRef)}</a></div>
<div class="preview"><sakai-document-viewer ref="${this.selectedAttachmentRef}"></sakai-document-viewer></div>
` : ""}
`}
` : ""}
Expand Down Expand Up @@ -191,8 +191,8 @@ class SakaiGrader extends gradableDataMixin(SakaiElement) {
<div class="attachments">
${this.submission.submittedAttachments.length > 0 ? html`
<div class="attachments-header">${this.i18n["submitted_attachments"]}:</div>
${Object.keys(this.submission.submittedAttachments).map(k => html`
<span class="attachment-link"><a href="javascript;" data-url="${this.submission.submittedAttachments[k]}" @click=${this.previewAttachment}>${parseInt(k) + 1}</a></span>
${this.submission.submittedAttachments.map(r => html`
<span class="attachment-link"><a href="javascript;" data-url="${r}" @click=${this.previewAttachment}>${this.fileNameFromRef(r)}</a></span>
`)}` : ""}
</div>
</div> <!-- /submitted-block -->
Expand Down
Loading

0 comments on commit 18a64c7

Please sign in to comment.