diff --git a/admin-tools/src/bundle/archive_es.properties b/admin-tools/src/bundle/archive_es.properties
index a7d425e87188..7a4c4e6202d2 100644
--- a/admin-tools/src/bundle/archive_es.properties
+++ b/admin-tools/src/bundle/archive_es.properties
@@ -5,7 +5,8 @@ archive=Guardando herramientas y contenidos del sitio de la asignatura {0}
archive.file=Por favor, indique un nombre de fichero y un sitio id para importar.
archive.import=la importaci\u00f3n est\u00e1 limitada a administradores.\n
archive.import1=importar {0}\: desde fichero {1} al sitio {2} con el id de creador {3} completar.
-archive.import2=importar desde fichero {0} al sitio {2} completar. \n
+archive.import2=importar desde fichero {0} al sitio {1} finalizado. \n
+archive.import3=No se pudo realizar la importaci\u00f3n desde el fichero {0}. La extensi\u00f3n del fichero no es correcta.
archive.limited=archivar est\u00e1 limitado a administradores.\n
archive.please=Por favor, indique un sitio para archivar.
archive.vm.alert=Alerta\:
diff --git a/admin-tools/src/java/org/sakaiproject/archive/tool/ArchiveAction.java b/admin-tools/src/java/org/sakaiproject/archive/tool/ArchiveAction.java
index 8585707fc678..afa641b6dbab 100644
--- a/admin-tools/src/java/org/sakaiproject/archive/tool/ArchiveAction.java
+++ b/admin-tools/src/java/org/sakaiproject/archive/tool/ArchiveAction.java
@@ -347,6 +347,9 @@ public void doImport(RunData data, Context context)
&& (file != null) && (file.trim().length() > 0))
{
String msg = archiveService.merge(file.trim(), id.trim(), null);
+ if (StringUtils.isBlank(msg)) {
+ msg = rb.getFormattedMessage("archive.import3", new Object[]{file});
+ }
addAlert(state, rb.getFormattedMessage("archive.import2", new Object[]{file, id}) + msg);
}
else
diff --git a/assignment/api/pom.xml b/assignment/api/pom.xml
index 74d504377e85..35da936e446a 100644
--- a/assignment/api/pom.xml
+++ b/assignment/api/pom.xml
@@ -52,5 +52,9 @@
org.sakaiproject.contentreview
content-review-api
+
+ com.fasterxml.jackson.core
+ jackson-annotations
+
diff --git a/assignment/api/src/java/org/sakaiproject/assignment/api/AssignmentService.java b/assignment/api/src/java/org/sakaiproject/assignment/api/AssignmentService.java
index 78feba6d647e..7fd1a0be12ee 100644
--- a/assignment/api/src/java/org/sakaiproject/assignment/api/AssignmentService.java
+++ b/assignment/api/src/java/org/sakaiproject/assignment/api/AssignmentService.java
@@ -23,7 +23,11 @@
import java.io.OutputStream;
import java.time.Instant;
-import java.util.*;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
import org.sakaiproject.assignment.api.model.Assignment;
import org.sakaiproject.assignment.api.model.AssignmentSubmission;
@@ -38,7 +42,6 @@
import org.sakaiproject.site.api.Group;
import org.sakaiproject.site.api.Site;
import org.sakaiproject.user.api.User;
-import org.w3c.dom.Element;
/**
*
@@ -274,17 +277,6 @@ public interface AssignmentService extends EntityProducer {
*/
public Assignment addAssignment(String context) throws PermissionException;
- /**
- * Add a new assignment to the directory, from a definition in XML. Must commitEdit() to make official, or cancelEdit() when done!
- *
- * @param el The XML DOM Element defining the assignment.
- * @return A locked AssignmentEdit object (reserving the id).
- * @throws IdInvalidException if the assignment id is invalid.
- * @throws IdUsedException if the assignment id is already used.
- * @throws PermissionException if the current user does not have permission to add an assignnment.
- */
- public Assignment mergeAssignment(Element el) throws IdInvalidException, IdUsedException, PermissionException;
-
/**
* Creates and adds a new Assignment to the service which is a copy of an existing Assignment.
*
@@ -331,17 +323,6 @@ public interface AssignmentService extends EntityProducer {
*/
public AssignmentSubmission addSubmission(String assignmentId, String submitter) throws PermissionException;
- /**
- * Add a new AssignmentSubmission to the directory, from a definition in XML. Must commitEdit() to make official, or cancelEdit() when done!
- *
- * @param el The XML DOM Element defining the submission.
- * @return A locked AssignmentSubmission object (reserving the id).
- * @throws IdInvalidException if the submission id is invalid.
- * @throws IdUsedException if the submission id is already used.
- * @throws PermissionException if the current user does not have permission to add a submission.
- */
- public AssignmentSubmission mergeSubmission(Element el) throws IdInvalidException, IdUsedException, PermissionException;
-
/**
* Removes an AssignmentSubmission and all references to it
*
diff --git a/assignment/api/src/java/org/sakaiproject/assignment/api/AssignmentServiceConstants.java b/assignment/api/src/java/org/sakaiproject/assignment/api/AssignmentServiceConstants.java
index d1a9c648b791..e0cace45927b 100644
--- a/assignment/api/src/java/org/sakaiproject/assignment/api/AssignmentServiceConstants.java
+++ b/assignment/api/src/java/org/sakaiproject/assignment/api/AssignmentServiceConstants.java
@@ -125,6 +125,9 @@ public final class AssignmentServiceConstants {
AssignmentServiceConstants.NEW_ASSIGNMENT_ADD_TO_GRADEBOOK,
AssignmentServiceConstants.PROP_ASSIGNMENT_ASSOCIATE_GRADEBOOK_ASSIGNMENT)));
+ public static final String LINE_SEPARATOR = System.getProperty("line.separator");
+ public static final String SAK_PROP_ASSIGNMENT_IMPORT_SUBMISSIONS = "assignment.merge.import.submissions";
+
private AssignmentServiceConstants() {
throw new RuntimeException(this.getClass().getCanonicalName() + " is not to be instantiated");
}
diff --git a/assignment/api/src/java/org/sakaiproject/assignment/api/model/Assignment.java b/assignment/api/src/java/org/sakaiproject/assignment/api/model/Assignment.java
index 0d25f5a6cdfb..75949934147a 100644
--- a/assignment/api/src/java/org/sakaiproject/assignment/api/model/Assignment.java
+++ b/assignment/api/src/java/org/sakaiproject/assignment/api/model/Assignment.java
@@ -53,6 +53,10 @@
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Type;
+import com.fasterxml.jackson.annotation.JsonIdentityInfo;
+import com.fasterxml.jackson.annotation.JsonManagedReference;
+import com.fasterxml.jackson.annotation.ObjectIdGenerators;
+
/**
* Assignment represents a specific assignment for a specific section or class.
*
@@ -97,6 +101,7 @@
@NoArgsConstructor
@ToString(exclude = {"authors", "submissions", "groups", "properties", "attachments"})
@EqualsAndHashCode(of = "id")
+@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class Assignment {
@Id
@@ -168,6 +173,7 @@ public class Assignment {
private Integer position;
@OneToMany(mappedBy = "assignment", cascade = CascadeType.ALL, orphanRemoval = true)
+ @JsonManagedReference
private Set submissions = new HashSet<>();
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
diff --git a/assignment/api/src/java/org/sakaiproject/assignment/api/model/AssignmentSubmission.java b/assignment/api/src/java/org/sakaiproject/assignment/api/model/AssignmentSubmission.java
index 871b259ad252..1cf2f21a3229 100644
--- a/assignment/api/src/java/org/sakaiproject/assignment/api/model/AssignmentSubmission.java
+++ b/assignment/api/src/java/org/sakaiproject/assignment/api/model/AssignmentSubmission.java
@@ -56,6 +56,11 @@
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Type;
+import com.fasterxml.jackson.annotation.JsonBackReference;
+import com.fasterxml.jackson.annotation.JsonIdentityInfo;
+import com.fasterxml.jackson.annotation.JsonManagedReference;
+import com.fasterxml.jackson.annotation.ObjectIdGenerators;
+
/**
* AssignmentSubmission represents a student submission for an assignment.
*/
@@ -67,6 +72,7 @@
@NoArgsConstructor
@ToString(exclude = {"assignment", "submitters", "attachments", "feedbackAttachments", "properties"})
@EqualsAndHashCode(of = "id")
+@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class AssignmentSubmission {
@Id
@@ -77,11 +83,13 @@ public class AssignmentSubmission {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "ASSIGNMENT_ID")
+ @JsonBackReference
private Assignment assignment;
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
@OneToMany(mappedBy = "submission", fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
@BatchSize(size = 100)
+ @JsonManagedReference
private Set submitters = new HashSet<>();
//private List submissionLog;
diff --git a/assignment/api/src/java/org/sakaiproject/assignment/api/model/AssignmentSubmissionSubmitter.java b/assignment/api/src/java/org/sakaiproject/assignment/api/model/AssignmentSubmissionSubmitter.java
index 659309d3e975..3c03233a0570 100644
--- a/assignment/api/src/java/org/sakaiproject/assignment/api/model/AssignmentSubmissionSubmitter.java
+++ b/assignment/api/src/java/org/sakaiproject/assignment/api/model/AssignmentSubmissionSubmitter.java
@@ -28,6 +28,10 @@
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
+import com.fasterxml.jackson.annotation.JsonBackReference;
+import com.fasterxml.jackson.annotation.JsonIdentityInfo;
+import com.fasterxml.jackson.annotation.ObjectIdGenerators;
+
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@@ -53,6 +57,7 @@
@NoArgsConstructor
@ToString(exclude = {"submission"})
@EqualsAndHashCode(of = {"submission", "submitter"})
+@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class AssignmentSubmissionSubmitter {
@Id
@@ -63,6 +68,7 @@ public class AssignmentSubmissionSubmitter {
@ManyToOne
@JoinColumn(name = "SUBMISSION_ID", nullable = false)
+ @JsonBackReference
private AssignmentSubmission submission;
@Column(name = "SUBMITTER", length = 99, nullable = false)
diff --git a/assignment/impl/pom.xml b/assignment/impl/pom.xml
index 82ab44b1a1a5..b887d01e90d7 100644
--- a/assignment/impl/pom.xml
+++ b/assignment/impl/pom.xml
@@ -155,10 +155,18 @@
com.fasterxml.jackson.dataformat
jackson-dataformat-xml
+
+ com.fasterxml.jackson.module
+ jackson-module-parameter-names
+
com.fasterxml.jackson.datatype
jackson-datatype-jdk8
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-jsr310
+
com.fasterxml.woodstox
woodstox-core
diff --git a/assignment/impl/src/java/org/sakaiproject/assignment/impl/AssignmentServiceImpl.java b/assignment/impl/src/java/org/sakaiproject/assignment/impl/AssignmentServiceImpl.java
index a691fa29211d..731130f3bfa2 100644
--- a/assignment/impl/src/java/org/sakaiproject/assignment/impl/AssignmentServiceImpl.java
+++ b/assignment/impl/src/java/org/sakaiproject/assignment/impl/AssignmentServiceImpl.java
@@ -53,6 +53,8 @@
import java.util.Stack;
import java.util.StringTokenizer;
import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
@@ -179,6 +181,9 @@
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.ls.DOMImplementationLS;
+import org.w3c.dom.ls.LSSerializer;
import org.xml.sax.InputSource;
import lombok.Setter;
@@ -191,7 +196,7 @@
@Transactional(readOnly = true)
public class AssignmentServiceImpl implements AssignmentService, EntityTransferrer, ApplicationContextAware {
- @Setter private AnnouncementService announcementService;
+ @Setter private AnnouncementService announcementService;
@Setter private ApplicationContext applicationContext;
@Setter private AssignmentActivityProducer assignmentActivityProducer;
@Setter private AssignmentDueReminderService assignmentDueReminderService;
@@ -284,8 +289,8 @@ public boolean willArchiveMerge() {
@Override
public String archive(String siteId, Document doc, Stack stack, String archivePath, List attachments) {
- String message = "archiving " + getLabel() + " context " + Entity.SEPARATOR + siteId + Entity.SEPARATOR + SiteService.MAIN_CONTAINER + ".\n";
- log.debug(message);
+ final StringBuilder results = new StringBuilder();
+ results.append("begin archiving ").append(getLabel()).append(" context ").append(siteId).append(LINE_SEPARATOR);
// start with an element with our very own (service) name
Element element = doc.createElement(AssignmentService.class.getName());
@@ -293,6 +298,7 @@ public String archive(String siteId, Document doc, Stack stack, String
stack.push(element);
Collection assignments = getAssignmentsForContext(siteId);
+ int assignmentsArchived = 0;
for (Assignment assignment : assignments) {
String xml = assignmentRepository.toXML(assignment);
@@ -302,6 +308,7 @@ public String archive(String siteId, Document doc, Stack stack, String
Element assignmentElement = assignmentDocument.getDocumentElement();
Node assignmentNode = doc.importNode(assignmentElement, true);
element.appendChild(assignmentNode);
+ assignmentsArchived++;
} catch (Exception e) {
log.warn("could not append assignment {} to archive, {}", assignment.getId(), e.getMessage());
}
@@ -309,12 +316,34 @@ public String archive(String siteId, Document doc, Stack stack, String
stack.pop();
- return message;
+ results.append("completed archiving ").append(getLabel()).append(" context ").append(siteId).append(" count (").append(assignmentsArchived).append(")").append(LINE_SEPARATOR);
+ return results.toString();
}
@Override
+ @Transactional
public String merge(String siteId, Element root, String archivePath, String fromSiteId, Map attachmentNames, Map userIdTrans, Set userListAllowImport) {
- return null;
+
+ final StringBuilder results = new StringBuilder();
+ results.append("begin merging ").append(getLabel()).append(" context ").append(siteId).append(LINE_SEPARATOR);
+ final NodeList allChildrenNodeList = root.getChildNodes();
+ final Stream allChildrenNodes = IntStream.range(0, allChildrenNodeList.getLength()).mapToObj(allChildrenNodeList::item);
+ final List assignmentElements = allChildrenNodes.filter(node -> node.getNodeType() == Node.ELEMENT_NODE).map(element -> (Element) element).collect(Collectors.toList());
+
+ int assignmentsMerged = 0;
+
+ for (Element assignmentElement : assignmentElements) {
+ try {
+ mergeAssignment(siteId, assignmentElement, results);
+ assignmentsMerged++;
+ } catch (Exception e) {
+ final String error = "could not merge assignment with id: " + assignmentElement.getFirstChild().getFirstChild().getNodeValue();
+ log.warn(error, e);
+ results.append(error).append(LINE_SEPARATOR);
+ }
+ }
+ results.append("completed merging ").append(getLabel()).append(" context ").append(siteId).append(" count (").append(assignmentsMerged).append(")").append(LINE_SEPARATOR);
+ return results.toString();
}
@Override
@@ -768,14 +797,52 @@ public Assignment addAssignment(String context) throws PermissionException {
return assignment;
}
- @Override
- @Transactional
- public Assignment mergeAssignment(Element el) throws IdInvalidException, IdUsedException, PermissionException {
- // TODO need to write a test for this
- // this may also need to handle submission serialization?
- Assignment assignmentFromXml = assignmentRepository.fromXML(el.toString());
+ private Assignment mergeAssignment(final String siteId, final Element element, final StringBuilder results) throws PermissionException {
- return addAssignment(assignmentFromXml.getContext());
+ if (!allowAddAssignment(siteId)) {
+ throw new PermissionException(sessionManager.getCurrentSessionUserId(), SECURE_ADD_ASSIGNMENT, AssignmentReferenceReckoner.reckoner().context(siteId).reckon().getReference());
+ }
+
+ // Serialize the element to a String
+ DOMImplementationLS dom = (DOMImplementationLS) element.getOwnerDocument().getImplementation();
+ LSSerializer domSerializer = dom.createLSSerializer();
+ domSerializer.getDomConfig().setParameter("xml-declaration", false);
+ final String xml = domSerializer.writeToString(element);
+
+ // Get an assignment object from the xml
+ final Assignment assignmentFromXml = assignmentRepository.fromXML(xml);
+ if (assignmentFromXml != null) {
+ assignmentFromXml.setId(null);
+ assignmentFromXml.setContext(siteId);
+
+ if (serverConfigurationService.getBoolean(SAK_PROP_ASSIGNMENT_IMPORT_SUBMISSIONS, false) && !assignmentFromXml.getIsGroup()) {
+ // here it's imported exactly as it was including all submissions
+ // except for group submissions as group ids will never be the same
+ Set submissions = assignmentFromXml.getSubmissions();
+ List submitters = submissions.stream().flatMap(s -> s.getSubmitters().stream()).map(AssignmentSubmissionSubmitter::getSubmitter).collect(Collectors.toList());
+ // only if all submitters can be found do we import submissions
+ if (submitters.containsAll(userDirectoryService.getUsers(submitters))) {
+ submissions.forEach(s -> s.setId(null));
+ submissions.forEach(s -> s.getSubmitters().forEach(u -> u.setId(null)));
+ }
+ } else {
+ // here it is importing the assignment only
+ assignmentFromXml.setDraft(true);
+ assignmentFromXml.setAttachments(new HashSet<>());
+ assignmentFromXml.setGroups(new HashSet<>());
+ assignmentFromXml.setTypeOfAccess(SITE);
+ Map properties = assignmentFromXml.getProperties().entrySet().stream()
+ .filter(e -> !PROPERTIES_EXCLUDED_FROM_DUPLICATE_ASSIGNMENTS.contains(e.getKey()))
+ .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
+ assignmentFromXml.setProperties(properties);
+ assignmentFromXml.setSubmissions(new HashSet<>());
+ }
+ assignmentRepository.newAssignment(assignmentFromXml);
+ String result = "merging assignment " + assignmentFromXml.getId() + " with " + assignmentFromXml.getSubmissions().size() + " submissions.";
+ results.append(result).append(LINE_SEPARATOR);
+ log.debug(result);
+ }
+ return assignmentFromXml;
}
@Override
@@ -1110,38 +1177,6 @@ public AssignmentSubmission addSubmission(String assignmentId, String submitter)
return null;
}
- @Override
- public AssignmentSubmission mergeSubmission(Element el) throws IdInvalidException, IdUsedException, PermissionException {
- // TODO this will probably be handled in merge Assignments as submissions are children of assignments
-// AssignmentSubmission submissionFromXml = new AssignmentSubmission();
-//
-// // check for a valid submission name
-// if (!Validator.checkResourceId(submissionFromXml.getId())) throw new IdInvalidException(submissionFromXml.getId());
-//
-// // check security (throws if not permitted)
-// unlock(SECURE_ADD_ASSIGNMENT_SUBMISSION, submissionFromXml.getReference());
-//
-// // reserve a submission with this id from the info store - if it's in use, this will return null
-// AssignmentSubmissionEdit submission = m_submissionStorage.put( submissionFromXml.getId(),
-// submissionFromXml.getAssignmentId(),
-// submissionFromXml.getSubmitterIdString(),
-// (submissionFromXml.getTimeSubmitted() != null)?String.valueOf(submissionFromXml.getTimeSubmitted().getTime()):null,
-// Boolean.valueOf(submissionFromXml.getSubmitted()).toString(),
-// Boolean.valueOf(submissionFromXml.getGraded()).toString());
-// if (submission == null)
-// {
-// throw new IdUsedException(submissionFromXml.getId());
-// }
-//
-// // transfer from the XML read submission object to the SubmissionEdit
-// ((BaseAssignmentSubmissionEdit) submission).set(submissionFromXml);
-//
-// ((BaseAssignmentSubmissionEdit) submission).setEvent(AssignmentConstants.EVENT_ADD_ASSIGNMENT_SUBMISSION);
-//
-// return submission;
- return null;
- }
-
@Override
@Transactional
public void removeSubmission(AssignmentSubmission submission) throws PermissionException {
diff --git a/assignment/impl/src/test/org/sakaiproject/assignment/impl/AssignmentServiceTest.java b/assignment/impl/src/test/org/sakaiproject/assignment/impl/AssignmentServiceTest.java
index 65023a6ecabc..aeec561f6cc1 100644
--- a/assignment/impl/src/test/org/sakaiproject/assignment/impl/AssignmentServiceTest.java
+++ b/assignment/impl/src/test/org/sakaiproject/assignment/impl/AssignmentServiceTest.java
@@ -22,9 +22,14 @@
package org.sakaiproject.assignment.impl;
import static org.hamcrest.CoreMatchers.is;
+import static org.mockito.ArgumentMatchers.anyCollection;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.time.Duration;
@@ -81,12 +86,17 @@
import org.sakaiproject.user.api.User;
import org.sakaiproject.user.api.UserNotDefinedException;
import org.sakaiproject.util.ResourceLoader;
+import org.sakaiproject.util.Xml;
import org.sakaiproject.util.api.FormattedText;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
import com.github.javafaker.Faker;
@@ -923,6 +933,52 @@ public void gradeUpdateFromAssignmentEventObeserver() {
}
}
+ @Test
+ public void mergeAssignmentFromXML() {
+ String context = UUID.randomUUID().toString();
+ String xml = readResourceToString("/importAssignment.xml");
+ Document doc = Xml.readDocument(xml);
+
+ if (doc != null) {
+ // Mock everything needed to have permission
+ Site siteMock = mock(Site.class);
+ Collection groupCollection = new ArrayList<>();
+ Group groupMock = mock(Group.class);
+ when(groupMock.getReference()).thenReturn("reference");
+ groupCollection.add(groupMock);
+ when(siteMock.getGroups()).thenReturn(groupCollection);
+ Set references = new HashSet<>();
+ references.add("reference");
+ when(authzGroupService.getAuthzGroupsIsAllowed(anyString(), anyString(), anyCollection())).thenReturn(references);
+ try {
+ when(siteService.getSite(context)).thenReturn(siteMock);
+ } catch (IdUnusedException e) {
+ Assert.fail("Site mock failed");
+ }
+
+ when(securityService.unlock("asn.new", "/assignment/a/SITE_ID")).thenReturn(true);
+ when(securityService.unlock("asn.revise", "/assignment/a/SITE_ID")).thenReturn(true);
+ when(securityService.unlock("asn.read", "/assignment/a/SITE_ID")).thenReturn(true);
+
+ // verify the root element
+ Element root = doc.getDocumentElement();
+ // the children
+ NodeList children = root.getChildNodes();
+ int length = children.getLength();
+
+ for (int i = 0; i < length; i++) {
+ Node child = children.item(i);
+ if (child.getNodeType() != Node.ELEMENT_NODE) continue;
+
+ Element element = (Element) child;
+ if ("org.sakaiproject.assignment.api.AssignmentService".equals(element.getTagName())) {
+ assignmentService.merge(context, element, null, null, null, null, null);
+ Assert.assertEquals(1, assignmentService.getAssignmentsForContext(context).size());
+ }
+ }
+ }
+ }
+
private AssignmentSubmission createNewSubmission(String context, String submitterId) throws UserNotDefinedException, IdUnusedException {
Assignment assignment = createNewAssignment(context);
String addSubmissionRef = AssignmentReferenceReckoner.reckoner().context(context).subtype("s").reckon().getReference();
@@ -1057,4 +1113,10 @@ private Event createMockEvent(String context, String gradebookId, Long itemId, S
when(event.getContext()).thenReturn(context);
return event;
}
+
+ private String readResourceToString(String resource) {
+ InputStream is = this.getClass().getResourceAsStream(resource);
+ BufferedReader br = new BufferedReader(new InputStreamReader(is));
+ return br.lines().collect(Collectors.joining("\n"));
+ }
}
diff --git a/assignment/impl/src/test/resources/importAssignment.xml b/assignment/impl/src/test/resources/importAssignment.xml
new file mode 100644
index 000000000000..b0f28cdbda86
--- /dev/null
+++ b/assignment/impl/src/test/resources/importAssignment.xml
@@ -0,0 +1,112 @@
+
+
+
+
+ 12345678-abcd-1234-abcd-123456789abc
+ Title
+ Instructions
+ SITE_ID
+
+ 1511517810.303000000
+ 1315415101.522000000
+
+ 1530396000.000000000
+ 1531259700.000000000
+ 1531346100.000000000
+ 1531259700.000000000
+ 12345678-abcd-1234-abcd-123456789ab1
+ 12345678-abcd-1234-abcd-123456789ab2
+ false
+ false
+ false
+ false
+ 0
+
+
+ 12345678-abcd-1234-abcd-123456789ab3
+
+
+ 00001
+ 12345678-abcd-1234-abcd-123456789ab4
+ true
+ gen.nograd
+
+
+
+ 1513276322.014000000
+ 1516957649.084000000
+ 1513155622.517000000
+ 1516957649.084000000
+
+
+
+ FeedbackComment
+
+ gen.nograd
+ 0
+ true
+ true
+ true
+ 12345678-abcd-1234-abcd-123456789ab5
+ true
+ false
+ false
+ true
+
+
+ 12345678-abcd-1234-abcd-123456789ab6
+ 20171213090022517
+ 12345678-abcd-1234-abcd-123456789ab7
+ 20180126090729084
+ 23-dic-2017 11:15
+ Thu Dec 14 19:32:02 CET 2017 USER submitted
+
+
+
+
+ 0
+ false
+ false
+ assignment_instructor_notifications_none
+ 12345678-abcd-1234-abcd-123456789ab8
+ false
+ false
+ 0
+ 20180621100207763
+ assignment_releasereturn_notification_none
+ false
+ false
+ 1
+ true
+ 12345678-abcd-1234-abcd-123456789ab9
+ false
+ false
+ no
+ false
+ 20171124100330341
+ false
+ 0
+ 12345678-abcd-1234-abcd-123456789a10
+ assignment_releasegrade_notification_none
+
+
+
+ SITE
+ false
+ TEXT_AND_ATTACHMENT_ASSIGNMENT_SUBMISSION
+ UNGRADED_GRADE_TYPE
+ 0
+ 10
+ false
+ false
+ true
+ false
+ 1416611100.000000000
+ false
+ false
+ 0
+
+ false
+
+
+
\ No newline at end of file
diff --git a/config/configuration/bundles/src/bundle/org/sakaiproject/config/bundle/default.sakai.properties b/config/configuration/bundles/src/bundle/org/sakaiproject/config/bundle/default.sakai.properties
index c0e358bc3451..3ca28213dc8e 100644
--- a/config/configuration/bundles/src/bundle/org/sakaiproject/config/bundle/default.sakai.properties
+++ b/config/configuration/bundles/src/bundle/org/sakaiproject/config/bundle/default.sakai.properties
@@ -1984,6 +1984,10 @@
# Default: false
# assignment.show.official.photo=true
+# Assignment archive should import submissions during merge
+# DEFAULT: false (import only the assignment no submissions)
+# assignment.merge.import.submissions=true
+
# ######################################
# SAK-29406 Allow Assignment tool to grade with two decimal points
# ######################################
diff --git a/deploy/pom.xml b/deploy/pom.xml
index e738a248cc5b..421d1674b690 100644
--- a/deploy/pom.xml
+++ b/deploy/pom.xml
@@ -253,6 +253,12 @@
jackson-dataformat-xml
compile
+
+ com.fasterxml.jackson.module
+ jackson-module-parameter-names
+ ${sakai.jackson.version}
+ compile
+
com.fasterxml.jackson.datatype
jackson-datatype-jdk8
diff --git a/kernel/kernel-private/pom.xml b/kernel/kernel-private/pom.xml
index bd61d8c4396d..bd809a522424 100644
--- a/kernel/kernel-private/pom.xml
+++ b/kernel/kernel-private/pom.xml
@@ -69,7 +69,23 @@
com.fasterxml.jackson.dataformat
jackson-dataformat-xml
- ${sakai.jackson.version}
+
+
+ com.fasterxml.woodstox
+ woodstox-core
+
+
+ com.fasterxml.jackson.module
+ jackson-module-parameter-names
+ ${sakai.jackson.version}
+
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-jdk8
+
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-jsr310
net.sf.ehcache
diff --git a/kernel/kernel-private/src/main/java/org/sakaiproject/hibernate/CrudRepository.java b/kernel/kernel-private/src/main/java/org/sakaiproject/hibernate/CrudRepository.java
index 82f86027ceeb..6a508ad7d52e 100644
--- a/kernel/kernel-private/src/main/java/org/sakaiproject/hibernate/CrudRepository.java
+++ b/kernel/kernel-private/src/main/java/org/sakaiproject/hibernate/CrudRepository.java
@@ -115,5 +115,7 @@ public interface CrudRepository extends Repository${sakai.jackson.version}
provided
+
+ com.fasterxml.jackson.module
+ jackson-module-parameter-names
+ ${sakai.jackson.version}
+ provided
+
com.fasterxml.jackson.datatype
jackson-datatype-jdk8
@@ -567,6 +573,11 @@
${sakai.jackson.version}
provided
+
+ com.fasterxml.jackson.module
+ jackson-modules-java8
+ ${sakai.jackson.version}
+
com.fasterxml.jackson.module
jackson-module-jaxb-annotations