diff --git a/common/archive-impl/impl2/src/java/org/sakaiproject/archive/impl/SiteArchiver.java b/common/archive-impl/impl2/src/java/org/sakaiproject/archive/impl/SiteArchiver.java index 7fd154ccd889..372be1cf6df8 100644 --- a/common/archive-impl/impl2/src/java/org/sakaiproject/archive/impl/SiteArchiver.java +++ b/common/archive-impl/impl2/src/java/org/sakaiproject/archive/impl/SiteArchiver.java @@ -312,7 +312,7 @@ protected String archiveSite(Site site, Document doc, Stack stack, String fromSy if ( pattern != null ) { NodeList nl = siteNode.getElementsByTagName("property"); - List toRemove = new ArrayList(); + List toRemove = new ArrayList<>(); for(int i = 0; i < nl.getLength(); i++) { Element proptag = (Element)nl.item(i); diff --git a/common/archive-impl/impl2/src/webapp/WEB-INF/components.xml b/common/archive-impl/impl2/src/webapp/WEB-INF/components.xml index b3d1df40d644..a697d2bce781 100644 --- a/common/archive-impl/impl2/src/webapp/WEB-INF/components.xml +++ b/common/archive-impl/impl2/src/webapp/WEB-INF/components.xml @@ -32,6 +32,7 @@ DiscussionForumService WebService LessonBuilderEntityProducer + rubrics 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 a4d10cd2fc77..66ae3a7a00ae 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 @@ -533,7 +533,7 @@ # archive.merge.filter.services=false # List of data service types that can merge in data from an archive -# DEFAULT: AnnouncementService,AssignmentService,ContentHostingService,CalendarService,ChatEntityProducer,DiscussionService,MailArchiveService,SyllabusService,RWikiObjectService,DiscussionForumService,WebService,LessonBuilderEntityProducer +# DEFAULT: AnnouncementService,AssignmentService,ContentHostingService,CalendarService,ChatEntityProducer,DiscussionService,MailArchiveService,SyllabusService,RWikiObjectService,DiscussionForumService,WebService,LessonBuilderEntityProducer,RubricsService # archive.merge.filtered.services={list of service names} # Controls if user role filtering is enabled. If enabled, any user roles not in the list cannot archive or merge data diff --git a/rubrics/api/src/main/java/org/sakaiproject/rubrics/api/RubricsConstants.java b/rubrics/api/src/main/java/org/sakaiproject/rubrics/api/RubricsConstants.java index 3d80f5a34e40..c2afba871743 100644 --- a/rubrics/api/src/main/java/org/sakaiproject/rubrics/api/RubricsConstants.java +++ b/rubrics/api/src/main/java/org/sakaiproject/rubrics/api/RubricsConstants.java @@ -24,27 +24,27 @@ public interface RubricsConstants { - public static final String RBCS_TOOL = "sakai.rubrics"; - public static final String RBCS_TOOL_ASSIGNMENT_GRADES = "sakai.assignment.grades"; - public static final String RBCS_TOOL_FORUMS = "sakai.forums"; - public static final String RBCS_TOOL_GRADEBOOKNG = "sakai.gradebookng"; - public static final String RBCS_TOOL_SAMIGO = "sakai.samigo"; - public static final String RBCS_TOOL_LESSONBUILDERTOOL = "sakai.lessonbuildertool"; + public static final String RBCS_TOOL = "sakai.rubrics"; + public static final String RBCS_TOOL_ASSIGNMENT_GRADES = "sakai.assignment.grades"; + public static final String RBCS_TOOL_FORUMS = "sakai.forums"; + public static final String RBCS_TOOL_GRADEBOOKNG = "sakai.gradebookng"; + public static final String RBCS_TOOL_SAMIGO = "sakai.samigo"; + public static final String RBCS_TOOL_LESSONBUILDERTOOL = "sakai.lessonbuildertool"; - public static final String RBCS_PREFIX = "rbcs-"; - public static final String RBCS_CONFIG_PREFIX = "config-"; - public static final String RBCS_CONFIG = RBCS_PREFIX + RBCS_CONFIG_PREFIX; - public static final String RBCS_MULTIPLE_OPTIONS_CONFIG = RBCS_PREFIX + "multiple-options-config-"; - public static final String RBCS_ASSOCIATION_STATE_DETAILS = RBCS_PREFIX + "state-details"; - public static final String RBCS_ASSOCIATE_SUFFIX = "associate"; - public static final String RBCS_ASSOCIATE = RBCS_PREFIX + RBCS_ASSOCIATE_SUFFIX;// values: 0 or empty no association, 1 regular association, 2 dynamic rubric in Samigo - public static final String RBCS_LIST_SUFFIX = "rubricslist"; - public static final String RBCS_LIST = RBCS_PREFIX + RBCS_LIST_SUFFIX; - public static final String RBCS_SOFT_DELETED = RBCS_PREFIX + "soft-deleted"; - public static final String RBCS_ASSESSOR_ID = "assessorId"; - public static final String RBCS_HAS_ASSOCIATED_RUBRIC = "hasAssociatedRubric"; - public static final String RBCS_STUDENT_SELF_REPORT = "studentSelfReport"; - public static final String RBCS_STUDENT_SELF_REPORT_MODE = "studentSelfReportMode"; + public static final String RBCS_PREFIX = "rbcs-"; + public static final String RBCS_CONFIG_PREFIX = "config-"; + public static final String RBCS_CONFIG = RBCS_PREFIX + RBCS_CONFIG_PREFIX; + public static final String RBCS_MULTIPLE_OPTIONS_CONFIG = RBCS_PREFIX + "multiple-options-config-"; + public static final String RBCS_ASSOCIATION_STATE_DETAILS = RBCS_PREFIX + "state-details"; + public static final String RBCS_ASSOCIATE_SUFFIX = "associate"; + public static final String RBCS_ASSOCIATE = RBCS_PREFIX + RBCS_ASSOCIATE_SUFFIX;// values: 0 or empty no association, 1 regular association, 2 dynamic rubric in Samigo + public static final String RBCS_LIST_SUFFIX = "rubricslist"; + public static final String RBCS_LIST = RBCS_PREFIX + RBCS_LIST_SUFFIX; + public static final String RBCS_SOFT_DELETED = RBCS_PREFIX + "soft-deleted"; + public static final String RBCS_ASSESSOR_ID = "assessorId"; + public static final String RBCS_HAS_ASSOCIATED_RUBRIC = "hasAssociatedRubric"; + public static final String RBCS_STUDENT_SELF_REPORT = "studentSelfReport"; + public static final String RBCS_STUDENT_SELF_REPORT_MODE = "studentSelfReportMode"; public static final String RBCS_PERMISSIONS_EVALUATOR = "rubrics.evaluator"; public static final String RBCS_PERMISSIONS_EDITOR = "rubrics.editor"; @@ -53,12 +53,13 @@ public interface RubricsConstants { public static final String RBCS_EXPORT_PDF = "rubrics.export.pdf"; - //samigo custom props - public static final String RBCS_PUBLISHED_ASSESSMENT_ENTITY_PREFIX = "pub."; + //samigo custom props + public static final String RBCS_PUBLISHED_ASSESSMENT_ENTITY_PREFIX = "pub."; - //forums custom props - public static final String RBCS_FORUM_ENTITY_PREFIX = "for."; - public static final String RBCS_TOPIC_ENTITY_PREFIX = "top."; - public static final String RBCS_MSG_ENTITY_PREFIX = "msg."; - + //forums custom props + public static final String RBCS_FORUM_ENTITY_PREFIX = "for."; + public static final String RBCS_TOPIC_ENTITY_PREFIX = "top."; + public static final String RBCS_MSG_ENTITY_PREFIX = "msg."; + + public static final String LINE_SEPARATOR = System.getProperty("line.separator"); } diff --git a/rubrics/api/src/main/java/org/sakaiproject/rubrics/api/RubricsService.java b/rubrics/api/src/main/java/org/sakaiproject/rubrics/api/RubricsService.java index 547033ffe5b7..16f634b65536 100644 --- a/rubrics/api/src/main/java/org/sakaiproject/rubrics/api/RubricsService.java +++ b/rubrics/api/src/main/java/org/sakaiproject/rubrics/api/RubricsService.java @@ -29,6 +29,7 @@ import java.util.Optional; import org.sakaiproject.entity.api.Entity; +import org.sakaiproject.entity.api.EntityProducer; import org.sakaiproject.rubrics.api.beans.AssociationTransferBean; import org.sakaiproject.rubrics.api.beans.CriterionTransferBean; import org.sakaiproject.rubrics.api.beans.EvaluationTransferBean; @@ -37,7 +38,7 @@ import org.sakaiproject.rubrics.api.model.Rubric; import org.sakaiproject.rubrics.api.model.ToolItemRubricAssociation; -public interface RubricsService { +public interface RubricsService extends EntityProducer { public static final String REFERENCE_ROOT = Entity.SEPARATOR + "rubrics"; diff --git a/rubrics/api/src/main/java/org/sakaiproject/rubrics/api/model/Rubric.java b/rubrics/api/src/main/java/org/sakaiproject/rubrics/api/model/Rubric.java index 7ba8d49f9a10..7adb886621e6 100644 --- a/rubrics/api/src/main/java/org/sakaiproject/rubrics/api/model/Rubric.java +++ b/rubrics/api/src/main/java/org/sakaiproject/rubrics/api/model/Rubric.java @@ -65,6 +65,7 @@ public class Rubric implements PersistableEntity, Serializable, Cloneable @GeneratedValue(strategy = GenerationType.AUTO, generator ="rbc_seq" ) private Long id; + @Column(nullable = false) private String title; private Boolean weighted = Boolean.FALSE; @@ -81,14 +82,15 @@ public class Rubric implements PersistableEntity, Serializable, Cloneable @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "rubric") private List associations = new ArrayList<>(); - private Instant created; + @Column(nullable = false) + private Instant created = Instant.now(); private Instant modified; - @Column(length = 99) + @Column(length = 99, nullable = false) private String ownerId; - @Column(length = 99) + @Column(length = 99, nullable = false) private String creatorId; private Boolean shared = Boolean.FALSE; diff --git a/rubrics/impl/pom.xml b/rubrics/impl/pom.xml index 8a920dbf7778..1cc33e80ddac 100644 --- a/rubrics/impl/pom.xml +++ b/rubrics/impl/pom.xml @@ -119,6 +119,11 @@ org.sakaiproject.edu-services.sections sections-api + + org.sakaiproject.common + archive-api + test + diff --git a/rubrics/impl/src/main/java/org/sakaiproject/rubrics/impl/RubricsServiceImpl.java b/rubrics/impl/src/main/java/org/sakaiproject/rubrics/impl/RubricsServiceImpl.java index 3c81f03cbb90..c47487272029 100644 --- a/rubrics/impl/src/main/java/org/sakaiproject/rubrics/impl/RubricsServiceImpl.java +++ b/rubrics/impl/src/main/java/org/sakaiproject/rubrics/impl/RubricsServiceImpl.java @@ -25,6 +25,7 @@ import static org.sakaiproject.rubrics.api.RubricsConstants.RBCS_CONFIG; import static org.sakaiproject.rubrics.api.RubricsConstants.RBCS_MULTIPLE_OPTIONS_CONFIG; import static org.sakaiproject.rubrics.api.RubricsConstants.RBCS_PREFIX; +import static org.sakaiproject.rubrics.api.RubricsConstants.LINE_SEPARATOR; import java.awt.Color; import java.io.ByteArrayOutputStream; @@ -43,10 +44,11 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.Set; +import java.util.Stack; import java.util.function.Predicate; import java.util.stream.Collectors; -import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.math.NumberUtils; import org.jsoup.Jsoup; @@ -58,7 +60,6 @@ import org.sakaiproject.component.api.ServerConfigurationService; import org.sakaiproject.entity.api.Entity; import org.sakaiproject.entity.api.EntityManager; -import org.sakaiproject.entity.api.EntityProducer; import org.sakaiproject.entity.api.EntityTransferrer; import org.sakaiproject.entity.api.Reference; import org.sakaiproject.event.api.EventTrackingService; @@ -88,11 +89,8 @@ import org.sakaiproject.rubrics.api.repository.RatingRepository; import org.sakaiproject.rubrics.api.repository.ReturnedEvaluationRepository; import org.sakaiproject.rubrics.api.repository.RubricRepository; -import org.sakaiproject.site.api.Group; -import org.sakaiproject.site.api.Site; import org.sakaiproject.site.api.SiteService; import org.sakaiproject.time.api.UserTimeService; -import org.sakaiproject.tool.assessment.data.ifc.assessment.PublishedAssessmentIfc; import org.sakaiproject.tool.assessment.facade.PublishedAssessmentFacadeQueriesAPI; import org.sakaiproject.tool.api.SessionManager; import org.sakaiproject.tool.api.ToolManager; @@ -107,7 +105,6 @@ import com.lowagie.text.Chunk; import com.lowagie.text.Document; -import com.lowagie.text.Element; import com.lowagie.text.Font; import com.lowagie.text.FontFactory; import com.lowagie.text.PageSize; @@ -119,10 +116,14 @@ import lombok.Setter; import lombok.extern.slf4j.Slf4j; +import org.w3c.dom.CDATASection; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + @Slf4j @Setter @Transactional -public class RubricsServiceImpl implements RubricsService, EntityProducer, EntityTransferrer { +public class RubricsServiceImpl implements RubricsService, EntityTransferrer { private static final Font BOLD_FONT = FontFactory.getFont(FontFactory.HELVETICA, 10, Font.BOLD); private static final Font NORMAL_FONT = FontFactory.getFont(FontFactory.HELVETICA, 7, Font.NORMAL); @@ -1280,7 +1281,7 @@ public byte[] createPdf(String siteId, Long rubricId, String toolId, String item PdfPCell header = new PdfPCell(); Paragraph paragraph = new Paragraph(resourceLoader.getFormattedMessage("export_rubric_title", rubric.getTitle() + "\n"), BOLD_FONT); - paragraph.setAlignment(Element.ALIGN_LEFT); + paragraph.setAlignment(com.lowagie.text.Element.ALIGN_LEFT); try { String siteTitle = siteService.getSite(rubric.getOwnerId()).getTitle(); paragraph.add(resourceLoader.getFormattedMessage("export_rubric_site", siteTitle)); @@ -1522,6 +1523,181 @@ public Map transferCopyEntities(String fromContext, String toCon return transferCopyEntities(fromContext, toContext, ids, null); } + @Override + public String getLabel() { + return "rubrics"; + } + + @Override + public boolean willArchiveMerge() { + return true; + } + + @Override + public String archive(String siteId, org.w3c.dom.Document doc, Stack stack, String archivePath, List attachments) { + + StringBuilder results = new StringBuilder(); + results.append("begin archiving ").append(getLabel()).append(" for site ").append(siteId).append(LINE_SEPARATOR); + + Element rubrics = doc.createElement(getLabel()); + stack.peek().appendChild(rubrics); + stack.push(rubrics); + + rubricRepository.findByOwnerId(siteId).stream().sorted((r1, r2) -> r1.getTitle().compareTo(r2.getTitle())).forEach(rubric -> { + + Element rubricEl = doc.createElement("rubric"); + rubrics.appendChild(rubricEl); + + rubricEl.setAttribute("title", rubric.getTitle()); + rubricEl.setAttribute("created", Long.toString(rubric.getCreated().getEpochSecond())); + rubricEl.setAttribute("creator", rubric.getCreatorId()); + rubricEl.setAttribute("weighted", Boolean.toString(rubric.getWeighted())); + rubricEl.setAttribute("adhoc", Boolean.toString(rubric.getAdhoc())); + rubricEl.setAttribute("max-points", Double.toString(rubric.getMaxPoints())); + + Element criteriaEl = doc.createElement("criteria"); + rubricEl.appendChild(criteriaEl); + + rubric.getCriteria().forEach(criterion -> { + + Element criterionEl = doc.createElement("criterion"); + criteriaEl.appendChild(criterionEl); + + criterionEl.setAttribute("title", criterion.getTitle()); + Float weight = criterion.getWeight(); + if (weight != null) { + criterionEl.setAttribute("weight", Float.toString(weight)); + } + + String description = criterion.getDescription(); + if (StringUtils.isNotBlank(description)) { + Element descriptionEl = doc.createElement("description"); + criterionEl.appendChild(descriptionEl); + descriptionEl.appendChild(doc.createCDATASection(description)); + } + + Element ratingsEl = doc.createElement("ratings"); + criterionEl.appendChild(ratingsEl); + + criterion.getRatings().forEach(rating -> { + + Element ratingEl = doc.createElement("rating"); + ratingsEl.appendChild(ratingEl); + ratingEl.setAttribute("title", rating.getTitle()); + ratingEl.setAttribute("points", Double.toString(rating.getPoints())); + String ratingDescription = rating.getDescription(); + if (StringUtils.isNotBlank(ratingDescription)) { + Element ratingDescriptionEl = doc.createElement("description"); + ratingEl.appendChild(ratingDescriptionEl); + ratingDescriptionEl.appendChild(doc.createCDATASection(ratingDescription)); + } + }); + }); + }); + + stack.pop(); + + results.append("completed archiving ").append(getLabel()).append(" for site ").append(siteId).append(LINE_SEPARATOR); + return results.toString(); + } + + @Override + public String merge(String toSiteId, Element root, String archivePath, String fromSiteId, Map attachmentNames, Map userIdTrans, Set userListAllowImport) { + + StringBuilder results = new StringBuilder(); + results.append("begin merging ").append(getLabel()).append(" for site ").append(toSiteId).append(LINE_SEPARATOR); + + String currentUserId = sessionManager.getCurrentSessionUserId(); + + if (!root.getTagName().equals(getLabel())) { + log.warn("Tried to merge a non <{}> xml document", getLabel()); + return "Invalid xml document"; + } + + NodeList rubricNodes = root.getElementsByTagName("rubric"); + + for (int i = 0; i < rubricNodes.getLength(); i++) { + Element rubricEl = (Element) rubricNodes.item(i); + + String creatorId = currentUserId; + + String originalCreatorId = rubricEl.getAttribute("creator"); + if (StringUtils.isNotBlank(originalCreatorId)) { + try { + userDirectoryService.getUser(originalCreatorId); + creatorId = originalCreatorId; + } catch (UserNotDefinedException unde) { + } + } + + RubricTransferBean rubricBean = new RubricTransferBean(); + rubricBean.setOwnerId(toSiteId); + // TODO: Do we honour the original creator, or the current user? + rubricBean.setCreatorId(creatorId); + // TODO: Do we honour the original created date, or use now? + rubricBean.setCreated(Instant.ofEpochSecond(Long.parseLong(rubricEl.getAttribute("created")))); + rubricBean.setTitle(rubricEl.getAttribute("title")); + rubricBean.setMaxPoints(Double.parseDouble(rubricEl.getAttribute("max-points"))); + rubricBean.setWeighted(Boolean.parseBoolean(rubricEl.getAttribute("weighted"))); + rubricBean.setAdhoc(Boolean.parseBoolean(rubricEl.getAttribute("adhoc"))); + rubricBean.setDraft(false); + + NodeList criteriaNodes = rubricEl.getElementsByTagName("criteria"); + if (criteriaNodes.getLength() != 1) { + log.warn("No criteria element in rubrics archive XML"); + continue; + } + + NodeList criterionNodes = ((Element) criteriaNodes.item(0)).getElementsByTagName("criterion"); + + List criteria = new ArrayList<>(); + for (int j = 0; j < criterionNodes.getLength(); j++) { + Element criterionEl = (Element) criterionNodes.item(j); + CriterionTransferBean criterionBean = new CriterionTransferBean(); + criterionBean.setTitle(criterionEl.getAttribute("title")); + + NodeList descriptionNodes = criterionEl.getElementsByTagName("description"); + if (descriptionNodes.getLength() == 1) { + CDATASection descriptionSection = (CDATASection) descriptionNodes.item(0).getFirstChild(); + if (descriptionSection != null) { + criterionBean.setDescription(descriptionSection.getNodeValue()); + } + } + + NodeList ratingsNodes = criterionEl.getElementsByTagName("ratings"); + if (ratingsNodes.getLength() == 1) { + NodeList ratingNodes = ((Element) ratingsNodes.item(0)).getElementsByTagName("rating"); + List ratings = new ArrayList<>(); + for (int k = 0; k < ratingNodes.getLength(); k++) { + Element ratingEl = (Element) ratingNodes.item(k); + RatingTransferBean ratingBean = new RatingTransferBean(); + ratingBean.setTitle(ratingEl.getAttribute("title")); + ratingBean.setPoints(Double.parseDouble(ratingEl.getAttribute("points"))); + + NodeList ratingDescriptionNodes = ratingEl.getElementsByTagName("description"); + if (ratingDescriptionNodes.getLength() == 1) { + CDATASection ratingDescriptionSection = (CDATASection) ratingDescriptionNodes.item(0).getFirstChild(); + if (ratingDescriptionSection != null) { + ratingBean.setDescription(ratingDescriptionSection.getNodeValue()); + } + } + ratings.add(ratingBean); + } + criterionBean.setRatings(ratings); + } + + criteria.add(criterionBean); + } + + rubricBean.setCriteria(criteria); + + saveRubric(rubricBean); + } + + results.append("completed merging ").append(getLabel()).append(" for site ").append(toSiteId).append(LINE_SEPARATOR); + return results.toString(); + } + @Override public String[] myToolIds() { return new String[] { RubricsConstants.RBCS_TOOL }; @@ -1636,7 +1812,6 @@ private Optional getCriterionPoints(Criterion cri, Evaluation evaluation } private boolean isEditor(String siteId) { - return securityService.unlock(RubricsConstants.RBCS_PERMISSIONS_EDITOR, siteService.siteReference(siteId)); } diff --git a/rubrics/impl/src/test/org/sakaiproject/rubrics/impl/test/RubricsServiceTests.java b/rubrics/impl/src/test/org/sakaiproject/rubrics/impl/test/RubricsServiceTests.java index 9e419037c75b..6d00bfd9ca87 100644 --- a/rubrics/impl/src/test/org/sakaiproject/rubrics/impl/test/RubricsServiceTests.java +++ b/rubrics/impl/src/test/org/sakaiproject/rubrics/impl/test/RubricsServiceTests.java @@ -15,6 +15,8 @@ */ package org.sakaiproject.rubrics.impl.test; +import org.apache.commons.lang3.StringUtils; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -30,11 +32,14 @@ import java.util.Locale; import java.util.Map; import java.util.Optional; +import java.util.Stack; import org.hibernate.SessionFactory; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; + +import org.sakaiproject.archive.api.ArchiveService; import org.sakaiproject.authz.api.SecurityService; import org.sakaiproject.rubrics.api.RubricsConstants; import org.sakaiproject.rubrics.api.RubricsService; @@ -47,12 +52,14 @@ import org.sakaiproject.rubrics.api.model.EvaluatedItemOwnerType; import org.sakaiproject.rubrics.api.model.Evaluation; import org.sakaiproject.rubrics.api.model.EvaluationStatus; +import org.sakaiproject.rubrics.api.model.Rating; import org.sakaiproject.rubrics.api.model.ReturnedEvaluation; import org.sakaiproject.rubrics.api.model.Rubric; import org.sakaiproject.rubrics.api.model.ToolItemRubricAssociation; import org.sakaiproject.rubrics.api.repository.AssociationRepository; import org.sakaiproject.rubrics.api.repository.CriterionRepository; import org.sakaiproject.rubrics.api.repository.EvaluationRepository; +import org.sakaiproject.rubrics.api.repository.RubricRepository; import org.sakaiproject.rubrics.api.repository.ReturnedEvaluationRepository; import org.sakaiproject.rubrics.impl.RubricsServiceImpl; import org.sakaiproject.site.api.Site; @@ -64,12 +71,24 @@ import org.sakaiproject.user.api.UserDirectoryService; import org.sakaiproject.user.api.UserNotDefinedException; import org.sakaiproject.util.ResourceLoader; +import org.sakaiproject.util.Xml; + 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.springframework.test.util.AopTestUtils; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import org.w3c.dom.CDATASection; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + import lombok.extern.slf4j.Slf4j; @Slf4j @@ -81,6 +100,7 @@ public class RubricsServiceTests extends AbstractTransactionalJUnit4SpringContex @Autowired private CriterionRepository criterionRepository; @Autowired private EvaluationRepository evaluationRepository; @Autowired private ReturnedEvaluationRepository returnedEvaluationRepository; + @Autowired private RubricRepository rubricRepository; @Autowired private RubricsService rubricsService; @Autowired private SecurityService securityService; @Autowired private SessionManager sessionManager; @@ -471,8 +491,6 @@ public void deleteRating() { int initialRatingsSize = criterionBean.getRatings().size(); RatingTransferBean ratingBean = criterionBean.getRatings().get(2); - System.out.println(ratingBean.getPoints()); - switchToUser1(); assertThrows(SecurityException.class, () -> rubricsService.deleteRating(ratingBean.getId(), criterionBean.getId(), siteId, rubricBean.getId())); @@ -767,6 +785,7 @@ public void saveEvaluationAsTeachingAssistant() { @Test public void contextualFilenameNoEvaluation() { + switchToInstructor(); RubricTransferBean rubric = rubricsService.createDefaultRubric(siteId); String toolId = "sakai.assignment"; @@ -782,6 +801,7 @@ public void contextualFilenameNoEvaluation() { @Test public void contextualFilename() throws UserNotDefinedException { + switchToInstructor(); RubricTransferBean rubric = rubricsService.createDefaultRubric(siteId); String toolId = "sakai.assignment"; @@ -801,6 +821,184 @@ public void contextualFilename() throws UserNotDefinedException { assertEquals(rubric.getTitle() + '_' + user1SortName, filename); } + @Test + public void archive() { + + switchToInstructor(); + + RubricTransferBean rubric1 = rubricsService.createDefaultRubric(siteId); + String title1 = "Cheese Sandwich"; + rubric1.setTitle(title1); + String rubric1Criteria1Rating1Description = "This is great"; + rubric1.getCriteria().get(0).getRatings().get(0).setDescription(rubric1Criteria1Rating1Description); + rubricsService.saveRubric(rubric1); + + String rubric2CriteriaDescription = "Rate those sandwiches"; + RubricTransferBean rubric2 = rubricsService.createDefaultRubric(siteId); + String title2 = "Egg Sandwich"; + rubric2.setTitle(title2); + rubric2.getCriteria().get(0).setDescription(rubric2CriteriaDescription); + rubricsService.saveRubric(rubric2); + + RubricTransferBean rubric3 = rubricsService.createDefaultRubric(siteId); + String title3 = "Ham Sandwich"; + rubric3.setTitle(title3); + rubricsService.saveRubric(rubric3); + + RubricTransferBean[] rubrics = new RubricTransferBean[] { rubric1, rubric2, rubric3 }; + + Document doc = Xml.createDocument(); + Stack stack = new Stack<>(); + + Element root = doc.createElement("archive"); + doc.appendChild(root); + root.setAttribute("source", siteId); + root.setAttribute("xmlns:sakai", ArchiveService.SAKAI_ARCHIVE_NS); + root.setAttribute("xmlns:CHEF", ArchiveService.SAKAI_ARCHIVE_NS.concat("CHEF")); + root.setAttribute("xmlns:DAV", ArchiveService.SAKAI_ARCHIVE_NS.concat("DAV")); + stack.push(root); + + String results = rubricsService.archive(siteId, doc, stack, "", null); + + assertEquals(1, stack.size()); + + NodeList rubricsNode = root.getElementsByTagName(rubricsService.getLabel()); + assertEquals(1, rubricsNode.getLength()); + + NodeList rubricNodes = ((Element) rubricsNode.item(0)).getElementsByTagName("rubric"); + assertEquals(3, rubricNodes.getLength()); + + for (int i = 0; i < rubricNodes.getLength(); i++) { + + RubricTransferBean rubricBean = rubrics[i]; + Element rubricEl = (Element) rubricNodes.item(i); + + assertEquals(rubricBean.getTitle(), rubricEl.getAttribute("title")); + assertEquals(Double.toString(rubricBean.getMaxPoints()), rubricEl.getAttribute("max-points")); + assertEquals(Long.toString(rubricBean.getCreated().getEpochSecond()), rubricEl.getAttribute("created")); + assertEquals(Boolean.toString(rubricBean.getWeighted()), rubricEl.getAttribute("weighted")); + assertEquals(Boolean.toString(rubricBean.getAdhoc()), rubricEl.getAttribute("adhoc")); + + NodeList criteriaNodes = rubricEl.getElementsByTagName("criteria"); + assertEquals(1, criteriaNodes.getLength()); + + NodeList criterionNodes = ((Element) criteriaNodes.item(0)).getElementsByTagName("criterion"); + assertEquals(rubricBean.getCriteria().size(), criterionNodes.getLength()); + + for (int j = 0; j < criterionNodes.getLength(); j++) { + + CriterionTransferBean criterionBean = rubrics[i].getCriteria().get(j); + Element criterionEl = (Element) criterionNodes.item(j); + + assertEquals(criterionBean.getTitle(), criterionEl.getAttribute("title")); + assertEquals(Float.toString(criterionBean.getWeight()), criterionEl.getAttribute("weight")); + + if (i == 1 && j == 0) { + // We've only set a description on the first criteria of the second rubric, and + // descriptions are optional + NodeList descriptionNodes = criterionEl.getElementsByTagName("description"); + assertEquals(1, descriptionNodes.getLength()); + CDATASection descriptionNode = (CDATASection) descriptionNodes.item(0).getFirstChild(); + assertNotNull(descriptionNode); + assertEquals(criterionBean.getDescription(), descriptionNode.getNodeValue()); + } + + NodeList ratingsNodes = criterionEl.getElementsByTagName("ratings"); + assertEquals(1, ratingsNodes.getLength()); + + NodeList ratingNodes = ((Element) ratingsNodes.item(0)).getElementsByTagName("rating"); + assertEquals(criterionBean.getRatings().size(), ratingNodes.getLength()); + + for (int k = 0; k < ratingNodes.getLength(); k++) { + + RatingTransferBean ratingBean = criterionBean.getRatings().get(k); + Element ratingEl = (Element) ratingNodes.item(k); + + assertEquals(ratingBean.getTitle(), ratingEl.getAttribute("title")); + assertEquals(Double.toString(ratingBean.getPoints()), ratingEl.getAttribute("points")); + + String ratingDescription = ratingBean.getDescription(); + if (StringUtils.isNotBlank(ratingDescription)) { + NodeList ratingDescriptionNodes = ratingEl.getElementsByTagName("description"); + assertEquals(1, ratingDescriptionNodes.getLength()); + } + } + } + } + } + + @Test + public void merge() { + + Document doc = Xml.readDocumentFromStream(this.getClass().getResourceAsStream("/archive/rubrics.xml")); + + Element root = doc.getDocumentElement(); + + String fromSite = root.getAttribute("source"); + String toSite = "my-new-site"; + + switchToInstructor(toSite); + + Element rubricsElement = doc.createElement("not-rubrics"); + + rubricsService.merge(toSite, rubricsElement, "", fromSite, null, null, null); + + assertEquals("Invalid xml document", rubricsService.merge(siteId, rubricsElement, "", fromSite, null, null, null)); + + rubricsElement = (Element) root.getElementsByTagName(rubricsService.getLabel()).item(0); + + rubricsService.merge(toSite, rubricsElement, "", fromSite, null, null, null); + + NodeList rubricNodes = rubricsElement.getElementsByTagName("rubric"); + + List rubrics = rubricRepository.findByOwnerId(toSite); + + for (int i = 0; i < rubricNodes.getLength(); i++) { + Element rubricEl = (Element) rubricNodes.item(i); + String rubricTitle = rubricEl.getAttribute("title"); + Optional optRubric = rubrics.stream().filter(r -> r.getTitle().equals(rubricTitle)).findAny(); + assertTrue(optRubric.isPresent()); + Rubric rubric = optRubric.get(); + //assertEquals(Double.valueOf(rubricEl.getAttribute("max-points")), rubric.getMaxPoints()); + assertEquals(Boolean.valueOf(rubricEl.getAttribute("adhoc")), rubric.getAdhoc()); + assertEquals(Boolean.valueOf(rubricEl.getAttribute("weighted")), rubric.getWeighted()); + + NodeList criterionNodes = ((Element) rubricEl.getElementsByTagName("criteria").item(0)) + .getElementsByTagName("criterion"); + List criteria = rubric.getCriteria(); + assertEquals(criterionNodes.getLength(), criteria.size()); + + for (int j = 0; j < criterionNodes.getLength(); j++) { + Element criterionEl = (Element) criterionNodes.item(j); + String criterionTitle = criterionEl.getAttribute("title"); + Optional optCriterion = criteria.stream().filter(c -> c.getTitle().equals(criterionEl.getAttribute("title"))).findAny(); + assertTrue(optCriterion.isPresent()); + Criterion criterion = optCriterion.get(); + NodeList descriptionNodes = criterionEl.getElementsByTagName("description"); + if (descriptionNodes.getLength() == 1) { + assertEquals(descriptionNodes.item(0).getFirstChild().getNodeValue(), criterion.getDescription()); + } + + NodeList ratingNodes = ((Element) criterionEl.getElementsByTagName("ratings").item(0)) + .getElementsByTagName("rating"); + List ratings = criterion.getRatings(); + for (int k = 0; k < ratingNodes.getLength(); k++) { + Element ratingEl = (Element) ratingNodes.item(j); + String ratingTitle = ratingEl.getAttribute("title"); + Optional optRating = ratings.stream().filter(r -> r.getTitle().equals(ratingEl.getAttribute("title"))).findAny(); + assertTrue(optRating.isPresent()); + Rating rating = optRating.get(); + assertEquals(Double.valueOf(ratingEl.getAttribute("points")), rating.getPoints()); + + NodeList ratingDescriptionNodes = ratingEl.getElementsByTagName("description"); + if (ratingDescriptionNodes.getLength() == 1) { + assertEquals(ratingDescriptionNodes.item(0).getFirstChild().getNodeValue(), rating.getDescription()); + } + } + } + } + } + private EvaluationTransferBean buildEvaluation(Long associationId, RubricTransferBean rubricBean, String toolItemId) { EvaluationTransferBean etb = new EvaluationTransferBean(); @@ -824,6 +1022,7 @@ private EvaluationTransferBean buildEvaluation(Long associationId, RubricTransfe } private void setupStudentPermissions() { + when(securityService.unlock(RubricsConstants.RBCS_PERMISSIONS_EDITOR, siteRef)).thenReturn(false); when(securityService.unlock(RubricsConstants.RBCS_PERMISSIONS_EVALUATOR, siteRef)).thenReturn(false); when(securityService.unlock(RubricsConstants.RBCS_PERMISSIONS_EVALUEE, siteRef)).thenReturn(true); @@ -866,10 +1065,20 @@ private void switchToUser3() { } private void switchToInstructor() { + switchToInstructor(null); + } - when(securityService.unlock(RubricsConstants.RBCS_PERMISSIONS_EDITOR, siteRef)).thenReturn(true); - when(securityService.unlock(RubricsConstants.RBCS_PERMISSIONS_EVALUATOR, siteRef)).thenReturn(true); - when(securityService.unlock(RubricsConstants.RBCS_PERMISSIONS_EVALUEE, siteRef)).thenReturn(false); + private void switchToInstructor(String siteId) { + + String ref = siteId != null ? "/site/" + siteId : siteRef; + + if (siteId != null) { + when(siteService.siteReference(siteId)).thenReturn(ref); + } + + when(securityService.unlock(RubricsConstants.RBCS_PERMISSIONS_EDITOR, ref)).thenReturn(true); + when(securityService.unlock(RubricsConstants.RBCS_PERMISSIONS_EVALUATOR, ref)).thenReturn(true); + when(securityService.unlock(RubricsConstants.RBCS_PERMISSIONS_EVALUEE, ref)).thenReturn(false); when(sessionManager.getCurrentSessionUserId()).thenReturn(instructor); when(userDirectoryService.getCurrentUser()).thenReturn(instructorUser); diff --git a/rubrics/impl/src/test/resources/archive/rubrics.xml b/rubrics/impl/src/test/resources/archive/rubrics.xml new file mode 100644 index 000000000000..27af7247a308 --- /dev/null +++ b/rubrics/impl/src/test/resources/archive/rubrics.xml @@ -0,0 +1,6 @@ + +spin it around

+]]>
some foods

+]]>
rackets

+]]>
pitch

+]]>
\ No newline at end of file diff --git a/webapi/src/main/java/org/sakaiproject/webapi/controllers/RubricsRestController.java b/webapi/src/main/java/org/sakaiproject/webapi/controllers/RubricsRestController.java index 06dfcf9dc47a..12739ec42cbf 100644 --- a/webapi/src/main/java/org/sakaiproject/webapi/controllers/RubricsRestController.java +++ b/webapi/src/main/java/org/sakaiproject/webapi/controllers/RubricsRestController.java @@ -85,7 +85,13 @@ List> getRubricsForSite(@PathVariable String sit checkSakaiSession(); - return rubricsService.getRubricsForSite(siteId).stream().map(b -> entityModelForRubricBean(b)).collect(Collectors.toList()); + try { + return rubricsService.getRubricsForSite(siteId).stream().map(b -> entityModelForRubricBean(b)).collect(Collectors.toList()); + } catch (Exception e) { + e.printStackTrace(); + } + + return null; } @GetMapping(value = "/rubrics/shared", produces = MediaType.APPLICATION_JSON_VALUE) diff --git a/webcomponents/tool/src/main/frontend/packages/sakai-editor/src/SakaiEditor.js b/webcomponents/tool/src/main/frontend/packages/sakai-editor/src/SakaiEditor.js index 647459986e95..04f162f91320 100644 --- a/webcomponents/tool/src/main/frontend/packages/sakai-editor/src/SakaiEditor.js +++ b/webcomponents/tool/src/main/frontend/packages/sakai-editor/src/SakaiEditor.js @@ -1,7 +1,5 @@ import { SakaiElement } from "@sakai-ui/sakai-element"; import { html } from "lit"; -import { ifDefined } from "lit/directives/if-defined.js"; -import { unsafeHTML } from "lit/directives/unsafe-html.js"; export class SakaiEditor extends SakaiElement { @@ -133,14 +131,8 @@ export class SakaiEditor extends SakaiElement { render() { - if (this.textarea) { - return html ` - - `; - } - return html ` -
${unsafeHTML(this.content)}
+ `; } }