/********************************************************************************** * $URL: https://source.sakaiproject.org/svn/assignment/trunk/assignment-impl/impl/src/java/org/sakaiproject/assignment/impl/BaseAssignmentService.java $ * $Id: BaseAssignmentService.java 131845 2013-11-21 17:53:50Z ottenhoff@longsight.com $ *********************************************************************************** * * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009 The Sakai Foundation * * Licensed under the Educational Community 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.opensource.org/licenses/ECL-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. * **********************************************************************************/ package org.sakaiproject.assignment.impl; import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.StringWriter; import java.text.NumberFormat; import java.text.Normalizer; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.Stack; import java.util.StringTokenizer; import java.util.Vector; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; import org.sakaiproject.site.api.Group; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.codec.binary.Base64; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.commons.lang.math.NumberUtils; import org.apache.poi.hssf.usermodel.HSSFCell; import org.apache.poi.hssf.usermodel.HSSFCellStyle; import org.apache.poi.hssf.usermodel.HSSFRow; import org.apache.poi.hssf.usermodel.HSSFSheet; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.util.WorkbookUtil; import org.sakaiproject.announcement.api.AnnouncementChannel; import org.sakaiproject.announcement.api.AnnouncementService; import org.sakaiproject.assignment.api.Assignment; import org.sakaiproject.assignment.api.AssignmentConstants; import org.sakaiproject.assignment.api.AssignmentContent; import org.sakaiproject.assignment.api.AssignmentContentEdit; import org.sakaiproject.assignment.api.AssignmentContentNotEmptyException; import org.sakaiproject.assignment.api.AssignmentEdit; import org.sakaiproject.assignment.api.AssignmentService; import org.sakaiproject.assignment.api.AssignmentSubmission; import org.sakaiproject.assignment.api.AssignmentSubmissionEdit; import org.sakaiproject.assignment.taggable.api.AssignmentActivityProducer; import org.sakaiproject.assignment.api.AssignmentPeerAssessmentService; import org.sakaiproject.authz.api.AuthzGroup; import org.sakaiproject.authz.api.AuthzPermissionException; import org.sakaiproject.authz.api.GroupNotDefinedException; import org.sakaiproject.authz.api.Member; import org.sakaiproject.authz.cover.AuthzGroupService; import org.sakaiproject.authz.cover.FunctionManager; import org.sakaiproject.authz.cover.SecurityService; import org.sakaiproject.calendar.api.Calendar; import org.sakaiproject.calendar.api.CalendarEvent; import org.sakaiproject.calendar.api.CalendarService; import org.sakaiproject.component.api.ServerConfigurationService; import org.sakaiproject.component.cover.ComponentManager; import org.sakaiproject.content.api.ContentHostingService; import org.sakaiproject.content.api.ContentResource; import org.sakaiproject.content.api.ContentResourceEdit; import org.sakaiproject.content.util.ZipContentUtil; import org.sakaiproject.contentreview.exception.QueueException; import org.sakaiproject.contentreview.exception.ReportException; import org.sakaiproject.contentreview.exception.SubmissionException; import org.sakaiproject.contentreview.model.ContentReviewItem; import org.sakaiproject.contentreview.service.ContentReviewService; import org.sakaiproject.email.cover.DigestService; import org.sakaiproject.email.cover.EmailService; import org.sakaiproject.entity.api.AttachmentContainer; import org.sakaiproject.entity.api.Edit; import org.sakaiproject.entity.api.Entity; import org.sakaiproject.entity.api.EntityAccessOverloadException; import org.sakaiproject.entity.api.EntityCopyrightException; import org.sakaiproject.entity.api.EntityManager; import org.sakaiproject.entity.api.EntityNotDefinedException; import org.sakaiproject.entity.api.EntityPermissionException; import org.sakaiproject.entity.api.EntityPropertyNotDefinedException; import org.sakaiproject.entity.api.EntityPropertyTypeException; import org.sakaiproject.entity.api.EntityTransferrer; import org.sakaiproject.entity.api.EntityTransferrerRefMigrator; import org.sakaiproject.entity.api.HttpAccess; import org.sakaiproject.entity.api.Reference; import org.sakaiproject.entity.api.ResourceProperties; import org.sakaiproject.entity.api.ResourcePropertiesEdit; import org.sakaiproject.event.api.Event; import org.sakaiproject.event.api.LearningResourceStoreService; import org.sakaiproject.event.api.LearningResourceStoreService.LRS_Actor; import org.sakaiproject.event.api.LearningResourceStoreService.LRS_Context; import org.sakaiproject.event.api.LearningResourceStoreService.LRS_Object; import org.sakaiproject.event.api.LearningResourceStoreService.LRS_Result; import org.sakaiproject.event.api.LearningResourceStoreService.LRS_Statement; import org.sakaiproject.event.api.LearningResourceStoreService.LRS_Verb; import org.sakaiproject.event.api.LearningResourceStoreService.LRS_Verb.SAKAI_VERB; import org.sakaiproject.event.cover.EventTrackingService; import org.sakaiproject.event.cover.NotificationService; import org.sakaiproject.exception.IdInvalidException; import org.sakaiproject.exception.IdUnusedException; import org.sakaiproject.exception.IdUsedException; import org.sakaiproject.exception.InUseException; import org.sakaiproject.exception.PermissionException; import org.sakaiproject.exception.ServerOverloadException; import org.sakaiproject.exception.TypeException; import org.sakaiproject.id.cover.IdManager; import org.sakaiproject.memory.api.Cache; import org.sakaiproject.memory.api.CacheRefresher; import org.sakaiproject.memory.api.MemoryService; import org.sakaiproject.service.gradebook.shared.GradebookExternalAssessmentService; import org.sakaiproject.service.gradebook.shared.GradebookService; import org.sakaiproject.site.api.Group; import org.sakaiproject.site.api.Site; import org.sakaiproject.site.cover.SiteService; import org.sakaiproject.taggable.api.TaggingManager; import org.sakaiproject.taggable.api.TaggingProvider; import org.sakaiproject.time.api.Time; import org.sakaiproject.time.cover.TimeService; import org.sakaiproject.tool.api.SessionBindingEvent; import org.sakaiproject.tool.api.SessionBindingListener; import org.sakaiproject.tool.cover.SessionManager; import org.sakaiproject.tool.cover.ToolManager; import org.sakaiproject.user.api.User; import org.sakaiproject.user.api.UserNotDefinedException; import org.sakaiproject.user.cover.UserDirectoryService; import org.sakaiproject.util.BaseResourcePropertiesEdit; import org.sakaiproject.util.Blob; import org.sakaiproject.util.DefaultEntityHandler; import org.sakaiproject.util.EmptyIterator; import org.sakaiproject.util.EntityCollections; import org.sakaiproject.util.FormattedText; import org.sakaiproject.util.ResourceLoader; import org.sakaiproject.util.SAXEntityReader; import org.sakaiproject.util.SingleStorageUser; import org.sakaiproject.util.SortedIterator; import org.sakaiproject.util.StringUtil; import org.sakaiproject.util.Validator; import org.sakaiproject.util.Xml; import org.sakaiproject.util.cover.LinkMigrationHelper; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.Attributes; import org.xml.sax.ContentHandler; import org.xml.sax.SAXException; import au.com.bytecode.opencsv.CSVWriter; /** * <p> * BaseAssignmentService is the abstract service class for Assignments. * </p> * <p> * The Concrete Service classes extending this are the XmlFile and DbCached storage classes. * </p> */ public abstract class BaseAssignmentService implements AssignmentService, EntityTransferrer, EntityTransferrerRefMigrator { /** Our logger. */ private static Log M_log = LogFactory.getLog(BaseAssignmentService.class); /** the resource bundle */ private static ResourceLoader rb = new ResourceLoader("assignment"); /** A Storage object for persistent storage of Assignments. */ protected AssignmentStorage m_assignmentStorage = null; /** A Storage object for persistent storage of Assignments. */ protected AssignmentContentStorage m_contentStorage = null; /** A Storage object for persistent storage of Assignments. */ protected AssignmentSubmissionStorage m_submissionStorage = null; /** A Cache for this service - Assignments keyed by reference. */ protected Cache m_assignmentCache = null; /** A Cache for this service - AssignmentContents keyed by reference. */ protected Cache m_contentCache = null; /** A Cache for this service - AssignmentSubmissions keyed by reference. */ protected Cache m_submissionCache = null; /** The access point URL. */ protected static String m_relativeAccessPoint = null; private static final String NEW_ASSIGNMENT_DUE_DATE_SCHEDULED = "new_assignment_due_date_scheduled"; protected static final String GROUP_LIST = "group"; protected static final String GROUP_NAME = "authzGroup"; protected static final String GROUP_SECTION_PROPERTY = "sections_category"; // the file types for zip download protected static final String ZIP_COMMENT_FILE_TYPE = ".txt"; protected static final String ZIP_SUBMITTED_TEXT_FILE_TYPE = ".html"; // spring service injection protected ContentReviewService contentReviewService; public void setContentReviewService(ContentReviewService contentReviewService) { this.contentReviewService = contentReviewService; } private AssignmentPeerAssessmentService assignmentPeerAssessmentService = null; public void setAssignmentPeerAssessmentService(AssignmentPeerAssessmentService assignmentPeerAssessmentService){ this.assignmentPeerAssessmentService = assignmentPeerAssessmentService; } String newline = "<br />\n"; /********************************************************************************************************************************************************************************************************************************************************** * Abstractions, etc. *********************************************************************************************************************************************************************************************************************************************************/ /** * Construct a Storage object for Assignments. * * @return The new storage object. */ protected abstract AssignmentStorage newAssignmentStorage(); /** * Construct a Storage object for AssignmentContents. * * @return The new storage object. */ protected abstract AssignmentContentStorage newContentStorage(); /** * Construct a Storage object for AssignmentSubmissions. * * @return The new storage object. */ protected abstract AssignmentSubmissionStorage newSubmissionStorage(); /** * Access the partial URL that forms the root of resource URLs. * * @param relative - * if true, form within the access path only (i.e. starting with /msg) * @return the partial URL that forms the root of resource URLs. */ static protected String getAccessPoint(boolean relative) { return (relative ? "" : m_serverConfigurationService.getAccessUrl()) + m_relativeAccessPoint; } // getAccessPoint /** * Access the internal reference which can be used to assess security clearance. * * @param id * The assignment id string. * @return The the internal reference which can be used to access the resource from within the system. */ public String assignmentReference(String context, String id) { String retVal = null; if (context == null) retVal = getAccessPoint(true) + Entity.SEPARATOR + "a" + Entity.SEPARATOR + id; else retVal = getAccessPoint(true) + Entity.SEPARATOR + "a" + Entity.SEPARATOR + context + Entity.SEPARATOR + id; return retVal; } // assignmentReference /** * I feel silly having to look up the entire assignment object just to get the reference, * but if there's no context, that seems to be the only way to do it. * @param id * @return */ public String assignmentReference(String id) { String ref = null; Assignment assignment = findAssignment(id); if (assignment != null) ref = assignment.getReference(); return ref; } // assignmentReference public List getSortedGroupUsers(Group _g) { List retVal = new ArrayList(); Iterator<Member> _members = _g.getMembers().iterator(); while (_members.hasNext()) { Member _member = _members.next(); try { retVal.add(UserDirectoryService.getUser(_member.getUserId())); } catch (Exception e) { M_log.warn(" BaseAssignmentSubmission Group getSubmitters" + e.getMessage() + _member.getUserId()); } } java.util.Collections.sort(retVal, new UserComparator()); return retVal; } /** * Access the internal reference which can be used to access the resource from within the system. * * @param id * The content id string. * @return The the internal reference which can be used to access the resource from within the system. */ public String contentReference(String context, String id) { String retVal = null; if (context == null) retVal = getAccessPoint(true) + Entity.SEPARATOR + "c" + Entity.SEPARATOR + id; else retVal = getAccessPoint(true) + Entity.SEPARATOR + "c" + Entity.SEPARATOR + context + Entity.SEPARATOR + id; return retVal; } // contentReference /** * Access the internal reference which can be used to access the resource from within the system. * * @param id * The submission id string. * @return The the internal reference which can be used to access the resource from within the system. */ public String submissionReference(String context, String id, String assignmentId) { String retVal = null; if (context == null) retVal = getAccessPoint(true) + Entity.SEPARATOR + "s" + Entity.SEPARATOR + id; else retVal = getAccessPoint(true) + Entity.SEPARATOR + "s" + Entity.SEPARATOR + context + Entity.SEPARATOR + assignmentId + Entity.SEPARATOR + id; return retVal; } // submissionReference /** * Access the assignment id extracted from an assignment reference. * * @param ref * The assignment reference string. * @return The the assignment id extracted from an assignment reference. */ protected String assignmentId(String ref) { if (ref == null) return ref; int i = ref.lastIndexOf(Entity.SEPARATOR); if (i == -1) return ref; String id = ref.substring(i + 1); return id; } // assignmentId /** * Access the content id extracted from a content reference. * * @param ref * The content reference string. * @return The the content id extracted from a content reference. */ protected String contentId(String ref) { int i = ref.lastIndexOf(Entity.SEPARATOR); if (i == -1) return ref; String id = ref.substring(i + 1); return id; } // contentId /** * Access the submission id extracted from a submission reference. * * @param ref * The submission reference string. * @return The the submission id extracted from a submission reference. */ protected String submissionId(String ref) { int i = ref.lastIndexOf(Entity.SEPARATOR); if (i == -1) return ref; String id = ref.substring(i + 1); return id; } // submissionId /** * Check security permission. * * @param lock - * The lock id string. * @param resource - * The resource reference string, or null if no resource is involved. * @return true if allowed, false if not */ protected boolean unlockCheck(String lock, String resource) { if (!SecurityService.unlock(lock, resource)) { return false; } return true; }// unlockCheck /** * SAK-21525 Groups need to be queried, not just the site. * * @param lock The security function to be checked, 'asn.submit' for example. * @param resource The resource to be accessed * @param assignment An Assignment object. We use this for the group checks. * @return */ protected boolean unlockCheckWithGroups(String lock, String resource, Assignment assignment) { // SAK-23755 addons: // super user should be allowed if (SecurityService.isSuperUser()) return true; // all.groups permission should apply down to group level String context = assignment.getContext(); String userId = SessionManager.getCurrentSessionUserId(); if (allowAllGroups(context) && AuthzGroupService.isAllowed(userId,lock, SiteService.siteReference(context))) { return true; } // group level users Collection groupIds = null; //SAK-23235 this method can be passed a null assignment -DH if (assignment != null) { groupIds = assignment.getGroups(); } if(groupIds != null && groupIds.size() > 0) { Iterator i = groupIds.iterator(); while(i.hasNext()) { String groupId = (String) i.next(); boolean isAllowed = AuthzGroupService.isAllowed(userId,lock,groupId); if(isAllowed) return true; } if (SECURE_ADD_ASSIGNMENT_SUBMISSION.equals(lock) && assignment.isGroup()) return SecurityService.unlock(lock, resource); else return false; } else { return SecurityService.unlock(lock, resource); } }// unlockCheckWithGroups /** * Check security permission. * * @param lock1 * The lock id string. * @param lock2 * The lock id string. * @param resource * The resource reference string, or null if no resource is involved. * @return true if either allowed, false if not */ protected boolean unlockCheck2(String lock1, String lock2, String resource) { // check the first lock if (SecurityService.unlock(lock1, resource)) return true; // if the second is different, check that if ((!lock1.equals(lock2)) && (SecurityService.unlock(lock2, resource))) return true; return false; } // unlockCheck2 /** * Check security permission. * * @param lock - * The lock id string. * @param resource - * The resource reference string, or null if no resource is involved. * @exception PermissionException * Thrown if the user does not have access */ protected void unlock(String lock, String resource) throws PermissionException { if (!unlockCheck(lock, resource)) { throw new PermissionException(SessionManager.getCurrentSessionUserId(), lock, resource); } } // unlock /** * Check security permission. * * @param lock1 * The lock id string. * @param lock2 * The lock id string. * @param resource * The resource reference string, or null if no resource is involved. * @exception PermissionException * Thrown if the user does not have access to either. */ protected void unlock2(String lock1, String lock2, String resource) throws PermissionException { if (!unlockCheck2(lock1, lock2, resource)) { throw new PermissionException(SessionManager.getCurrentSessionUserId(), lock1 + "/" + lock2, resource); } } // unlock2 /********************************************************************************************************************************************************************************************************************************************************** * Dependencies and their setter methods *********************************************************************************************************************************************************************************************************************************************************/ /** Dependency: MemoryService. */ protected MemoryService m_memoryService = null; /** * Dependency: MemoryService. * * @param service * The MemoryService. */ public void setMemoryService(MemoryService service) { m_memoryService = service; } /** Dependency: ContentHostingService. */ protected ContentHostingService m_contentHostingService = null; /** * Dependency:ContentHostingService. * * @param service * The ContentHostingService. */ public void setContentHostingService(ContentHostingService service) { m_contentHostingService = service; } /** Configuration: cache, or not. */ protected boolean m_caching = false; /** * Configuration: set the locks-in-db * * @param path * The storage path. */ public void setCaching(String value) { m_caching = Boolean.valueOf(value).booleanValue(); } /** Dependency: EntityManager. */ protected EntityManager m_entityManager = null; /** * Dependency: EntityManager. * * @param service * The EntityManager. */ public void setEntityManager(EntityManager service) { m_entityManager = service; } /** Dependency: ServerConfigurationService. */ static protected ServerConfigurationService m_serverConfigurationService = null; /** * Dependency: ServerConfigurationService. * * @param service * The ServerConfigurationService. */ public void setServerConfigurationService(ServerConfigurationService service) { m_serverConfigurationService = service; } /** Dependency: TaggingManager. */ protected TaggingManager m_taggingManager = null; /** * Dependency: TaggingManager. * * @param manager * The TaggingManager. */ public void setTaggingManager(TaggingManager manager) { m_taggingManager = manager; } /** Dependency: AssignmentActivityProducer. */ protected AssignmentActivityProducer m_assignmentActivityProducer = null; /** * Dependency: AssignmentActivityProducer. * * @param assignmentActivityProducer * The AssignmentActivityProducer. */ public void setAssignmentActivityProducer(AssignmentActivityProducer assignmentActivityProducer) { m_assignmentActivityProducer = assignmentActivityProducer; } /** Dependency: GradebookService. */ protected GradebookService m_gradebookService = null; /** * Dependency: GradebookService * * @param gradebookService * The GradebookService */ public void setGradebookService(GradebookService gradebookService) { m_gradebookService= gradebookService; } /** Dependency: GradebookExternalAssessmentService. */ protected GradebookExternalAssessmentService m_gradebookExternalAssessmentService = null; /** * Dependency: GradebookExternalAssessmentService * * @param gradebookExternalAssessmentService * The GradebookExternalAssessmentService */ public void setGradebookExternalAssessmentService(GradebookExternalAssessmentService gradebookExternalAssessmentService) { m_gradebookExternalAssessmentService= gradebookExternalAssessmentService; } /** Dependency: CalendarService. */ protected CalendarService m_calendarService = null; /** * Dependency: CalendarService * * @param calendarService * The CalendarService */ public void setCalendarService(CalendarService calendarService) { m_calendarService= calendarService; } /** Dependency: AnnouncementService. */ protected AnnouncementService m_announcementService = null; /** * Dependency: AnnouncementService * * @param announcementService * The AnnouncementService */ public void setAnnouncementService(AnnouncementService announcementService) { m_announcementService= announcementService; } /** Dependency: allowGroupAssignments setting */ protected boolean m_allowGroupAssignments = true; /** * Dependency: allowGroupAssignments * * @param allowGroupAssignments * the setting */ public void setAllowGroupAssignments(boolean allowGroupAssignments) { m_allowGroupAssignments = allowGroupAssignments; } /** * Get * * @return allowGroupAssignments */ public boolean getAllowGroupAssignments() { return m_allowGroupAssignments; } /** Dependency: allowSubmitByInstructor setting */ protected boolean m_allowSubmitByInstructor = true; /** * Dependency: allowSubmitByInstructor * * @param allowSubmitByInstructor * the setting */ public void setAllowSubmitByInstructor(boolean allowSubmitByInstructor) { m_allowSubmitByInstructor = allowSubmitByInstructor; } /** * Get * * @return allowSubmitByInstructor */ public boolean getAllowSubmitByInstructor() { return m_allowSubmitByInstructor; } /** Dependency: allowGroupAssignmentsInGradebook setting */ protected boolean m_allowGroupAssignmentsInGradebook = true; /** * Dependency: allowGroupAssignmentsInGradebook * * @param allowGroupAssignmentsInGradebook */ public void setAllowGroupAssignmentsInGradebook(boolean allowGroupAssignmentsInGradebook) { m_allowGroupAssignmentsInGradebook = allowGroupAssignmentsInGradebook; } /** * Get * * @return allowGroupAssignmentsGradebook */ public boolean getAllowGroupAssignmentsInGradebook() { return m_allowGroupAssignmentsInGradebook; } /********************************************************************************************************************************************************************************************************************************************************** * Init and Destroy *********************************************************************************************************************************************************************************************************************************************************/ /** * Final initialization, once all dependencies are set. */ public void init() { m_relativeAccessPoint = REFERENCE_ROOT; M_log.info(this + " init()"); // construct storage helpers and read m_assignmentStorage = newAssignmentStorage(); m_assignmentStorage.open(); m_contentStorage = newContentStorage(); m_contentStorage.open(); m_submissionStorage = newSubmissionStorage(); m_submissionStorage.open(); // make the cache if (m_caching) { m_assignmentCache = m_memoryService .newCache( "org.sakaiproject.assignment.api.AssignmentService.assignmentCache", new AssignmentCacheRefresher(), assignmentReference(null, "")); m_contentCache = m_memoryService .newCache( "org.sakaiproject.assignment.api.AssignmentService.contentCache", new AssignmentContentCacheRefresher(), contentReference(null, "")); m_submissionCache = m_memoryService .newCache( "org.sakaiproject.assignment.api.AssignmentService.submissionCache", new AssignmentSubmissionCacheRefresher(), submissionReference(null, "", "")); } m_allowSubmitByInstructor = m_serverConfigurationService.getBoolean("assignments.instructor.submit.for.student", m_allowSubmitByInstructor); if (!m_allowSubmitByInstructor) { M_log.info("Instructor submission of assignments is disabled - add assignments.instructor.submit.for.student=true to sakai config to enable"); } else { M_log.info("Instructor submission of assignments is enabled"); } // register as an entity producer m_entityManager.registerEntityProducer(this, REFERENCE_ROOT); // register functions FunctionManager.registerFunction(SECURE_ALL_GROUPS); FunctionManager.registerFunction(SECURE_ADD_ASSIGNMENT); FunctionManager.registerFunction(SECURE_ADD_ASSIGNMENT_SUBMISSION); FunctionManager.registerFunction(SECURE_REMOVE_ASSIGNMENT); FunctionManager.registerFunction(SECURE_ACCESS_ASSIGNMENT); FunctionManager.registerFunction(SECURE_UPDATE_ASSIGNMENT); FunctionManager.registerFunction(SECURE_GRADE_ASSIGNMENT_SUBMISSION); FunctionManager.registerFunction(SECURE_ASSIGNMENT_RECEIVE_NOTIFICATIONS); FunctionManager.registerFunction(SECURE_SHARE_DRAFTS); //if no contentReviewService was set try discovering it if (contentReviewService == null) { contentReviewService = (ContentReviewService) ComponentManager.get(ContentReviewService.class.getName()); } } // init /** * Returns to uninitialized state. */ public void destroy() { if (m_caching) { if (m_assignmentCache != null) { m_assignmentCache.destroy(); m_assignmentCache = null; } if (m_contentCache != null) { m_contentCache.destroy(); m_contentCache = null; } if (m_submissionCache != null) { m_submissionCache.destroy(); m_submissionCache = null; } } m_assignmentStorage.close(); m_assignmentStorage = null; m_contentStorage.close(); m_contentStorage = null; m_submissionStorage.close(); m_submissionStorage = null; M_log.info(this + " destroy()"); } /********************************************************************************************************************************************************************************************************************************************************** * AssignmentService implementation *********************************************************************************************************************************************************************************************************************************************************/ /** * Creates and adds a new Assignment to the service. * * @param context - * Describes the portlet context - generated with DefaultId.getChannel(). * @return The new Assignment object. * @throws IdInvalidException * if the id contains prohibited characers. * @throws IdUsedException * if the id is already used in the service. * @throws PermissionException * if current User does not have permission to do this. */ public AssignmentEdit addAssignment(String context) throws PermissionException { M_log.debug(this + " ENTERING ADD ASSIGNMENT : CONTEXT : " + context); String assignmentId = null; boolean badId = false; do { badId = !Validator.checkResourceId(assignmentId); assignmentId = IdManager.createUuid(); if (m_assignmentStorage.check(assignmentId)) badId = true; } while (badId); String key = assignmentReference(context, assignmentId); // security check if (!allowAddAssignment(context)) { throw new PermissionException(SessionManager.getCurrentSessionUserId(), SECURE_ADD_ASSIGNMENT, key); } // storage AssignmentEdit assignment = m_assignmentStorage.put(assignmentId, context); // event for tracking ((BaseAssignmentEdit) assignment).setEvent(AssignmentConstants.EVENT_ADD_ASSIGNMENT); M_log.debug(this + " LEAVING ADD ASSIGNMENT WITH : ID : " + assignment.getId()); return assignment; } // addAssignment /** * 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). * @exception IdInvalidException * if the assignment id is invalid. * @exception IdUsedException * if the assignment id is already used. * @exception PermissionException * if the current user does not have permission to add an assignnment. */ public AssignmentEdit mergeAssignment(Element el) throws IdInvalidException, IdUsedException, PermissionException { // construct from the XML Assignment assignmentFromXml = new BaseAssignment(el); // check for a valid assignment name if (!Validator.checkResourceId(assignmentFromXml.getId())) throw new IdInvalidException(assignmentFromXml.getId()); // check security (throws if not permitted) unlock(SECURE_ADD_ASSIGNMENT, assignmentFromXml.getReference()); // reserve a assignment with this id from the info store - if it's in use, this will return null AssignmentEdit assignment = m_assignmentStorage.put(assignmentFromXml.getId(), assignmentFromXml.getContext()); if (assignment == null) { throw new IdUsedException(assignmentFromXml.getId()); } // transfer from the XML read assignment object to the AssignmentEdit ((BaseAssignmentEdit) assignment).set(assignmentFromXml); ((BaseAssignmentEdit) assignment).setEvent(AssignmentConstants.EVENT_ADD_ASSIGNMENT); ResourcePropertiesEdit propertyEdit = (BaseResourcePropertiesEdit)assignment.getProperties(); try { propertyEdit.getTimeProperty(ResourceProperties.PROP_CREATION_DATE); } catch(EntityPropertyNotDefinedException epnde) { String now = TimeService.newTime().toString(); propertyEdit.addProperty(ResourceProperties.PROP_CREATION_DATE, now); } catch(EntityPropertyTypeException epte) { M_log.error(this + " mergeAssignment error when trying to get creation time property " + epte); } return assignment; } /** * Creates and adds a new Assignment to the service which is a copy of an existing Assignment. * * @param assignmentId - * The Assignment to be duplicated. * @return The new Assignment object, or null if the original Assignment does not exist. * @throws PermissionException * if current User does not have permission to do this. */ public AssignmentEdit addDuplicateAssignment(String context, String assignmentReference) throws PermissionException, IdInvalidException, IdUsedException, IdUnusedException { M_log.debug(this + " ENTERING ADD DUPLICATE ASSIGNMENT WITH ID : " + assignmentReference); AssignmentEdit retVal = null; AssignmentContentEdit newContent = null; if (assignmentReference != null) { String assignmentId = assignmentId(assignmentReference); if (!m_assignmentStorage.check(assignmentId)) throw new IdUnusedException(assignmentId); else { M_log.debug(this + " addDuplicateAssignment : assignment exists - will copy"); Assignment existingAssignment = getAssignment(assignmentReference); newContent = addDuplicateAssignmentContent(context, existingAssignment.getContentReference()); commitEdit(newContent); retVal = addAssignment(context); retVal.setContentReference(newContent.getReference()); retVal.setTitle(existingAssignment.getTitle() + " - " + rb.getString("assignment.copy")); retVal.setSection(existingAssignment.getSection()); retVal.setOpenTime(existingAssignment.getOpenTime()); retVal.setDueTime(existingAssignment.getDueTime()); retVal.setDropDeadTime(existingAssignment.getDropDeadTime()); retVal.setCloseTime(existingAssignment.getCloseTime()); retVal.setDraft(true); retVal.setGroup(existingAssignment.isGroup()); ResourcePropertiesEdit pEdit = (BaseResourcePropertiesEdit) retVal.getProperties(); pEdit.addAll(existingAssignment.getProperties()); addLiveProperties(pEdit); } } M_log.debug(this + " ADD DUPLICATE ASSIGNMENT : LEAVING ADD DUPLICATE ASSIGNMENT WITH ID : " + retVal != null ? retVal.getId() : ""); return retVal; } /** * Access the Assignment with the specified reference. * * @param assignmentReference - * The reference of the Assignment. * @return The Assignment corresponding to the reference, or null if it does not exist. * @throws IdUnusedException * if there is no object with this reference. * @throws PermissionException * if the current user is not allowed to access this. */ public Assignment getAssignment(String assignmentReference) throws IdUnusedException, PermissionException { M_log.debug(this + " GET ASSIGNMENT : REF : " + assignmentReference); // check security on the assignment unlockCheck(SECURE_ACCESS_ASSIGNMENT, assignmentReference); Assignment assignment = findAssignment(assignmentReference); String currentUserId = SessionManager.getCurrentSessionUserId(); if (assignment == null) throw new IdUnusedException(assignmentReference); return checkAssignmentAccessibleForUser(assignment, currentUserId); }// getAssignment /** * Check visibility of an assignment for a given user. We consider an * an assignment to be visible to the user if it has been opened and is * not deleted. However, we allow access to deleted assignments if the * user has already made a submission for the assignment. * * Note that this method does not check permissions at all. It should * already be established that the user is permitted to access this * assignment. * * @param assignment the assignment to check * @param userId the user for whom to check * @return true if the assignment is available (open, not deleted) or * submitted by the specified user; false otherwise */ private boolean isAvailableOrSubmitted(Assignment assignment, String userId) { boolean accessible = false; String deleted = assignment.getProperties().getProperty(ResourceProperties.PROP_ASSIGNMENT_DELETED); if (deleted == null || "".equals(deleted)) { // show not deleted, not draft, opened assignments Time openTime = assignment.getOpenTime(); if (openTime != null && TimeService.newTime().after(openTime) && !assignment.getDraft()) { accessible = true; } } else if (deleted.equalsIgnoreCase(Boolean.TRUE.toString()) && (assignment.getContent().getTypeOfSubmission() != Assignment.NON_ELECTRONIC_ASSIGNMENT_SUBMISSION) && getSubmission(assignment.getReference(), userId) != null) { // and those deleted but not non-electronic assignments but the user has made submissions to them accessible = true; } return accessible; } private Assignment checkAssignmentAccessibleForUser(Assignment assignment, String currentUserId) throws PermissionException { if (assignment.getAccess() == Assignment.AssignmentAccess.GROUPED) { String context = assignment.getContext(); Collection<Group> asgGroups = assignment.getGroups(); Collection<Group> allowedGroups = getGroupsAllowGetAssignment(context, currentUserId); // reject and throw PermissionException if there is no intersection if (!allowAllGroups(context) && !isIntersectionGroupRefsToGroups(asgGroups, allowedGroups)) { throw new PermissionException(currentUserId, SECURE_ACCESS_ASSIGNMENT, assignment.getReference()); } } if (assignment.getAccess() == Assignment.AssignmentAccess.GROUPED) { Collection<Group> asgGroups = assignment.getGroups(); Collection<Group> allowedGroups = getGroupsAllowGetAssignment(assignment.getContext(), currentUserId); // reject and throw PermissionException if there is no intersection if (!isIntersectionGroupRefsToGroups(asgGroups, allowedGroups)) { throw new PermissionException(currentUserId, SECURE_ACCESS_ASSIGNMENT, assignment.getReference()); } } if (allowAddAssignment(assignment.getContext())) { // always return for users can add assignent in the context return assignment; } else if (isAvailableOrSubmitted(assignment, currentUserId)) { return assignment; } throw new PermissionException(currentUserId, SECURE_ACCESS_ASSIGNMENT, assignment.getReference()); } protected Assignment findAssignment(String assignmentReference) { Assignment assignment = null; String assignmentId = assignmentId(assignmentReference); if ((m_caching) && (m_assignmentCache != null) && (!m_assignmentCache.disabled())) { // if we have it in the cache, use it if (m_assignmentCache.containsKey(assignmentReference)) assignment = (Assignment) m_assignmentCache.get(assignmentReference); if ( assignment == null ) //SAK-12447 cache.get can return null on expired { assignment = m_assignmentStorage.get(assignmentId); // cache the result m_assignmentCache.put(assignmentReference, assignment); } } else { assignment = m_assignmentStorage.get(assignmentId); } return assignment; } /** * Access all assignment objects - known to us (not from external providers). * * @return A list of assignment objects. */ protected List getAssignments(String context) { return assignments(context, null); } // getAssignments /** * Access all assignment objects - known to us (not from external providers) and accessible by the user * * @return A list of assignment objects. */ protected List getAssignments(String context, String userId) { return assignments(context, userId); } // getAssignments // private List assignments(String context, String userId) { List rv = new ArrayList(); if (!allowGetAssignment(context)) { // no permission to read assignment in context return rv; } else { List assignments = getUnfilteredAssignments(context); if (userId == null) { userId = SessionManager.getCurrentSessionUserId(); } // check for the site and group permissions of these assignments as well as visibility (release time, etc.) rv = getAccessibleAssignments(assignments, context, userId); } return rv; } /** * Access all assignment objects for a site without considering user permissions. * This should be used with care; almost all scenarios should use {@link getAssignments(String)} * or {@link getAssignments(String, String)}, which do enforce permissions and visibility. * * TODO: Decide whether or not this should be exposed as part of the public API. * * @return A list of Assignment objects. * */ protected List getUnfilteredAssignments(String context) { List assignments = new ArrayList(); if ((m_caching) && (m_assignmentCache != null) && (!m_assignmentCache.disabled())) { // if the cache is complete, use it if (m_assignmentCache.isComplete()) { assignments = m_assignmentCache.getAll(); // TODO: filter by context } // otherwise get all the assignments from storage else { // Note: while we are getting from storage, storage might change. These can be processed // after we get the storage entries, and put them in the cache, and mark the cache complete. // -ggolden synchronized (m_assignmentCache) { // if we were waiting and it's now complete... if (m_assignmentCache.isComplete()) { assignments = m_assignmentCache.getAll(); return assignments; } // save up any events to the cache until we get past this load m_assignmentCache.holdEvents(); assignments = m_assignmentStorage.getAll(context); // update the cache, and mark it complete for (int i = 0; i < assignments.size(); i++) { Assignment assignment = (Assignment) assignments.get(i); m_assignmentCache.put(assignment.getReference(), assignment); } m_assignmentCache.setComplete(); // TODO: not reall, just for context // now we are complete, process any cached events m_assignmentCache.processEvents(); } } } else { // // if we have done this already in this thread, use that // assignments = (List) CurrentService.getInThread(context+".assignment.assignments"); // if (assignments == null) // { assignments = m_assignmentStorage.getAll(context); // // // "cache" the assignments in the current service in case they are needed again in this thread... // if (assignments != null) // { // CurrentService.setInThread(context+".assignment.assignments", assignments); // } // } } return assignments; } /** * Filter a list of assignments to those that the supplied user can access. * * This method is primarily provided to be called from assignments() for * set-based efficiency over iteration in building a list of assignments * for a given user. * * There are a few ways that we consider an assignment to be accessible: * 1. The user can add assignments to the site, or * 2. The assignment is grouped and the user can view assignments in at * least one of those groups, or * 3. The assignment is ungrouped and the user can view assignments in * the site * An additional state check applies, which is that the assignment is * not visible if it is deleted, except when the user has made a * submission for it already or can add (manage) assignments. * * These rules were extracted from assignments() and we are enforcing * them here for a set, rather than a single assignment. * * This is a somewhat awkward signature; it should really either have just the * assignments list or just the siteId, but the other methods are not refactored * now. Namely, getAssignments calls assignments, which has some cache specifics * and other items that would need to be refactored very carefully. Rather than * potentially changing the behavior subtly, this only replaces the iterative * permissions checks with set-based ones. * * @param assignments a list of assignments to filter; must all be from the same site * @param siteId the Site ID for all assignments * @param userId the user whose access should be checked for the assignments * @return a list of the assignments that are accessible; will never be null but may be empty */ protected List<Assignment> getAccessibleAssignments(List<Assignment> assignments, String siteId, String userId) { // Make sure that everything is from the specified site List<Assignment> siteAssignments = filterAssignmentsBySite(assignments, siteId); // Check whether the user can add assignments for the site. // If so, return the full list. String siteRef = SiteService.siteReference(siteId); boolean allowAdd = SecurityService.unlock(userId, SECURE_ALL_GROUPS, siteRef); if (allowAdd) { return siteAssignments; } // Partition the assignments into grouped and ungrouped for access checks List<List<Assignment>> partitioned = partitionAssignments(siteAssignments); List<Assignment> grouped = partitioned.get(0); List<Assignment> ungrouped = partitioned.get(1); List<Assignment> permitted = new ArrayList<Assignment>(); // Check the user's site permissions and collect all of the ungrouped // assignments if the user has permission boolean allowSiteGet = SecurityService.unlock(userId, SECURE_ACCESS_ASSIGNMENT, siteRef); if (allowSiteGet) { permitted.addAll(ungrouped); } // Collect grouped assignments that the user can access permitted.addAll(filterGroupedAssignmentsForAccess(grouped, siteId, userId)); // Filter for visibility/submission state List<Assignment> visible = (SecurityService.unlock(userId, SECURE_ADD_ASSIGNMENT, siteRef))? permitted : filterAssignmentsByVisibility(permitted, userId); // We are left with the original list filtered by site/group permissions and visibility/submission state return visible; } /** * Filter a list of assignments to those in a given site. * * @param assignments the list of assignments to filter; none may be null * @param siteId the site ID to use to filter * @return a new list with only the assignments that belong to the site; * never null, but empty if the site doesn't exist, the assignments * list is empty, or none of the assignments belong to the site */ protected List<Assignment> filterAssignmentsBySite(List<Assignment> assignments, String siteId) { List<Assignment> filtered = new ArrayList<Assignment>(); if (siteId == null) { return filtered; } try { SiteService.getSite(siteId); } catch (IdUnusedException e) { return filtered; } for (Assignment assignment : assignments) { if (assignment != null && siteId.equals(assignment.getContext())) { filtered.add(assignment); } } return filtered; } /** * Partition a list of assignments into those that are grouped and ungrouped. * * @param assignments the list of assignments to inspect and partition * @return a two-element list containing List<Assignment> in both indexes; * the first is the grouped assignments, the second is ungrouped; * never null, always two elements, neither list is null; * any null assignments will be omitted in the final lists */ protected List<List<Assignment>> partitionAssignments(List<Assignment> assignments) { List<Assignment> grouped = new ArrayList<Assignment>(); List<Assignment> ungrouped = new ArrayList<Assignment>(); for (Assignment assignment : assignments) { if (assignment != null && assignment.getAccess() == Assignment.AssignmentAccess.GROUPED) { grouped.add(assignment); } else { ungrouped.add(assignment); } } List<List<Assignment>> partitioned = new ArrayList<List<Assignment>>(); partitioned.add(grouped); partitioned.add(ungrouped); return partitioned; } /** * Filter a list of grouped assignments by permissions based on a given site. Note that * this does not consider the assignment or submission state, only permissions. * * @param assignments the list of assignments to filter; should all be grouped and from the same site * @param siteId the site to which all of the assignments belong * @param userId the user for which group permissions should be checked * @return a new list of assignments, containing those supplied that the user * can access, based on permission to view the assignment in one or more of its * groups, permission to add assignments in the site, or permission to * view assignments in all of the site's groups; never null but may be empty * */ protected List<Assignment> filterGroupedAssignmentsForAccess(List<Assignment> assignments, String siteId, String userId) { List<Assignment> filtered = new ArrayList<Assignment>(); // Short-circuit to save the group query if we can't make a reasonable check if (assignments == null || assignments.isEmpty() || siteId == null || userId == null) { return filtered; } // Collect the groups where the user is permitted to view assignments // and the groups covered by the assignments, then check the // intersection to keep only visible assignments. Collection<Group> allowedGroups = (Collection<Group>) getGroupsAllowGetAssignment(siteId, userId); Set<String> allowedGroupRefs = new HashSet<String>(); for (Group group : allowedGroups) { allowedGroupRefs.add(group.getReference()); } for (Assignment assignment : assignments) { for (String groupRef : (Collection<String>) assignment.getGroups()) { if (allowedGroupRefs.contains(groupRef)) { filtered.add(assignment); break; } } } return filtered; } /** * Filter a list of assignments based on visibility (open time, deletion, submission, etc.) * for a specified user. Note that this only considers assignment and submission state and * does not consider permissions so the assignments should have already been checked for * permissions for the given user. * * @param assignments the list of assignments to filter * @param userId the user for whom to check visibility; should be permitted to * access all of the assignments * @return a new list containing those supplied that the user may access, based * on visibility; never null but may be empty */ protected List<Assignment> filterAssignmentsByVisibility(List<Assignment> assignments, String userId) { List<Assignment> visible = new ArrayList<Assignment>(); for (Assignment assignment : assignments) { if (assignment != null && isAvailableOrSubmitted(assignment, userId)) { visible.add(assignment); } } return visible; } /** * See if the collection of group reference strings has at least one group that is in the collection of Group objects. * * @param groupRefs * The collection (String) of group references. * @param groups * The collection (Group) of group objects. * @return true if there is interesection, false if not. */ protected boolean isIntersectionGroupRefsToGroups(Collection groupRefs, Collection groups) { for (Iterator iRefs = groupRefs.iterator(); iRefs.hasNext();) { String findThisGroupRef = (String) iRefs.next(); for (Iterator iGroups = groups.iterator(); iGroups.hasNext();) { String thisGroupRef = ((Group) iGroups.next()).getReference(); if (thisGroupRef.equals(findThisGroupRef)) { return true; } } } return false; } /** * Get a locked assignment object for editing. Must commitEdit() to make official, or cancelEdit() when done! * * @param id * The assignment id string. * @return An AssignmentEdit object for editing. * @exception IdUnusedException * if not found, or if not an AssignmentEdit object * @exception PermissionException * if the current user does not have permission to edit this assignment. * @exception InUseException * if the assignment is being edited by another user. */ public AssignmentEdit editAssignment(String assignmentReference) throws IdUnusedException, PermissionException, InUseException { // check security (throws if not permitted) unlock(SECURE_UPDATE_ASSIGNMENT, assignmentReference); String assignmentId = assignmentId(assignmentReference); // check for existance if (!m_assignmentStorage.check(assignmentId)) { throw new IdUnusedException(assignmentId); } // ignore the cache - get the assignment with a lock from the info store AssignmentEdit assignmentEdit = m_assignmentStorage.edit(assignmentId); if (assignmentEdit == null) throw new InUseException(assignmentId); ((BaseAssignmentEdit) assignmentEdit).setEvent(AssignmentConstants.EVENT_UPDATE_ASSIGNMENT); return assignmentEdit; } // editAssignment /** * Commit the changes made to an AssignmentEdit object, and release the lock. * * @param assignment * The AssignmentEdit object to commit. */ public void commitEdit(AssignmentEdit assignment) { // check for closed edit if (!assignment.isActiveEdit()) { try { throw new Exception(); } catch (Exception e) { M_log.warn(" commitEdit(): closed AssignmentEdit " + e.getMessage() + " assignment id=" + assignment.getId()); } return; } // update the properties addLiveUpdateProperties(assignment.getPropertiesEdit()); // complete the edit m_assignmentStorage.commit(assignment); //update peer assessment information: if(!assignment.getDraft() && assignment.getAllowPeerAssessment()){ assignmentPeerAssessmentService.schedulePeerReview(assignment.getId()); }else{ assignmentPeerAssessmentService.removeScheduledPeerReview(assignment.getId()); } // track it EventTrackingService.post(EventTrackingService.newEvent(((BaseAssignmentEdit) assignment).getEvent(), assignment .getReference(), true)); // close the edit object ((BaseAssignmentEdit) assignment).closeEdit(); } // commitEdit /** * Cancel the changes made to a AssignmentEdit object, and release the lock. * * @param assignment * The AssignmentEdit object to commit. */ public void cancelEdit(AssignmentEdit assignment) { // check for closed edit if (!assignment.isActiveEdit()) { try { throw new Exception(); } catch (Exception e) { M_log.warn(" cancelEdit(): closed AssignmentEdit " + e.getMessage() + " assignment id=" + assignment.getId()); } return; } // release the edit lock m_assignmentStorage.cancel(assignment); // close the edit object ((BaseAssignmentEdit) assignment).closeEdit(); } // cancelEdit(Assignment) /** * {@inheritDoc} */ public void removeAssignment(AssignmentEdit assignment) throws PermissionException { if (assignment != null) { M_log.debug(this + " removeAssignment with id : " + assignment.getId()); if (!assignment.isActiveEdit()) { try { throw new Exception(); } catch (Exception e) { M_log.warn(" removeAssignment(): closed AssignmentEdit" + e.getMessage() + " assignment id=" + assignment.getId()); } return; } // CHECK PERMISSION unlock(SECURE_REMOVE_ASSIGNMENT, assignment.getReference()); // complete the edit m_assignmentStorage.remove(assignment); // track event EventTrackingService.post(EventTrackingService.newEvent(AssignmentConstants.EVENT_REMOVE_ASSIGNMENT, assignment.getReference(), true)); // close the edit object ((BaseAssignmentEdit) assignment).closeEdit(); // remove any realm defined for this resource try { AuthzGroupService.removeAuthzGroup(assignment.getReference()); } catch (AuthzPermissionException e) { M_log.warn(" removeAssignment: removing realm for assignment reference=" + assignment.getReference() + " : " + e.getMessage()); } } }// removeAssignment /** * {@inheritDoc} */ public void removeAssignmentAndAllReferences(AssignmentEdit assignment) throws PermissionException { if (assignment != null) { M_log.debug(this + " removeAssignmentAndAllReferences with id : " + assignment.getId()); if (!assignment.isActiveEdit()) { try { throw new Exception(); } catch (Exception e) { M_log.warn(" removeAssignmentAndAllReferences(): closed AssignmentEdit" + e.getMessage() + " assignment id=" + assignment.getId()); } return; } // CHECK PERMISSION unlock(SECURE_REMOVE_ASSIGNMENT, assignment.getReference()); // we may need to remove associated calendar events and annc, so get the basic info here ResourcePropertiesEdit pEdit = assignment.getPropertiesEdit(); String context = assignment.getContext(); // 1. remove associated calendar events, if exists removeAssociatedCalendarItem(getCalendar(context), assignment, pEdit); // 2. remove associated announcement, if exists removeAssociatedAnnouncementItem(getAnnouncementChannel(context), assignment, pEdit); // 3. remove Gradebook items, if linked removeAssociatedGradebookItem(pEdit, context); // 4. remove tags as necessary removeAssociatedTaggingItem(assignment); // 5. remove assignment submissions List submissions = getSubmissions(assignment); if (submissions != null) { for (Iterator sIterator=submissions.iterator(); sIterator.hasNext();) { AssignmentSubmission s = (AssignmentSubmission)sIterator.next(); String sReference = s.getReference(); try { removeSubmission(editSubmission(sReference)); } catch (PermissionException e) { M_log.warn("removeAssignmentAndAllReference: User does not have permission to remove submission " + sReference + " for assignment: " + assignment.getId() + e.getMessage()); } catch (InUseException e) { M_log.warn("removeAssignmentAndAllReference: submission " + sReference + " for assignment: " + assignment.getId() + " is in use. " + e.getMessage()); }catch (IdUnusedException e) { M_log.warn("removeAssignmentAndAllReference: submission " + sReference + " for assignment: " + assignment.getId() + " does not exist. " + e.getMessage()); } } } // 6. remove associated content object try { removeAssignmentContent(editAssignmentContent(assignment.getContent().getReference())); } catch (AssignmentContentNotEmptyException e) { M_log.warn(" removeAssignmentAndAllReferences(): cannot remove non-empty AssignmentContent object for assignment = " + assignment.getId() + ". " + e.getMessage()); } catch (PermissionException e) { M_log.warn(" removeAssignmentAndAllReferences(): not allowed to remove AssignmentContent object for assignment = " + assignment.getId() + ". " + e.getMessage()); } catch (InUseException e) { M_log.warn(" removeAssignmentAndAllReferences(): AssignmentContent object for assignment = " + assignment.getId() + " is in used. " + e.getMessage()); } catch (IdUnusedException e) { M_log.warn(" removeAssignmentAndAllReferences(): cannot find AssignmentContent object for assignment = " + assignment.getId() + ". " + e.getMessage()); } // 7. remove assignment m_assignmentStorage.remove(assignment); // close the edit object ((BaseAssignmentEdit) assignment).closeEdit(); // 8. remove any realm defined for this resource try { AuthzGroupService.removeAuthzGroup(assignment.getReference()); } catch (AuthzPermissionException e) { M_log.warn(" removeAssignment: removing realm for assignment reference=" + assignment.getReference() + " : " + e.getMessage()); } // track event EventTrackingService.post(EventTrackingService.newEvent(AssignmentConstants.EVENT_REMOVE_ASSIGNMENT, assignment.getReference(), true)); } }// removeAssignment /** * remove the associated tagging items * @param assignment */ private void removeAssociatedTaggingItem(AssignmentEdit assignment) { try { if (m_taggingManager.isTaggable()) { for (TaggingProvider provider : m_taggingManager.getProviders()) { provider.removeTags(m_assignmentActivityProducer.getActivity(assignment)); } } } catch (PermissionException pe) { M_log.warn("removeAssociatedTaggingItem: User does not have permission to remove tags for assignment: " + assignment.getId() + " via transferCopyEntities"); } } /** * remove the linked Gradebook item related with the assignment * @param pEdit * @param context */ private void removeAssociatedGradebookItem(ResourcePropertiesEdit pEdit, String context) { String associatedGradebookAssignment = pEdit.getProperty(AssignmentService.PROP_ASSIGNMENT_ASSOCIATE_GRADEBOOK_ASSIGNMENT); if (associatedGradebookAssignment != null) { boolean isExternalAssignmentDefined = m_gradebookExternalAssessmentService.isExternalAssignmentDefined(context, associatedGradebookAssignment); if (isExternalAssignmentDefined) { m_gradebookExternalAssessmentService.removeExternalAssessment(context, associatedGradebookAssignment); } } } private Calendar getCalendar(String contextId) { Calendar calendar = null; String calendarId = m_serverConfigurationService.getString("calendar", null); if (calendarId == null) { calendarId = m_calendarService.calendarReference(contextId, SiteService.MAIN_CONTAINER); try { calendar = m_calendarService.getCalendar(calendarId); } catch (IdUnusedException e) { M_log.warn("getCalendar: No calendar found for site: " + contextId); calendar = null; } catch (PermissionException e) { M_log.warn("getCalendar: The current user does not have permission to access " + "the calendar for context: " + contextId, e); } catch (Exception ex) { M_log.warn("getCalendar: Unknown exception occurred retrieving calendar for site: " + contextId, ex); calendar = null; } } return calendar; } /** * Will determine if there is a calendar event associated with this assignment and * remove it, if found. * @param calendar Calendar * @param aEdit AssignmentEdit * @param pEdit ResourcePropertiesEdit */ private void removeAssociatedCalendarItem(Calendar calendar, AssignmentEdit aEdit, ResourcePropertiesEdit pEdit) { String isThereEvent = pEdit.getProperty(NEW_ASSIGNMENT_DUE_DATE_SCHEDULED); if (isThereEvent != null && isThereEvent.equals(Boolean.TRUE.toString())) { // remove the associated calendar event if (calendar != null) { // already has calendar object // get the old event CalendarEvent event = null; String oldEventId = pEdit.getProperty(ResourceProperties.PROP_ASSIGNMENT_DUEDATE_CALENDAR_EVENT_ID); if (oldEventId != null) { try { event = calendar.getEvent(oldEventId); } catch (IdUnusedException ee) { // no action needed for this condition M_log.warn(":removeCalendarEvent " + ee.getMessage()); } catch (PermissionException ee) { M_log.warn(":removeCalendarEvent " + ee.getMessage()); } } // remove the event if it exists if (event != null) { try { calendar.removeEvent(calendar.getEditEvent(event.getId(), CalendarService.EVENT_REMOVE_CALENDAR)); pEdit.removeProperty(NEW_ASSIGNMENT_DUE_DATE_SCHEDULED); pEdit.removeProperty(ResourceProperties.PROP_ASSIGNMENT_DUEDATE_CALENDAR_EVENT_ID); } catch (PermissionException ee) { M_log.warn(":removeCalendarEvent not allowed to remove calendar event for assignment = " + aEdit.getTitle() + ". "); } catch (InUseException ee) { M_log.warn(":removeCalendarEvent someone else is editing calendar event for assignment = " + aEdit.getTitle() + ". "); } catch (IdUnusedException ee) { M_log.warn(":removeCalendarEvent calendar event are in use for assignment = " + aEdit.getTitle() + " and event =" + event.getId()); } } } } } private AnnouncementChannel getAnnouncementChannel(String contextId) { AnnouncementService aService = org.sakaiproject.announcement.cover.AnnouncementService.getInstance(); AnnouncementChannel channel = null; String channelId = m_serverConfigurationService.getString(m_announcementService.ANNOUNCEMENT_CHANNEL_PROPERTY, null); if (channelId == null) { channelId = m_announcementService.channelReference(contextId, SiteService.MAIN_CONTAINER); try { channel = aService.getAnnouncementChannel(channelId); } catch (IdUnusedException e) { M_log.warn("getAnnouncement:No announcement channel found"); channel = null; } catch (PermissionException e) { M_log.warn("getAnnouncement:Current user not authorized to deleted annc associated " + "with assignment. " + e.getMessage()); channel = null; } } return channel; } /** * Will determine if there is an announcement associated * with this assignment and removes it, if found. * @param channel AnnouncementChannel * @param aEdit AssignmentEdit * @param pEdit ResourcePropertiesEdit */ private void removeAssociatedAnnouncementItem(AnnouncementChannel channel, AssignmentEdit aEdit, ResourcePropertiesEdit pEdit) { if (channel != null) { String openDateAnnounced = StringUtils.trimToNull(pEdit.getProperty("new_assignment_open_date_announced")); String openDateAnnouncementId = StringUtils.trimToNull(pEdit.getProperty(ResourceProperties.PROP_ASSIGNMENT_OPENDATE_ANNOUNCEMENT_MESSAGE_ID)); if (openDateAnnounced != null && openDateAnnouncementId != null) { try { channel.removeMessage(openDateAnnouncementId); } catch (PermissionException e) { M_log.warn(":removeAnnouncement " + e.getMessage()); } } } } /** * Creates and adds a new AssignmentContent to the service. * * @param context - * Describes the portlet context - generated with DefaultId.getChannel(). * @return AssignmentContent The new AssignmentContent object. * @throws PermissionException * if current User does not have permission to do this. */ public AssignmentContentEdit addAssignmentContent(String context) throws PermissionException { M_log.debug(this + " ENTERING ADD ASSIGNMENT CONTENT"); String contentId = null; boolean badId = false; do { badId = !Validator.checkResourceId(contentId); contentId = IdManager.createUuid(); if (m_contentStorage.check(contentId)) badId = true; } while (badId); // security check if (!allowAddAssignmentContent(context)) { throw new PermissionException(SessionManager.getCurrentSessionUserId(), SECURE_ADD_ASSIGNMENT_CONTENT, contentId); } AssignmentContentEdit content = m_contentStorage.put(contentId, context); M_log.debug(this + " LEAVING ADD ASSIGNMENT CONTENT : ID : " + content.getId()); // event for tracking ((BaseAssignmentContentEdit) content).setEvent(AssignmentConstants.EVENT_ADD_ASSIGNMENT_CONTENT); return content; }// addAssignmentContent /** * Add a new AssignmentContent 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 AssignmentContent. * @return A locked AssignmentContentEdit object (reserving the id). * @exception IdInvalidException * if the AssignmentContent id is invalid. * @exception IdUsedException * if the AssignmentContent id is already used. * @exception PermissionException * if the current user does not have permission to add an AssignnmentContent. */ public AssignmentContentEdit mergeAssignmentContent(Element el) throws IdInvalidException, IdUsedException, PermissionException { // construct from the XML AssignmentContent contentFromXml = new BaseAssignmentContent(el); // check for a valid assignment name if (!Validator.checkResourceId(contentFromXml.getId())) throw new IdInvalidException(contentFromXml.getId()); // check security (throws if not permitted) unlock(SECURE_ADD_ASSIGNMENT_CONTENT, contentFromXml.getReference()); // reserve a content with this id from the info store - if it's in use, this will return null AssignmentContentEdit content = m_contentStorage.put(contentFromXml.getId(), contentFromXml.getContext()); if (content == null) { throw new IdUsedException(contentFromXml.getId()); } // transfer from the XML read content object to the AssignmentContentEdit ((BaseAssignmentContentEdit) content).set(contentFromXml); ((BaseAssignmentContentEdit) content).setEvent(AssignmentConstants.EVENT_ADD_ASSIGNMENT_CONTENT); return content; } /** * Creates and adds a new AssignmentContent to the service which is a copy of an existing AssignmentContent. * * @param context - * From DefaultId.getChannel(RunData) * @param contentReference - * The id of the AssignmentContent to be duplicated. * @return AssignmentContentEdit The new AssignmentContentEdit object, or null if the original does not exist. * @throws PermissionException * if current User does not have permission to do this. */ public AssignmentContentEdit addDuplicateAssignmentContent(String context, String contentReference) throws PermissionException, IdInvalidException, IdUnusedException { M_log.debug(this + " ENTERING ADD DUPLICATE ASSIGNMENT CONTENT : " + contentReference); AssignmentContentEdit retVal = null; AssignmentContent existingContent = null; List tempVector = null; Reference tempRef = null; Reference newRef = null; if (contentReference != null) { String contentId = contentId(contentReference); if (!m_contentStorage.check(contentId)) throw new IdUnusedException(contentId); else { M_log.debug(this + " ADD DUPL. CONTENT : found match - will copy"); existingContent = getAssignmentContent(contentReference); retVal = addAssignmentContent(context); retVal.setTitle(existingContent.getTitle() + " - " + rb.getString("assignment.copy")); retVal.setInstructions(existingContent.getInstructions()); retVal.setHonorPledge(existingContent.getHonorPledge()); retVal.setHideDueDate(existingContent.getHideDueDate()); retVal.setTypeOfSubmission(existingContent.getTypeOfSubmission()); retVal.setTypeOfGrade(existingContent.getTypeOfGrade()); retVal.setMaxGradePoint(existingContent.getMaxGradePoint()); retVal.setGroupProject(existingContent.getGroupProject()); retVal.setIndividuallyGraded(existingContent.individuallyGraded()); retVal.setReleaseGrades(existingContent.releaseGrades()); retVal.setAllowAttachments(existingContent.getAllowAttachments()); // for ContentReview service retVal.setAllowReviewService(existingContent.getAllowReviewService()); tempVector = existingContent.getAttachments(); if (tempVector != null) { for (int z = 0; z < tempVector.size(); z++) { tempRef = (Reference) tempVector.get(z); if (tempRef != null) { String tempRefId = tempRef.getId(); String tempRefCollectionId = m_contentHostingService.getContainingCollectionId(tempRefId); try { // get the original attachment display name ResourceProperties p = m_contentHostingService.getProperties(tempRefId); String displayName = p.getProperty(ResourceProperties.PROP_DISPLAY_NAME); // add another attachment instance String newItemId = m_contentHostingService.copyIntoFolder(tempRefId, tempRefCollectionId); ContentResourceEdit copy = m_contentHostingService.editResource(newItemId); // with the same display name ResourcePropertiesEdit pedit = copy.getPropertiesEdit(); pedit.addProperty(ResourceProperties.PROP_DISPLAY_NAME, displayName); m_contentHostingService.commitResource(copy, NotificationService.NOTI_NONE); newRef = m_entityManager.newReference(copy.getReference()); retVal.addAttachment(newRef); } catch (Exception e) { M_log.warn(" LEAVING ADD DUPLICATE CONTENT : " + e.toString()); } } } } ResourcePropertiesEdit pEdit = (BaseResourcePropertiesEdit) retVal.getPropertiesEdit(); pEdit.addAll(existingContent.getProperties()); addLiveProperties(pEdit); } } M_log.debug(this + " LEAVING ADD DUPLICATE CONTENT WITH ID : " + retVal != null ? retVal.getId() : ""); return retVal; } /** * Access the AssignmentContent with the specified reference. * * @param contentReference - * The reference of the AssignmentContent. * @return The AssignmentContent corresponding to the reference, or null if it does not exist. * @throws IdUnusedException * if there is no object with this reference. * @throws PermissionException * if the current user is not allowed to access this. */ public AssignmentContent getAssignmentContent(String contentReference) throws IdUnusedException, PermissionException { M_log.debug(this + " GET CONTENT : ID : " + contentReference); // check security on the assignment content unlockCheck(SECURE_ACCESS_ASSIGNMENT_CONTENT, contentReference); AssignmentContent content = null; // if we have it in the cache, use it String contentId = contentId(contentReference); if ((m_caching) && (m_contentCache != null) && (!m_contentCache.disabled())) { if (m_contentCache.containsKey(contentReference)) content = (AssignmentContent) m_contentCache.get(contentReference); if ( content == null ) //SAK-12447 cache.get can return null on expired { content = m_contentStorage.get(contentId); // cache the result m_contentCache.put(contentReference, content); } } else { // // if we have done this already in this thread, use that // content = (AssignmentContent) CurrentService.getInThread(contentId+".assignment.content"); // if (content == null) // { content = m_contentStorage.get(contentId); // // // "cache" the content in the current service in case they are needed again in this thread... // if (content != null) // { // CurrentService.setInThread(contentId+".assignment.content", contentId); // } // } } if (content == null) throw new IdUnusedException(contentId); M_log.debug(this + " GOT ASSIGNMENT CONTENT : ID : " + content.getId()); // track event // EventTrackingService.post(EventTrackingService.newEvent(AssignmentConstants.EVENT_ACCESS_ASSIGNMENT_CONTENT, content.getReference(), false)); return content; }// getAssignmentContent /** * Access all AssignmentContent objects - known to us (not from external providers). * * @return A list of AssignmentContent objects. */ protected List getAssignmentContents(String context) { List contents = new ArrayList(); if ((m_caching) && (m_contentCache != null) && (!m_contentCache.disabled())) { // if the cache is complete, use it if (m_contentCache.isComplete()) { contents = m_contentCache.getAll(); // TODO: filter by context } // otherwise get all the contents from storage else { // Note: while we are getting from storage, storage might change. These can be processed // after we get the storage entries, and put them in the cache, and mark the cache complete. // -ggolden synchronized (m_contentCache) { // if we were waiting and it's now complete... if (m_contentCache.isComplete()) { contents = m_contentCache.getAll(); return contents; } // save up any events to the cache until we get past this load m_contentCache.holdEvents(); contents = m_contentStorage.getAll(context); // update the cache, and mark it complete for (int i = 0; i < contents.size(); i++) { AssignmentContent content = (AssignmentContent) contents.get(i); m_contentCache.put(content.getReference(), content); } m_contentCache.setComplete(); // TODO: not really, just for context // now we are complete, process any cached events m_contentCache.processEvents(); } } } else { // // if we have done this already in this thread, use that // contents = (List) CurrentService.getInThread(context+".assignment.contents"); // if (contents == null) // { contents = m_contentStorage.getAll(context); // // // "cache" the contents in the current service in case they are needed again in this thread... // if (contents != null) // { // CurrentService.setInThread(context+".assignment.contents", contents); // } // } } return contents; } // getAssignmentContents /** * Get a locked AssignmentContent object for editing. Must commitEdit() to make official, or cancelEdit() when done! * * @param id * The content id string. * @return An AssignmentContentEdit object for editing. * @exception IdUnusedException * if not found, or if not an AssignmentContentEdit object * @exception PermissionException * if the current user does not have permission to edit this content. * @exception InUseException * if the assignment is being edited by another user. */ public AssignmentContentEdit editAssignmentContent(String contentReference) throws IdUnusedException, PermissionException, InUseException { // check security (throws if not permitted) unlock(SECURE_UPDATE_ASSIGNMENT_CONTENT, contentReference); String contentId = contentId(contentReference); // check for existance if (!m_contentStorage.check(contentId)) { throw new IdUnusedException(contentId); } // ignore the cache - get the AssignmentContent with a lock from the info store AssignmentContentEdit content = m_contentStorage.edit(contentId); if (content == null) throw new InUseException(contentId); ((BaseAssignmentContentEdit) content).setEvent(AssignmentConstants.EVENT_UPDATE_ASSIGNMENT_CONTENT); return content; } // editAssignmentContent /** * Commit the changes made to an AssignmentContentEdit object, and release the lock. * * @param content * The AssignmentContentEdit object to commit. */ public void commitEdit(AssignmentContentEdit content) { // check for closed edit if (!content.isActiveEdit()) { try { throw new Exception(); } catch (Exception e) { M_log.warn(" commitEdit(): closed AssignmentContentEdit " + e + " content id=" + content.getId()); } return; } // update the properties addLiveUpdateProperties(content.getPropertiesEdit()); // complete the edit m_contentStorage.commit(content); // track it EventTrackingService.post(EventTrackingService.newEvent(((BaseAssignmentContentEdit) content).getEvent(), content .getReference(), true)); // close the edit object ((BaseAssignmentContentEdit) content).closeEdit(); } // commitEdit(AssignmentContent) /** * Cancel the changes made to a AssignmentContentEdit object, and release the lock. * * @param content * The AssignmentContentEdit object to commit. */ public void cancelEdit(AssignmentContentEdit content) { // check for closed edit if (!content.isActiveEdit()) { try { throw new Exception(); } catch (Exception e) { M_log.warn(" cancelEdit(): closed AssignmentContentEdit " + e.getMessage() + " assignment content id=" + content.getId()); } return; } // release the edit lock m_contentStorage.cancel(content); // close the edit object ((BaseAssignmentContentEdit) content).closeEdit(); } // cancelEdit(Content) /** * Removes an AssignmentContent * * @param content - * the AssignmentContent to remove. * @throws an * AssignmentContentNotEmptyException if this content still has related Assignments. * @throws PermissionException * if current User does not have permission to do this. */ public void removeAssignmentContent(AssignmentContentEdit content) throws AssignmentContentNotEmptyException, PermissionException { if (content != null) { if (!content.isActiveEdit()) { try { throw new Exception(); } catch (Exception e) { M_log.warn(" removeAssignmentContent(): closed AssignmentContentEdit " + e.getMessage() + " assignment content id=" + content.getId()); } return; } // CHECK SECURITY unlock(SECURE_REMOVE_ASSIGNMENT_CONTENT, content.getReference()); // complete the edit m_contentStorage.remove(content); // track event EventTrackingService.post(EventTrackingService.newEvent(AssignmentConstants.EVENT_REMOVE_ASSIGNMENT_CONTENT, content.getReference(), true)); // close the edit object ((BaseAssignmentContentEdit) content).closeEdit(); } } /** * {@inheritDoc} */ public AssignmentSubmissionEdit addSubmission(String context, String assignmentId, String submitterId) throws PermissionException { M_log.debug(this + " ENTERING ADD SUBMISSION"); String submissionId = null; boolean badId = false; do { badId = !Validator.checkResourceId(submissionId); submissionId = IdManager.createUuid(); if (m_submissionStorage.check(submissionId)) badId = true; } while (badId); String key = submissionReference(context, submissionId, assignmentId); M_log.debug(this + " ADD SUBMISSION : SUB REF : " + key); Assignment assignment = null; try { assignment = getAssignment(assignmentId); } catch(IdUnusedException iue) { // A bit terminal, this. } // SAK-21525 if(!unlockCheckWithGroups(SECURE_ADD_ASSIGNMENT_SUBMISSION, key,assignment)) { throw new PermissionException(SessionManager.getCurrentSessionUserId(), SECURE_ADD_ASSIGNMENT_SUBMISSION, key); } M_log.debug(this + " ADD SUBMISSION : UNLOCKED"); // storage M_log.debug(this + " SUBMITTER ID " + submitterId); AssignmentSubmissionEdit submission = m_submissionStorage.put(submissionId, assignmentId, submitterId, null, null, null); if (submission != null) { submission.setContext(context); // event for tracking ((BaseAssignmentSubmissionEdit) submission).setEvent(AssignmentConstants.EVENT_ADD_ASSIGNMENT_SUBMISSION); M_log.debug(this + " LEAVING ADD SUBMISSION : REF : " + submission.getReference()); } else { M_log.warn(this + " ADD SUBMISSION: cannot add submission object with submission id=" + submissionId + ", assignment id=" + assignmentId + ", and submitter id=" + submitterId); } return submission; } /** * 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 AssignmentSubmissionEdit object (reserving the id). * @exception IdInvalidException * if the submission id is invalid. * @exception IdUsedException * if the submission id is already used. * @exception PermissionException * if the current user does not have permission to add a submission. */ public AssignmentSubmissionEdit mergeSubmission(Element el) throws IdInvalidException, IdUsedException, PermissionException { // construct from the XML BaseAssignmentSubmission submissionFromXml = new BaseAssignmentSubmission(el); // 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; } /** * Get a locked AssignmentSubmission object for editing. Must commitEdit() to make official, or cancelEdit() when done! * * @param submissionrReference - * the reference for the submission. * @return An AssignmentSubmissionEdit object for editing. * @exception IdUnusedException * if not found, or if not an AssignmentSubmissionEdit object * @exception PermissionException * if the current user does not have permission to edit this submission. * @exception InUseException * if the assignment is being edited by another user. */ public AssignmentSubmissionEdit editSubmission(String submissionReference) throws IdUnusedException, PermissionException, InUseException { String submissionId = submissionId(submissionReference); // ignore the cache - get the AssignmentSubmission with a lock from the info store AssignmentSubmissionEdit submission = m_submissionStorage.edit(submissionId); if (submission == null) throw new InUseException(submissionId); // pass if with grade or update assignment right if (!unlockCheck(SECURE_GRADE_ASSIGNMENT_SUBMISSION, submissionReference) && !unlockCheck(SECURE_UPDATE_ASSIGNMENT, submissionReference)) { boolean notAllowed = true; // normal user(not a grader) can only edit his/her own submission User currentUser = UserDirectoryService.getCurrentUser(); if (unlockCheck(SECURE_UPDATE_ASSIGNMENT_SUBMISSION, submissionReference)) { Assignment a = submission.getAssignment(); if (a.isGroup()) { String context = a.getContext(); Site st = SiteService.getSite(context); try { notAllowed = st.getGroup(submission.getSubmitterId()).getMember(currentUser.getId()) == null; } catch (Throwable _sss) { } } else { if ( submission.getSubmitterId() != null && submission.getSubmitterId().equals(currentUser.getId()) ) { // is editing one's own submission // then test against extra criteria depend on the status of submission try { if (canSubmit(a.getContext(), a)) { notAllowed = false; } } catch (Exception e) { M_log.warn(" editSubmission(): cannot get assignment for submission " + submissionReference + e.getMessage()); } } } } if (notAllowed) { // throw PermissionException throw new PermissionException(currentUser.getId(), SECURE_UPDATE_ASSIGNMENT, submissionReference); } } // check for existance if (!m_submissionStorage.check(submissionId)) { throw new IdUnusedException(submissionId); } ((BaseAssignmentSubmissionEdit) submission).setEvent(AssignmentConstants.EVENT_UPDATE_ASSIGNMENT_SUBMISSION); return submission; } // editSubmission /** * Commit the changes made to an AssignmentSubmissionEdit object, and release the lock. * * @param submission * The AssignmentSubmissionEdit object to commit. */ public void commitEdit(AssignmentSubmissionEdit submission) { String submissionRef = submission.getReference(); // check for closed edit if (!submission.isActiveEdit()) { try { throw new Exception(); } catch (Exception e) { M_log.warn(" commitEdit(): closed AssignmentSubmissionEdit assignment submission id=" + submission.getId() + e.getMessage()); } return; } // update the properties addLiveUpdateProperties(submission.getPropertiesEdit()); submission.setTimeLastModified(TimeService.newTime()); // complete the edit m_submissionStorage.commit(submission); // close the edit object ((BaseAssignmentSubmissionEdit) submission).closeEdit(); try { AssignmentSubmission s = getSubmission(submissionRef); Assignment a = s.getAssignment(); Time returnedTime = s.getTimeReturned(); Time submittedTime = s.getTimeSubmitted(); String resubmitNumber = s.getProperties().getProperty(AssignmentSubmission.ALLOW_RESUBMIT_NUMBER); // track it if (!s.getSubmitted()) { // saving a submission EventTrackingService.post(EventTrackingService.newEvent(AssignmentConstants.EVENT_SAVE_ASSIGNMENT_SUBMISSION, submissionRef, true)); } else if (returnedTime == null && !s.getReturned() && (submittedTime == null /*grading non-submissions*/ || (submittedTime != null && (s.getTimeLastModified().getTime() - submittedTime.getTime()) > 1000*60 /*make sure the last modified time is at least one minute after the submit time*/))) { if (StringUtils.trimToNull(s.getSubmittedText()) == null && s.getSubmittedAttachments().isEmpty() && StringUtils.trimToNull(s.getGrade()) == null && StringUtils.trimToNull(s.getFeedbackText()) == null && StringUtils.trimToNull(s.getFeedbackComment()) == null && s.getFeedbackAttachments().isEmpty() ) { // auto add submission for those not submitted //EventTrackingService.post(EventTrackingService.newEvent(AssignmentConstants.EVENT_ADD_ASSIGNMENT_SUBMISSION, submissionRef, true)); } else { // graded and saved before releasing it Event event = EventTrackingService.newEvent(AssignmentConstants.EVENT_GRADE_ASSIGNMENT_SUBMISSION, submissionRef, true); EventTrackingService.post(event); LearningResourceStoreService lrss = (LearningResourceStoreService) ComponentManager .get("org.sakaiproject.event.api.LearningResourceStoreService"); if (null != lrss && StringUtils.isNotEmpty(s.getGrade())) { for (User user : s.getSubmitters()) { lrss.registerStatement(getStatementForAssignmentGraded(lrss.getEventActor(event), event, a, s, user), "assignment"); } } } } else if (returnedTime != null && s.getGraded() && (submittedTime == null/*returning non-submissions*/ || (submittedTime != null && returnedTime.after(submittedTime))/*returning normal submissions*/ || (submittedTime != null && submittedTime.after(returnedTime) && s.getTimeLastModified().after(submittedTime))/*grading the resubmitted assignment*/)) { // releasing a submitted assignment or releasing grade to an unsubmitted assignment Event event = EventTrackingService.newEvent(AssignmentConstants.EVENT_GRADE_ASSIGNMENT_SUBMISSION, submissionRef, true); EventTrackingService.post(event); LearningResourceStoreService lrss = (LearningResourceStoreService) ComponentManager .get("org.sakaiproject.event.api.LearningResourceStoreService"); if (null != lrss && StringUtils.isNotEmpty(s.getGrade())) { for (User user : s.getSubmitters()) { lrss.registerStatement(getStatementForAssignmentGraded(lrss.getEventActor(event), event, a, s, user), "assignment"); } } // if this is releasing grade, depending on the release grade notification setting, send email notification to student sendGradeReleaseNotification(s.getGradeReleased(), a.getProperties().getProperty(Assignment.ASSIGNMENT_RELEASEGRADE_NOTIFICATION_VALUE), s.getSubmitters(), s); if(resubmitNumber!=null) sendGradeReleaseNotification(s.getGradeReleased(), a.getProperties().getProperty(Assignment.ASSIGNMENT_RELEASERESUBMISSION_NOTIFICATION_VALUE), s.getSubmitters(), s); } else if (submittedTime == null) /*grading non-submission*/ { // releasing a submitted assignment or releasing grade to an unsubmitted assignment Event event = EventTrackingService.newEvent(AssignmentConstants.EVENT_GRADE_ASSIGNMENT_SUBMISSION, submissionRef, true); EventTrackingService.post(event); LearningResourceStoreService lrss = (LearningResourceStoreService) ComponentManager .get("org.sakaiproject.event.api.LearningResourceStoreService"); if (null != lrss) { for (User user : s.getSubmitters()) { lrss.registerStatement(getStatementForUnsubmittedAssignmentGraded(lrss.getEventActor(event), event, a, s, user), "sakai.assignment"); } } } else { // submitting a submission EventTrackingService.post(EventTrackingService.newEvent(AssignmentConstants.EVENT_SUBMIT_ASSIGNMENT_SUBMISSION, submissionRef, true)); // only doing the notification for real online submissions if (a.getContent().getTypeOfSubmission() != Assignment.NON_ELECTRONIC_ASSIGNMENT_SUBMISSION) { // instructor notification notificationToInstructors(s, a); // student notification, whether the student gets email notification once he submits an assignment notificationToStudent(s); } } } catch (IdUnusedException e) { M_log.warn(" commitEdit(), submissionId=" + submissionRef, e); } catch (PermissionException e) { M_log.warn(" commitEdit(), submissionId=" + submissionRef, e); } } // commitEdit(Submission) protected void sendGradeReleaseNotification(boolean released, String notificationSetting, User[] allSubmitters, AssignmentSubmission s) { if (allSubmitters == null) return; // SAK-19916 need to filter submitters against list of valid users still in site Set<User> filteredSubmitters = new HashSet<User>(); try { String siteId = s.getAssignment().getContext(); Set<String> siteUsers = SiteService.getSite(siteId).getUsers(); for (int x = 0; x < allSubmitters.length; x++) { User u = (User) allSubmitters[x]; String userId = u.getId(); if (siteUsers.contains(userId)) { filteredSubmitters.add(u); } } } catch (IdUnusedException e) { // TODO Auto-generated catch block e.printStackTrace(); } User[] submitters = new User[filteredSubmitters.size()]; filteredSubmitters.toArray(submitters); if (released && notificationSetting != null && notificationSetting.equals(Assignment.ASSIGNMENT_RELEASEGRADE_NOTIFICATION_EACH)) { // send email to every submitters if (submitters != null) { // send the message immidiately EmailService.sendToUsers(new ArrayList(Arrays.asList(submitters)), getHeaders(null, "releasegrade"), getNotificationMessage(s, "releasegrade")); } } if (notificationSetting != null && notificationSetting.equals(Assignment.ASSIGNMENT_RELEASERESUBMISSION_NOTIFICATION_EACH)){ // send email to every submitters if (submitters != null){ // send the message immidiately EmailService.sendToUsers(new ArrayList(Arrays.asList(submitters)), getHeaders(null, "releaseresumbission"), getNotificationMessage(s, "releaseresumbission")); } } } /** * send notification to instructor type of users if necessary * @param s * @param a */ private void notificationToInstructors(AssignmentSubmission s, Assignment a) { String notiOption = a.getProperties().getProperty(Assignment.ASSIGNMENT_INSTRUCTOR_NOTIFICATIONS_VALUE); if (notiOption != null && !notiOption.equals(Assignment.ASSIGNMENT_INSTRUCTOR_NOTIFICATIONS_NONE)) { // need to send notification email String context = s.getContext(); // compare the list of users with the receive.notifications and list of users who can actually grade this assignment List receivers = allowReceiveSubmissionNotificationUsers(context); List allowGradeAssignmentUsers = allowGradeAssignmentUsers(a.getReference()); receivers.retainAll(allowGradeAssignmentUsers); String messageBody = getNotificationMessage(s, "submission"); if (notiOption.equals(Assignment.ASSIGNMENT_INSTRUCTOR_NOTIFICATIONS_EACH)) { // send the message immediately EmailService.sendToUsers(receivers, getHeaders(null, "submission"), messageBody); } else if (notiOption.equals(Assignment.ASSIGNMENT_INSTRUCTOR_NOTIFICATIONS_DIGEST)) { // just send plain/text version for now String digestMsgBody = getPlainTextNotificationMessage(s, "submission"); // digest the message to each user for (Iterator iReceivers = receivers.iterator(); iReceivers.hasNext();) { User user = (User) iReceivers.next(); DigestService.digest(user.getId(), getSubject("submission"), digestMsgBody); } } } } /** * get only the plain text of notification message * @param s * @return */ protected String getPlainTextNotificationMessage(AssignmentSubmission s, String submissionOrReleaseGrade) { StringBuilder message = new StringBuilder(); message.append(plainTextContent(s, submissionOrReleaseGrade)); return message.toString(); } /** * send notification to student/students if necessary * @param s */ private void notificationToStudent(AssignmentSubmission s) { if (m_serverConfigurationService.getBoolean("assignment.submission.confirmation.email", true)) { //send notification User[] users = s.getSubmitters(); List receivers = new ArrayList(); for (int i=0; users != null && i<users.length; i++){ if (StringUtils.trimToNull(users[i].getEmail()) != null){ receivers.add(users[i]); } } EmailService.sendToUsers(receivers, getHeaders(null, "submission"), getNotificationMessage(s, "submission")); } } protected List<String> getHeaders(String receiverEmail, String submissionOrReleaseGrade) { List<String> rv = new ArrayList<String>(); rv.add("MIME-Version: 1.0"); rv.add("Content-Type: multipart/alternative; boundary=\""+MULTIPART_BOUNDARY+"\""); // set the subject rv.add(getSubject(submissionOrReleaseGrade)); // from rv.add(getFrom()); // to if (StringUtils.trimToNull(receiverEmail) != null) { rv.add("To: " + receiverEmail); } return rv; } protected List<String> getReleaseGradeHeaders(String receiverEmail) { List<String> rv = new ArrayList<String>(); rv.add("MIME-Version: 1.0"); rv.add("Content-Type: multipart/alternative; boundary=\""+MULTIPART_BOUNDARY+"\""); // set the subject rv.add(getSubject("releasegrade")); // from rv.add(getFrom()); // to if (StringUtils.trimToNull(receiverEmail) != null) { rv.add("To: " + receiverEmail); } return rv; } protected String getSubject(String submissionOrReleaseGrade) { String subject = ""; if("submission".equals(submissionOrReleaseGrade)) subject = rb.getString("noti.subject.content"); else if ("releasegrade".equals(submissionOrReleaseGrade)) subject = rb.getString("noti.releasegrade.subject.content"); else subject = rb.getString("noti.releaseresubmission.subject.content"); return "Subject: " + subject ; } protected String getFrom() { return "From: " + "\"" + m_serverConfigurationService.getString("ui.service", "Sakai") + "\"<no-reply@"+ m_serverConfigurationService.getServerName() + ">"; } private final String MULTIPART_BOUNDARY = "======sakai-multi-part-boundary======"; private final String BOUNDARY_LINE = "\n\n--"+MULTIPART_BOUNDARY+"\n"; private final String TERMINATION_LINE = "\n\n--"+MULTIPART_BOUNDARY+"--\n\n"; private final String MIME_ADVISORY = "This message is for MIME-compliant mail readers."; /** * Get the message for the email. * * @param event * The event that matched criteria to cause the notification. * @return the message for the email. */ protected String getNotificationMessage(AssignmentSubmission s, String submissionOrReleaseGrade) { StringBuilder message = new StringBuilder(); message.append(MIME_ADVISORY); message.append(BOUNDARY_LINE); message.append(plainTextHeaders()); message.append(plainTextContent(s, submissionOrReleaseGrade)); message.append(BOUNDARY_LINE); message.append(htmlHeaders()); message.append(htmlPreamble(submissionOrReleaseGrade)); if("submission".equals(submissionOrReleaseGrade)) message.append(htmlContent(s)); else if ("releasegrade".equals(submissionOrReleaseGrade)) message.append(htmlContentReleaseGrade(s)); else message.append(htmlContentReleaseResubmission(s)); message.append(htmlEnd()); message.append(TERMINATION_LINE); return message.toString(); } protected String plainTextHeaders() { return "Content-Type: text/plain\n\n"; } protected String plainTextContent(AssignmentSubmission s, String submissionOrReleaseGrade) { if("submission".equals(submissionOrReleaseGrade)) return FormattedText.convertFormattedTextToPlaintext(htmlContent(s)); else if ("releasegrade".equals(submissionOrReleaseGrade)) return FormattedText.convertFormattedTextToPlaintext(htmlContentReleaseGrade(s)); else return FormattedText.convertFormattedTextToPlaintext(htmlContentReleaseResubmission(s)); } protected String htmlHeaders() { return "Content-Type: text/html\n\n"; } protected String htmlPreamble(String submissionOrReleaseGrade) { StringBuilder buf = new StringBuilder(); buf.append("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\n"); buf.append(" \"http://www.w3.org/TR/html4/loose.dtd\">\n"); buf.append("<html>\n"); buf.append(" <head><title>"); buf.append(getSubject(submissionOrReleaseGrade)); buf.append("</title></head>\n"); buf.append(" <body>\n"); return buf.toString(); } protected String htmlEnd() { return "\n </body>\n</html>\n"; } private String htmlContent(AssignmentSubmission s) { Assignment a = s.getAssignment(); String context = s.getContext(); String siteTitle = ""; String siteId = ""; try { Site site = SiteService.getSite(context); siteTitle = site.getTitle(); siteId = site.getId(); } catch (Exception ee) { M_log.warn(" htmlContent(), site id =" + context + " " + ee.getMessage()); } StringBuilder buffer = new StringBuilder(); // site title and id buffer.append(rb.getString("noti.site.title") + " " + siteTitle + newline); buffer.append(rb.getString("noti.site.id") + " " + siteId +newline + newline); // assignment title and due date buffer.append(rb.getString("assignment.title") + " " + a.getTitle()+newline); buffer.append(rb.getString("noti.assignment.duedate") + " " + a.getDueTime().toStringLocalFull()+newline + newline); // submitter name and id User[] submitters = s.getSubmitters(); String submitterNames = ""; String submitterIds = ""; for (int i = 0; i<submitters.length; i++) { User u = (User) submitters[i]; if (i>0) { submitterNames = submitterNames.concat("; "); submitterIds = submitterIds.concat("; "); } submitterNames = submitterNames.concat(u.getDisplayName()); submitterIds = submitterIds.concat(u.getDisplayId()); } buffer.append(rb.getString("noti.student") + " " + submitterNames); if (submitterIds.length() != 0) { buffer.append("( " + submitterIds + " )"); } buffer.append(newline + newline); // submit time buffer.append(rb.getString("submission.id") + " " + s.getId() + newline); // submit time buffer.append(rb.getString("noti.submit.time") + " " + s.getTimeSubmitted().toStringLocalFull() + newline + newline); // submit text String text = StringUtils.trimToNull(s.getSubmittedText()); if ( text != null) { buffer.append(rb.getString("gen.submittedtext") + newline + newline + Validator.escapeHtmlFormattedText(text) + newline + newline); } // attachment if any List attachments = s.getSubmittedAttachments(); if (attachments != null && attachments.size() >0) { buffer.append(rb.getString("gen.att") + newline + newline); for (int j = 0; j<attachments.size(); j++) { Reference r = (Reference) attachments.get(j); buffer.append(r.getProperties().getProperty(ResourceProperties.PROP_DISPLAY_NAME) + " (" + r.getProperties().getPropertyFormatted(ResourceProperties.PROP_CONTENT_LENGTH)+ ")\n"); //if this is a archive (zip etc) append the list of files in it if (isArchiveFile(r)) { buffer.append(getArchiveManifest(r)); } } } return buffer.toString(); } /** * get a list of the files in the archive * @param r * @return */ private Object getArchiveManifest(Reference r) { String extension = getFileExtension(r); StringBuilder builder = new StringBuilder(); if (".zip".equals(extension)) { ZipContentUtil zipUtil = new ZipContentUtil(); Map<String, Long> manifest = zipUtil.getZipManifest(r); Set<Entry<String, Long>> set = manifest.entrySet(); Iterator<Entry<String, Long>> it = set.iterator(); while (it.hasNext()) { Entry<String, Long> entry = it.next(); builder.append(entry.getKey() + " (" + formatFileSize(entry.getValue()) + ")" + newline); } } return builder.toString(); } private String formatFileSize(Long bytes) { long len = bytes; String[] byteString = { "KB", "KB", "MB", "GB" }; int count = 0; long newLen = 0; long lenBytesExtra = len; while (len > 1024) { newLen = len / 1024; lenBytesExtra = len - (newLen * 1024); len = newLen; count++; } if ((lenBytesExtra >= 512) || ((lenBytesExtra > 0) && (newLen == 0))) { newLen++; } return Long.toString(newLen) + " " + byteString[count]; } /** * is this an archive type for which we can get a manifest * @param r * @return */ private boolean isArchiveFile(Reference r) { String extension = getFileExtension(r); if (".zip".equals(extension)) { return true; } return false; } private String getFileExtension(Reference r) { ResourceProperties resourceProperties = r.getProperties(); String fileName = resourceProperties.getProperty(resourceProperties.getNamePropDisplayName()); if (fileName.indexOf(".")>0) { String extension = fileName.substring(fileName.lastIndexOf(".")); return extension; } return null; } private String htmlContentReleaseGrade(AssignmentSubmission s) { String newline = "<br />\n"; Assignment a = s.getAssignment(); String context = s.getContext(); String siteTitle = ""; String siteId = ""; try { Site site = SiteService.getSite(context); siteTitle = site.getTitle(); siteId = site.getId(); } catch (Exception ee) { M_log.warn(" htmlContentReleaseGrade(), site id =" + context + " " + ee.getMessage()); } StringBuilder buffer = new StringBuilder(); // site title and id buffer.append(rb.getString("noti.site.title") + " " + siteTitle + newline); buffer.append(rb.getString("noti.site.id") + " " + siteId +newline + newline); // notification text buffer.append(rb.getFormattedMessage("noti.releasegrade.text", new String[]{a.getTitle(), siteTitle})); return buffer.toString(); } private String htmlContentReleaseResubmission(AssignmentSubmission s){ String newline = "<br />\n"; Assignment a = s.getAssignment(); String context = s.getContext(); String siteTitle = ""; String siteId = ""; try { Site site = SiteService.getSite(context); siteTitle = site.getTitle(); siteId = site.getId(); }catch (Exception ee){ M_log.warn(this + " htmlContentReleaseResubmission(), site id =" + context + " " + ee.getMessage()); } StringBuilder buffer = new StringBuilder(); // site title and id buffer.append(rb.getString("noti.site.title") + " " + siteTitle + newline); buffer.append(rb.getString("noti.site.id") + " " + siteId +newline + newline); // notification text buffer.append(rb.getFormattedMessage("noti.releaseresubmission.text", new String[]{a.getTitle(), siteTitle})); return buffer.toString(); } /** * Cancel the changes made to a AssignmentSubmissionEdit object, and release the lock. * * @param submission * The AssignmentSubmissionEdit object to commit. */ public void cancelEdit(AssignmentSubmissionEdit submission) { // check for closed edit if (!submission.isActiveEdit()) { try { throw new Exception(); } catch (Exception e) { M_log.warn(" cancelEdit(): closed AssignmentSubmissionEdit assignment submission id=" + submission.getId() + " " + e.getMessage()); } return; } // release the edit lock m_submissionStorage.cancel(submission); // close the edit object ((BaseAssignmentSubmissionEdit) submission).closeEdit(); } // cancelEdit(Submission) /** * Removes an AssignmentSubmission and all references to it * * @param submission - * the AssignmentSubmission to remove. * @throws PermissionException * if current User does not have permission to do this. */ public void removeSubmission(AssignmentSubmissionEdit submission) throws PermissionException { if (submission != null) { if (!submission.isActiveEdit()) { try { throw new Exception(); } catch (Exception e) { M_log.warn(" removeSubmission(): closed AssignmentSubmissionEdit id=" + submission.getId() + " " + e.getMessage()); } return; } // check security unlock(SECURE_REMOVE_ASSIGNMENT_SUBMISSION, submission.getReference()); // complete the edit m_submissionStorage.remove(submission); // track event EventTrackingService.post(EventTrackingService.newEvent(AssignmentConstants.EVENT_REMOVE_ASSIGNMENT_SUBMISSION, submission.getReference(), true)); // close the edit object ((BaseAssignmentSubmissionEdit) submission).closeEdit(); // remove any realm defined for this resource try { AuthzGroupService.removeAuthzGroup(AuthzGroupService.getAuthzGroup(submission.getReference())); } catch (AuthzPermissionException e) { M_log.warn(" removeSubmission: removing realm for : " + submission.getReference() + " : " + e.getMessage()); } catch (GroupNotDefinedException e) { M_log.warn(" removeSubmission: cannot find group for submission " + submission.getReference() + " : " + e.getMessage()); } } }// removeSubmission /** *@inheritDoc */ public int getSubmissionsSize(String context) { int size = 0; List submissions = getSubmissions(context); if (submissions != null) { size = submissions.size(); } return size; } /** * Access all AssignmentSubmission objects - known to us (not from external providers). * * @return A list of AssignmentSubmission objects. */ protected List getSubmissions(String context) { List<AssignmentSubmission> submissions = new ArrayList<AssignmentSubmission>(); if ((m_caching) && (m_submissionCache != null) && (!m_submissionCache.disabled())) { // if the cache is complete, use it if (m_submissionCache.isComplete()) { submissions = m_submissionCache.getAll(); // TODO: filter by context } // otherwise get all the submissions from storage else { // Note: while we are getting from storage, storage might change. These can be processed // after we get the storage entries, and put them in the cache, and mark the cache complete. // -ggolden synchronized (m_submissionCache) { // if we were waiting and it's now complete... if (m_submissionCache.isComplete()) { submissions = m_submissionCache.getAll(); return submissions; } // save up any events to the cache until we get past this load m_submissionCache.holdEvents(); submissions = m_submissionStorage.getAll(context); // update the cache, and mark it complete for (int i = 0; i < submissions.size(); i++) { AssignmentSubmission submission = (AssignmentSubmission) submissions.get(i); m_submissionCache.put(submission.getReference(), submission); } m_submissionCache.setComplete(); // TODO: not really! just for context // now we are complete, process any cached events m_submissionCache.processEvents(); } } } else { // // if we have done this already in this thread, use that // submissions = (List) CurrentService.getInThread(context+".assignment.submissions"); // if (submissions == null) // { submissions = m_submissionStorage.getAll(context); // // // "cache" the submissions in the current service in case they are needed again in this thread... // if (submissions != null) // { // CurrentService.setInThread(context+".assignment.submissions", submissions); // } // } } //get all the review scores if (contentReviewService != null) { try { List<ContentReviewItem> reports = contentReviewService.getReportList(null, context); if (reports != null && reports.size() > 0) { updateSubmissionList(submissions, reports); } } catch (QueueException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (SubmissionException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ReportException e) { // TODO Auto-generated catch block e.printStackTrace(); } } return submissions; } // getAssignmentSubmissions private void updateSubmissionList(List<AssignmentSubmission> submissions, List<ContentReviewItem> reports) { //lets build a map to avoid multiple searches through the list of reports Map<String, ContentReviewItem> reportsMap = new HashMap<String, ContentReviewItem> (); for (int i = 0; i < reports.size(); i++) { ContentReviewItem item = reports.get(i); reportsMap.put(item.getUserId(), item); } for (int i = 0; i < submissions.size(); i++) { AssignmentSubmission sub = submissions.get(i); String submitterid = sub.getSubmitterId(); if (reportsMap.containsKey(submitterid)) { ContentReviewItem report = reportsMap.get(submitterid); AssignmentSubmissionEdit edit; try { edit = this.editSubmission(sub.getReference()); edit.setReviewScore(report.getReviewScore()); edit.setReviewIconUrl(report.getIconUrl()); edit.setSubmitterId(sub.getSubmitterId()); edit.setReviewError(report.getLastError()); this.commitEdit(edit); } catch (IdUnusedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (PermissionException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InUseException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } /** * Access list of all AssignmentContents created by the User. * * @param owner - * The User who's AssignmentContents are requested. * @return Iterator over all AssignmentContents owned by this User. */ public Iterator getAssignmentContents(User owner) { List retVal = new ArrayList(); AssignmentContent aContent = null; List allContents = getAssignmentContents(owner.getId()); for (int x = 0; x < allContents.size(); x++) { aContent = (AssignmentContent) allContents.get(x); if (aContent.getCreator().equals(owner.getId())) { retVal.add(aContent); } } if (retVal.isEmpty()) return new EmptyIterator(); else return retVal.iterator(); }// getAssignmentContents(User) /** * Access all the Assignments which have the specified AssignmentContent. * * @param content - * The particular AssignmentContent. * @return Iterator over all the Assignments with the specified AssignmentContent. */ public Iterator getAssignments(AssignmentContent content) { List retVal = new ArrayList(); String contentReference = null; String tempContentReference = null; if (content != null) { contentReference = content.getReference(); List allAssignments = getAssignments(content.getContext()); Assignment tempAssignment = null; for (int y = 0; y < allAssignments.size(); y++) { tempAssignment = (Assignment) allAssignments.get(y); tempContentReference = tempAssignment.getContentReference(); if (tempContentReference != null) { if (tempContentReference.equals(contentReference)) { retVal.add(tempAssignment); } } } } if (retVal.isEmpty()) return new EmptyIterator(); else return retVal.iterator(); } /** * Access all the Assignemnts associated with the context * * @param context - * Describes the portlet context - generated with DefaultId.getChannel(). * @return Iterator over all the Assignments associated with the context and the user. */ public Iterator getAssignmentsForContext(String context) { M_log.debug(this + " GET ASSIGNMENTS FOR CONTEXT : CONTEXT : " + context); return assignmentsForContextAndUser(context, null); } /** * Access all the Assignemnts associated with the context and the user * * @param context - * Describes the portlet context - generated with DefaultId.getChannel() * @return Iterator over all the Assignments associated with the context and the user */ public Iterator getAssignmentsForContext(String context, String userId) { M_log.debug(this + " GET ASSIGNMENTS FOR CONTEXT : CONTEXT : " + context); return assignmentsForContextAndUser(context, userId); } /** * @inheritDoc */ public Map<Assignment, List<String>> getSubmittableAssignmentsForContext(String context) { Map<Assignment, List<String>> submittable = new HashMap<Assignment, List<String>>(); if (!allowGetAssignment(context)) { // no permission to read assignment in context return submittable; } Site site = null; try { site = SiteService.getSite(context); } catch (IdUnusedException e) { if (M_log.isDebugEnabled()) { M_log.debug("Could not retrieve submittable assignments for nonexistent site: " + context); } } if (site == null) { return submittable; } Set<String> siteSubmitterIds = AuthzGroupService.getUsersIsAllowed( SECURE_ADD_ASSIGNMENT_SUBMISSION, Arrays.asList(site.getReference())); Map<String, Set<String>> groupIdUserIds = new HashMap<String, Set<String>>(); for (Group group : site.getGroups()) { String groupRef = group.getReference(); for (Member member : group.getMembers()) { if (member.getRole().isAllowed(SECURE_ADD_ASSIGNMENT_SUBMISSION)) { if (!groupIdUserIds.containsKey(groupRef)) { groupIdUserIds.put(groupRef, new HashSet<String>()); } groupIdUserIds.get(groupRef).add(member.getUserId()); } } } List<Assignment> assignments = (List<Assignment>) getAssignments(context); for (Assignment assignment : assignments) { Set<String> userIds = new HashSet<String>(); if (assignment.getAccess() == Assignment.AssignmentAccess.GROUPED) { for (String groupRef : (Collection<String>) assignment.getGroups()) { if (groupIdUserIds.containsKey(groupRef)) { userIds.addAll(groupIdUserIds.get(groupRef)); } } } else { userIds.addAll(siteSubmitterIds); } submittable.put(assignment, new ArrayList(userIds)); } return submittable; } /** * get proper assignments for specified context and user * @param context * @param user * @return */ private Iterator assignmentsForContextAndUser(String context, String userId) { Assignment tempAssignment = null; List retVal = new ArrayList(); List allAssignments = null; if (context != null) { allAssignments = getAssignments(context, userId); for (int x = 0; x < allAssignments.size(); x++) { tempAssignment = (Assignment) allAssignments.get(x); if ((context.equals(tempAssignment.getContext())) || (context.equals(getGroupNameFromContext(tempAssignment.getContext())))) { retVal.add(tempAssignment); } } } if (retVal.isEmpty()) return new EmptyIterator(); else return retVal.iterator(); } /** * @inheritDoc */ public List getListAssignmentsForContext(String context) { M_log.debug(this + " getListAssignmetsForContext : CONTEXT : " + context); Assignment tempAssignment = null; List retVal = new ArrayList(); if (context != null) { List allAssignments = getAssignments(context); for (int x = 0; x < allAssignments.size(); x++) { tempAssignment = (Assignment) allAssignments.get(x); if ((context.equals(tempAssignment.getContext())) || (context.equals(getGroupNameFromContext(tempAssignment.getContext())))) { String deleted = tempAssignment.getProperties().getProperty(ResourceProperties.PROP_ASSIGNMENT_DELETED); if (deleted == null || "".equals(deleted)) { // not deleted, show it if (tempAssignment.getDraft()) { // who can see the draft assigment if (isDraftAssignmentVisible(tempAssignment, context)) { retVal.add(tempAssignment); } } else { retVal.add(tempAssignment); } } } } } return retVal; } /** * who can see the draft assignment * @param assignment * @param context * @return */ private boolean isDraftAssignmentVisible(Assignment assignment, String context) { return SecurityService.isSuperUser() // super user can always see it || assignment.getCreator().equals(UserDirectoryService.getCurrentUser().getId()) // the creator can see it || (unlockCheck(SECURE_SHARE_DRAFTS, SiteService.siteReference(context))); // any role user with share draft permission } /** * Access a User's AssignmentSubmission to a particular Assignment. * * @param assignmentReference * The reference of the assignment. * @param person - * The User who's Submission you would like. * @return AssignmentSubmission The user's submission for that Assignment. * @throws IdUnusedException * if there is no object with this id. * @throws PermissionException * if the current user is not allowed to access this. */ public AssignmentSubmission getSubmission(String assignmentReference, User person) { AssignmentSubmission submission = null; String assignmentId = assignmentId(assignmentReference); if ((assignmentReference != null) && (person != null)) { try { Assignment a = getAssignment(assignmentReference); if (a.isGroup()) { Site _site = SiteService.getSite( a.getContext() ); Collection groups = _site.getGroupsWithMember(person.getId()); if (groups != null) { Iterator<Group> itgroup = groups.iterator(); while (submission == null && itgroup.hasNext()) { Group _g = itgroup.next(); submission = getSubmission(assignmentReference, _g.getId()); } } } else { M_log.debug(" BaseAssignmentContent : Getting submission "); submission = m_submissionStorage.get(assignmentId, person.getId()); } } catch (IdUnusedException iue) { } catch (PermissionException pme) { } } if (submission != null) { try { unlock2(SECURE_ACCESS_ASSIGNMENT_SUBMISSION, SECURE_ACCESS_ASSIGNMENT, submission.getReference()); } catch (PermissionException e) { return null; } } return submission; } /** * * Access a Group or User's AssignmentSubmission to a particular Assignment. * * @param assignmentReference * The reference of the assignment. * @param submitter - * The User or Group who's Submission you would like. * @return AssignmentSubmission The user's submission for that Assignment. * @throws IdUnusedException * if there is no object with this id. * @throws PermissionException * if the current user is not allowed to access this. */ public AssignmentSubmission getSubmission(String assignmentReference, String submitter) { AssignmentSubmission submission = null; String assignmentId = assignmentId(assignmentReference); if ((assignmentReference != null) && (submitter != null)) { submission = m_submissionStorage.get(assignmentId, submitter); } if (submission != null) { try { unlock2(SECURE_ACCESS_ASSIGNMENT_SUBMISSION, SECURE_ACCESS_ASSIGNMENT, submission.getReference()); } catch (PermissionException e) { return null; } } return submission; } /** * @inheritDoc */ public AssignmentSubmission getSubmission(List submissions, User person) { AssignmentSubmission retVal = null; for (int z = 0; z < submissions.size(); z++) { AssignmentSubmission sub = (AssignmentSubmission) submissions.get(z); if (sub != null) { List submitters = sub.getSubmitterIds(); for (int a = 0; a < submitters.size(); a++) { String aUserId = (String) submitters.get(a); M_log.debug(this + " getSubmission(List, User) comparing aUser id : " + aUserId + " and chosen user id : " + person.getId()); if (aUserId.equals(person.getId())) { M_log.debug(this + " getSubmission(List, User) found a match : return value is " + sub.getId()); retVal = sub; } } } } return retVal; } /** * Get the submissions for an assignment. * * @param assignment - * the Assignment who's submissions you would like. * @return Iterator over all the submissions for an Assignment. */ public List getSubmissions(Assignment assignment) { List retVal = new ArrayList(); if (assignment != null) { retVal = getSubmissions(assignment.getId()); } return retVal; } /** * {@inheritDoc} */ public int getSubmittedSubmissionsCount(String assignmentRef) { return m_submissionStorage.getSubmittedSubmissionsCount(assignmentRef); } /** * {@inheritDoc} */ public int getUngradedSubmissionsCount(String assignmentRef) { return m_submissionStorage.getUngradedSubmissionsCount(assignmentRef); } /** * Access the AssignmentSubmission with the specified id. * * @param submissionReference - * The reference of the AssignmentSubmission. * @return The AssignmentSubmission corresponding to the id, or null if it does not exist. * @throws IdUnusedException * if there is no object with this id. * @throws PermissionException * if the current user is not allowed to access this. */ public AssignmentSubmission getSubmission(String submissionReference) throws IdUnusedException, PermissionException { M_log.debug(this + " GET SUBMISSION : REF : " + submissionReference); // check permission unlock2(SECURE_ACCESS_ASSIGNMENT_SUBMISSION, SECURE_ACCESS_ASSIGNMENT, submissionReference); AssignmentSubmission submission = null; String submissionId = submissionId(submissionReference); if ((m_caching) && (m_submissionCache != null) && (!m_submissionCache.disabled())) { // if we have it in the cache, use it if (m_submissionCache.containsKey(submissionReference)) submission = (AssignmentSubmission) m_submissionCache.get(submissionReference); if ( submission == null ) //SAK-12447 cache.get can return null on expired { submission = m_submissionStorage.get(submissionId); // cache the result m_submissionCache.put(submissionReference, submission); } } else { // // if we have done this already in this thread, use that // submission = (AssignmentSubmission) CurrentService.getInThread(submissionId+".assignment.submission"); // if (submission == null) // { submission = m_submissionStorage.get(submissionId); // // // "cache" the submission in the current service in case they are needed again in this thread... // if (submission != null) // { // CurrentService.setInThread(submissionId+".assignment.submission", submission); // } // } } if (submission == null) throw new IdUnusedException(submissionId); // double check the submission submitter information: // if current user is not the original submitter and if he doesn't have grading permission, he should not have access to other people's submission. String assignmentRef = assignmentReference(submission.getContext(), submission.getAssignmentId()); if (!allowGradeSubmission(assignmentRef)) { List submitterIds = submission.getSubmitterIds(); if (submitterIds != null && !submitterIds.contains(SessionManager.getCurrentSessionUserId())) { throw new PermissionException(SessionManager.getCurrentSessionUserId(), SECURE_ACCESS_ASSIGNMENT_SUBMISSION, submissionId); } } // track event // EventTrackingService.post(EventTrackingService.newEvent(AssignmentConstants.EVENT_ACCESS_ASSIGNMENT_SUBMISSION, submission.getReference(), false)); return submission; }// getAssignmentSubmission /** * Return the reference root for use in resource references and urls. * * @return The reference root for use in resource references and urls. */ protected String getReferenceRoot() { return REFERENCE_ROOT; } /** * Update the live properties for an object when modified. */ protected void addLiveUpdateProperties(ResourcePropertiesEdit props) { props.addProperty(ResourceProperties.PROP_MODIFIED_BY, SessionManager.getCurrentSessionUserId()); props.addProperty(ResourceProperties.PROP_MODIFIED_DATE, TimeService.newTime().toString()); } // addLiveUpdateProperties /** * Create the live properties for the object. */ protected void addLiveProperties(ResourcePropertiesEdit props) { String current = SessionManager.getCurrentSessionUserId(); props.addProperty(ResourceProperties.PROP_CREATOR, current); props.addProperty(ResourceProperties.PROP_MODIFIED_BY, current); String now = TimeService.newTime().toString(); props.addProperty(ResourceProperties.PROP_CREATION_DATE, now); props.addProperty(ResourceProperties.PROP_MODIFIED_DATE, now); } // addLiveProperties /** * check permissions for addAssignment(). * * @param context - * Describes the portlet context - generated with DefaultId.getChannel() * @return true if the user is allowed to addAssignment(...), false if not. */ public boolean allowAddGroupAssignment(String context) { // base the check for SECURE_ADD on the site, any of the site's groups, and the channel // if the user can SECURE_ADD anywhere in that mix, they can add an assignment // this stack is not the normal azg set for channels, so use a special refernce to get this behavior String resourceString = getAccessPoint(true) + Entity.SEPARATOR + REF_TYPE_ASSIGNMENT_GROUPS + Entity.SEPARATOR + "a" + Entity.SEPARATOR + context + Entity.SEPARATOR; { M_log.debug(this + " allowAddGroupAssignment with resource string : " + resourceString); M_log.debug(" context string : " + context); } // check security on the channel (throws if not permitted) return unlockCheck(SECURE_ADD_ASSIGNMENT, resourceString); } // allowAddGroupAssignment /** * @inheritDoc */ public boolean allowReceiveSubmissionNotification(String context) { String resourceString = getContextReference(context); { M_log.debug(this + " allowReceiveSubmissionNotification with resource string : " + resourceString); } // checking allow at the site level if (unlockCheck(SECURE_ASSIGNMENT_RECEIVE_NOTIFICATIONS, resourceString)) return true; return false; } /** * @inheritDoc */ public List allowReceiveSubmissionNotificationUsers(String context) { String resourceString = getContextReference(context); { M_log.debug(this + " allowReceiveSubmissionNotificationUsers with resource string : " + resourceString); M_log.debug(" context string : " + context); } return SecurityService.unlockUsers(SECURE_ASSIGNMENT_RECEIVE_NOTIFICATIONS, resourceString); } // allowAddAssignmentUsers /** * @inheritDoc */ public boolean allowAddAssignment(String context) { String resourceString = getContextReference(context); // base the check for SECURE_ADD_ASSIGNMENT on the site and any of the site's groups // if the user can SECURE_ADD_ASSIGNMENT anywhere in that mix, they can add an assignment // this stack is not the normal azg set for site, so use a special refernce to get this behavior { M_log.debug(this + " allowAddAssignment with resource string : " + resourceString); } // checking allow at the site level if (unlockCheck(SECURE_ADD_ASSIGNMENT, resourceString)) return true; // if not, see if the user has any groups to which adds are allowed return (!getGroupsAllowAddAssignment(context).isEmpty()); } /** * @inheritDoc */ public boolean allowAddSiteAssignment(String context) { // check for assignments that will be site-wide: String resourceString = getContextReference(context); { M_log.debug(this + " allowAddSiteAssignment with resource string : " + resourceString); } // check security on the channel (throws if not permitted) return unlockCheck(SECURE_ADD_ASSIGNMENT, resourceString); } /** * @inheritDoc */ public boolean allowAllGroups(String context) { String resourceString = getContextReference(context); { M_log.debug(this + " allowAllGroups with resource string : " + resourceString); } // checking all.groups if (unlockCheck(SECURE_ALL_GROUPS, resourceString)) return true; // if not return false; } /** * @inheritDoc */ public Collection getGroupsAllowAddAssignment(String context) { return getGroupsAllowFunction(SECURE_ADD_ASSIGNMENT, context, null); } /** * @inheritDoc */ public Collection getGroupsAllowGradeAssignment(String context, String assignmentReference) { Collection rv = new ArrayList(); if (allowGradeSubmission(assignmentReference)) { // only if the user is allowed to group at all Collection allAllowedGroups = getGroupsAllowFunction(SECURE_GRADE_ASSIGNMENT_SUBMISSION, context, null); try { Assignment a = getAssignment(assignmentReference); if (a.getAccess() == Assignment.AssignmentAccess.SITE) { // for site-scope assignment, return all groups rv = allAllowedGroups; } else { Collection aGroups = a.getGroups(); // for grouped assignment, return only those also allowed for grading for (Iterator i = allAllowedGroups.iterator(); i.hasNext();) { Group g = (Group) i.next(); if (aGroups.contains(g.getReference())) { rv.add(g); } } } } catch (Exception e) { M_log.info(this + " getGroupsAllowGradeAssignment " + e.getMessage() + assignmentReference); } } return rv; } /** * @inherit */ public boolean allowGetAssignment(String context) { String resourceString = getContextReference(context); { M_log.debug(this + " allowGetAssignment with resource string : " + resourceString); } return unlockCheck(SECURE_ACCESS_ASSIGNMENT, resourceString); } /** * @inheritDoc */ public Collection getGroupsAllowGetAssignment(String context) { return getGroupsAllowFunction(SECURE_ACCESS_ASSIGNMENT, context, null); } // for specified user private Collection getGroupsAllowGetAssignment(String context, String userId) { return getGroupsAllowFunction(SECURE_ACCESS_ASSIGNMENT, context, userId); } /** * Check permissions for updateing an Assignment. * * @param assignmentReference - * The Assignment's reference. * @return True if the current User is allowed to update the Assignment, false if not. */ public boolean allowUpdateAssignment(String assignmentReference) { M_log.debug(this + " allowUpdateAssignment with resource string : " + assignmentReference); return unlockCheck(SECURE_UPDATE_ASSIGNMENT, assignmentReference); } /** * Check permissions for removing an Assignment. * * @return True if the current User is allowed to remove the Assignment, false if not. */ public boolean allowRemoveAssignment(String assignmentReference) { M_log.debug(this + " allowRemoveAssignment " + assignmentReference); // check security (throws if not permitted) return unlockCheck(SECURE_REMOVE_ASSIGNMENT, assignmentReference); } /** * @inheritDoc */ public Collection getGroupsAllowRemoveAssignment(String context) { return getGroupsAllowFunction(SECURE_REMOVE_ASSIGNMENT, context, null); } /** * Get the groups of this channel's contex-site that the end user has permission to "function" in. * * @param function * The function to check */ protected Collection getGroupsAllowFunction(String function, String context, String userId) { Collection rv = new ArrayList(); try { // get the site groups Site site = SiteService.getSite(context); Collection groups = site.getGroups(); if (SecurityService.isSuperUser()) { // for super user, return all groups return groups; } else if (userId == null) { // for current session user userId = SessionManager.getCurrentSessionUserId(); } // if the user has SECURE_ALL_GROUPS in the context (site), select all site groups if (SecurityService.unlock(userId, SECURE_ALL_GROUPS, SiteService.siteReference(context)) && unlockCheck(function, SiteService.siteReference(context))) { return groups; } // otherwise, check the groups for function // get a list of the group refs, which are authzGroup ids Collection groupRefs = new ArrayList(); for (Iterator i = groups.iterator(); i.hasNext();) { Group group = (Group) i.next(); groupRefs.add(group.getReference()); } // ask the authzGroup service to filter them down based on function groupRefs = AuthzGroupService.getAuthzGroupsIsAllowed(userId, function, groupRefs); // pick the Group objects from the site's groups to return, those that are in the groupRefs list for (Iterator i = groups.iterator(); i.hasNext();) { Group group = (Group) i.next(); if (groupRefs.contains(group.getReference())) { rv.add(group); } } } catch (IdUnusedException e) { M_log.debug(this + " getGroupsAllowFunction idunused :" + context + " : " + e.getMessage()); } return rv; } /** ***********************************************check permissions for AssignmentContent object ******************************************* */ /** * Check permissions for get AssignmentContent * * @param contentReference - * The AssignmentContent reference. * @return True if the current User is allowed to access the AssignmentContent, false if not. */ public boolean allowGetAssignmentContent(String context) { String resourceString = getAccessPoint(true) + Entity.SEPARATOR + "c" + Entity.SEPARATOR + context + Entity.SEPARATOR; { M_log.debug(this + " allowGetAssignmentContent with resource string : " + resourceString); } // check security (throws if not permitted) return unlockCheck(SECURE_ACCESS_ASSIGNMENT_CONTENT, resourceString); } /** * Check permissions for updating AssignmentContent * * @param contentReference - * The AssignmentContent reference. * @return True if the current User is allowed to update the AssignmentContent, false if not. */ public boolean allowUpdateAssignmentContent(String contentReference) { M_log.debug(this + " allowUpdateAssignmentContent with resource string : " + contentReference); // check security (throws if not permitted) return unlockCheck(SECURE_UPDATE_ASSIGNMENT_CONTENT, contentReference); } /** * Check permissions for adding an AssignmentContent. * * @param context - * Describes the portlet context - generated with DefaultId.getChannel(). * @return True if the current User is allowed to add an AssignmentContent, false if not. */ public boolean allowAddAssignmentContent(String context) { String resourceString = getAccessPoint(true) + Entity.SEPARATOR + "c" + Entity.SEPARATOR + context + Entity.SEPARATOR; M_log.debug(this + "allowAddAssignmentContent with resource string : " + resourceString); // check security (throws if not permitted) if (unlockCheck(SECURE_ADD_ASSIGNMENT_CONTENT, resourceString)) return true; // if not, see if the user has any groups to which adds are allowed return (!getGroupsAllowAddAssignment(context).isEmpty()); } /** * Check permissions for remove the AssignmentContent * * @param contentReference - * The AssignmentContent reference. * @return True if the current User is allowed to remove the AssignmentContent, false if not. */ public boolean allowRemoveAssignmentContent(String contentReference) { M_log.debug(this + " allowRemoveAssignmentContent with referece string : " + contentReference); // check security (throws if not permitted) return unlockCheck(SECURE_REMOVE_ASSIGNMENT_CONTENT, contentReference); } /** * Check permissions for add AssignmentSubmission * * @param context - * Describes the portlet context - generated with DefaultId.getChannel(). * @return True if the current User is allowed to add an AssignmentSubmission, false if not. */ public boolean allowAddSubmission(String context) { // check security (throws if not permitted) String resourceString = getAccessPoint(true) + Entity.SEPARATOR + "s" + Entity.SEPARATOR + context + Entity.SEPARATOR; M_log.debug(this + " allowAddSubmission with resource string : " + resourceString); return unlockCheck(SECURE_ADD_ASSIGNMENT_SUBMISSION, resourceString); } /** * SAK-21525 * * @param context * @param assignment - An Assignment object. Needed for the groups to be checked. * @return */ public boolean allowAddSubmissionCheckGroups(String context, Assignment assignment) { // check security (throws if not permitted) String resourceString = getAccessPoint(true) + Entity.SEPARATOR + "s" + Entity.SEPARATOR + context + Entity.SEPARATOR; M_log.debug(this + " allowAddSubmission with resource string : " + resourceString); return unlockCheckWithGroups(SECURE_ADD_ASSIGNMENT_SUBMISSION, resourceString, assignment); } /** * Get the List of Users who can addSubmission() for this assignment. * * @param assignmentReference - * a reference to an assignment * @return the List (User) of users who can addSubmission() for this assignment. */ public List allowAddSubmissionUsers(String assignmentReference) { return SecurityService.unlockUsers(SECURE_ADD_ASSIGNMENT_SUBMISSION, assignmentReference); } // allowAddSubmissionUsers /** * Get the List of Users who can grade submission for this assignment. * * @param assignmentReference - * a reference to an assignment * @return the List (User) of users who can grade submission for this assignment. */ public List allowGradeAssignmentUsers(String assignmentReference) { List users = SecurityService.unlockUsers(SECURE_GRADE_ASSIGNMENT_SUBMISSION, assignmentReference); if (users == null) { users = new ArrayList(); } try { Assignment a = getAssignment(assignmentReference); if (a.getAccess() == Assignment.AssignmentAccess.GROUPED) { // for grouped assignment, need to include those users that with "all.groups" and "grade assignment" permissions on the site level AuthzGroup group = AuthzGroupService.getAuthzGroup(SiteService.siteReference(a.getContext())); if (group != null) { // get the roles which are allowed for submission but not for all_site control Set rolesAllowAllSite = group.getRolesIsAllowed(SECURE_ALL_GROUPS); Set rolesAllowGradeAssignment = group.getRolesIsAllowed(SECURE_GRADE_ASSIGNMENT_SUBMISSION); // save all the roles with both "all.groups" and "grade assignment" permissions if (rolesAllowAllSite != null) rolesAllowAllSite.retainAll(rolesAllowGradeAssignment); if (rolesAllowAllSite != null && rolesAllowAllSite.size() > 0) { for (Iterator iRoles = rolesAllowAllSite.iterator(); iRoles.hasNext(); ) { Set<String> userIds = group.getUsersHasRole((String) iRoles.next()); if (userIds != null) { for (Iterator<String> iUserIds = userIds.iterator(); iUserIds.hasNext(); ) { String userId = iUserIds.next(); try { User u = UserDirectoryService.getUser(userId); if (!users.contains(u)) { users.add(u); } } catch (Exception ee) { M_log.warn(" allowGradeAssignmentUsers " + ee.getMessage() + " problem with getting user =" + userId); } } } } } } } } catch (Exception e) { M_log.warn(" allowGradeAssignmentUsers " + e.getMessage() + " assignmentReference=" + assignmentReference); } return users; } // allowGradeAssignmentUsers /** * @inheritDoc * @param context * @return */ public List allowAddAnySubmissionUsers(String context) { List<String> rv = new Vector(); try { AuthzGroup group = AuthzGroupService.getAuthzGroup(SiteService.siteReference(context)); // get the roles which are allowed for submission but not for all_site control Set rolesAllowSubmission = group.getRolesIsAllowed(SECURE_ADD_ASSIGNMENT_SUBMISSION); Set rolesAllowAllSite = group.getRolesIsAllowed(SECURE_ALL_GROUPS); rolesAllowSubmission.removeAll(rolesAllowAllSite); for (Iterator iRoles = rolesAllowSubmission.iterator(); iRoles.hasNext(); ) { rv.addAll(group.getUsersHasRole((String) iRoles.next())); } } catch (Exception e) { M_log.warn(" allowAddAnySubmissionUsers " + e.getMessage() + " context=" + context); } return rv; } /** * Get the List of Users who can add assignment * * @param assignmentReference - * a reference to an assignment * @return the List (User) of users who can addSubmission() for this assignment. */ public List allowAddAssignmentUsers(String context) { String resourceString = getContextReference(context); { M_log.debug(this + " allowAddAssignmentUsers with resource string : " + resourceString); M_log.debug(" context string : " + context); } return SecurityService.unlockUsers(SECURE_ADD_ASSIGNMENT, resourceString); } // allowAddAssignmentUsers /** * Check permissions for accessing a Submission. * * @param submissionReference - * The Submission's reference. * @return True if the current User is allowed to get the AssignmentSubmission, false if not. */ public boolean allowGetSubmission(String submissionReference) { M_log.debug(this + " allowGetSubmission with resource string : " + submissionReference); return unlockCheck2(SECURE_ACCESS_ASSIGNMENT_SUBMISSION, SECURE_ACCESS_ASSIGNMENT, submissionReference); } /** * Check permissions for updating Submission. * * @param submissionReference - * The Submission's reference. * @return True if the current User is allowed to update the AssignmentSubmission, false if not. */ public boolean allowUpdateSubmission(String submissionReference) { M_log.debug(this + " allowUpdateSubmission with resource string : " + submissionReference); return unlockCheck2(SECURE_UPDATE_ASSIGNMENT_SUBMISSION, SECURE_UPDATE_ASSIGNMENT, submissionReference); } /** * Check permissions for remove Submission * * @param submissionReference - * The Submission's reference. * @return True if the current User is allowed to remove the AssignmentSubmission, false if not. */ public boolean allowRemoveSubmission(String submissionReference) { M_log.debug(this + " allowRemoveSubmission with resource string : " + submissionReference); // check security (throws if not permitted) return unlockCheck(SECURE_REMOVE_ASSIGNMENT_SUBMISSION, submissionReference); } public boolean allowGradeSubmission(String assignmentReference) { { M_log.debug(this + " allowGradeSubmission with resource string : " + assignmentReference); } return unlockCheck(SECURE_GRADE_ASSIGNMENT_SUBMISSION, assignmentReference); } /** * Access the grades spreadsheet for the reference, either for an assignment or all assignments in a context. * * @param ref * The reference, either to a specific assignment, or just to an assignment context. * @return The grades spreadsheet bytes. * @throws IdUnusedException * if there is no object with this id. * @throws PermissionException * if the current user is not allowed to access this. */ public byte[] getGradesSpreadsheet(String ref) throws IdUnusedException, PermissionException { String typeGradesString = REF_TYPE_GRADES + Entity.SEPARATOR; String context = ref.substring(ref.indexOf(typeGradesString) + typeGradesString.length()); // get site title for display purpose String siteTitle = ""; try { Site s = SiteService.getSite(context); siteTitle = s.getTitle(); } catch (Exception e) { // ignore exception M_log.debug(this + ":getGradesSpreadsheet cannot get site context=" + context + e.getMessage()); } // does current user allowed to grade any assignment? boolean allowGradeAny = false; List assignmentsList = getListAssignmentsForContext(context); for (int iAssignment = 0; !allowGradeAny && iAssignment<assignmentsList.size(); iAssignment++) { if (allowGradeSubmission(((Assignment) assignmentsList.get(iAssignment)).getReference())) { allowGradeAny = true; } } if (!allowGradeAny) { // not permitted to download the spreadsheet return null; } else { short rowNum = 0; HSSFWorkbook wb = new HSSFWorkbook(); HSSFSheet sheet = wb.createSheet(WorkbookUtil.createSafeSheetName(siteTitle)); // Create a row and put some cells in it. Rows are 0 based. HSSFRow row = sheet.createRow(rowNum++); row.createCell((short) 0).setCellValue(rb.getString("download.spreadsheet.title")); // empty line row = sheet.createRow(rowNum++); row.createCell((short) 0).setCellValue(""); // site title row = sheet.createRow(rowNum++); row.createCell((short) 0).setCellValue(rb.getString("download.spreadsheet.site") + siteTitle); // download time row = sheet.createRow(rowNum++); row.createCell((short) 0).setCellValue( rb.getString("download.spreadsheet.date") + TimeService.newTime().toStringLocalFull()); // empty line row = sheet.createRow(rowNum++); row.createCell((short) 0).setCellValue(""); HSSFCellStyle style = wb.createCellStyle(); // this is the header row number short headerRowNumber = rowNum; // set up the header cells row = sheet.createRow(rowNum++); short cellNum = 0; // user enterprise id column HSSFCell cell = row.createCell(cellNum++); cell.setCellStyle(style); cell.setCellValue(rb.getString("download.spreadsheet.column.name")); // user name column cell = row.createCell(cellNum++); cell.setCellStyle(style); cell.setCellValue(rb.getString("download.spreadsheet.column.userid")); // starting from this row, going to input user data Iterator assignments = new SortedIterator(assignmentsList.iterator(), new AssignmentComparator("duedate", "true")); // site members excluding those who can add assignments List members = new ArrayList(); // hashmap which stores the Excel row number for particular user HashMap user_row = new HashMap(); List allowAddAnySubmissionUsers = allowAddAnySubmissionUsers(context); for (Iterator iUserIds = new SortedIterator(allowAddAnySubmissionUsers.iterator(), new AssignmentComparator("sortname", "true")); iUserIds.hasNext();) { String userId = (String) iUserIds.next(); try { User u = UserDirectoryService.getUser(userId); members.add(u); // create the column for user first row = sheet.createRow(rowNum); // update user_row Hashtable user_row.put(u.getId(), Integer.valueOf(rowNum)); // increase row rowNum++; // put user displayid and sortname in the first two cells cellNum = 0; row.createCell(cellNum++).setCellValue(u.getSortName()); row.createCell(cellNum).setCellValue(u.getDisplayId()); } catch (Exception e) { M_log.warn(" getGradesSpreadSheet " + e.getMessage() + " userId = " + userId); } } int index = 0; // the grade data portion starts from the third column, since the first two are used for user's display id and sort name while (assignments.hasNext()) { Assignment a = (Assignment) assignments.next(); int assignmentType = a.getContent().getTypeOfGrade(); // for column header, check allow grade permission based on each assignment if(!a.getDraft() && allowGradeSubmission(a.getReference())) { // put in assignment title as the column header rowNum = headerRowNumber; row = sheet.getRow(rowNum++); cellNum = (short) (index + 2); cell = row.createCell(cellNum); // since the first two column is taken by student id and name cell.setCellStyle(style); cell.setCellValue(a.getTitle()); for (int loopNum = 0; loopNum < members.size(); loopNum++) { // prepopulate the column with the "no submission" string row = sheet.getRow(rowNum++); cell = row.createCell(cellNum); cell.setCellType(1); cell.setCellValue(rb.getString("listsub.nosub")); } // begin to populate the column for this assignment, iterating through student list for (Iterator sIterator=getSubmissions(a).iterator(); sIterator.hasNext();) { AssignmentSubmission submission = (AssignmentSubmission) sIterator.next(); String userId = submission.getSubmitterId(); if (a.isGroup()) { User[] _users = submission.getSubmitters(); for (int i=0; _users != null && i < _users.length; i++) { userId = _users[i].getId(); if (user_row.containsKey(userId)) { // find right row row = sheet.getRow(((Integer)user_row.get(userId)).intValue()); if (submission.getGraded() && submission.getGrade() != null) { // graded and released if (assignmentType == 3) { try { // numeric cell type? String grade = submission.getGradeForUser(userId) == null ? submission.getGradeDisplay(): submission.getGradeForUser(userId); //We get float number no matter the locale it was managed with. NumberFormat nbFormat = NumberFormat.getNumberInstance(rb.getLocale()); nbFormat.setMaximumFractionDigits(1); nbFormat.setMinimumFractionDigits(1); float f = nbFormat.parse(grade).floatValue(); // remove the String-based cell first cell = row.getCell(cellNum); row.removeCell(cell); // add number based cell cell=row.createCell(cellNum); cell.setCellType(0); cell.setCellValue(f); style = wb.createCellStyle(); style.setDataFormat(wb.createDataFormat().getFormat("#,##0.0")); cell.setCellStyle(style); } catch (Exception e) { // if the grade is not numeric, let's make it as String type // No need to remove the cell and create a new one, as the existing one is String type. cell = row.getCell(cellNum); cell.setCellType(1); cell.setCellValue(submission.getGradeForUser(userId) == null ? submission.getGradeDisplay(): submission.getGradeForUser(userId)); } } else { // String cell type cell = row.getCell(cellNum); cell.setCellValue(submission.getGradeForUser(userId) == null ? submission.getGradeDisplay(): submission.getGradeForUser(userId)); } } else if (submission.getSubmitted() && submission.getTimeSubmitted() != null) { // submitted, but no grade available yet cell = row.getCell(cellNum); cell.setCellValue(rb.getString("gen.nograd")); } } // if } } else { if (user_row.containsKey(userId)) { // find right row row = sheet.getRow(((Integer)user_row.get(userId)).intValue()); if (submission.getGraded() && submission.getGrade() != null) { // graded and released if (assignmentType == 3) { try { // numeric cell type? String grade = submission.getGradeDisplay(); //We get float number no matter the locale it was managed with. NumberFormat nbFormat = NumberFormat.getNumberInstance(rb.getLocale()); nbFormat.setMaximumFractionDigits(1); nbFormat.setMinimumFractionDigits(1); float f = nbFormat.parse(grade).floatValue(); // remove the String-based cell first cell = row.getCell(cellNum); row.removeCell(cell); // add number based cell cell=row.createCell(cellNum); cell.setCellType(0); cell.setCellValue(f); style = wb.createCellStyle(); style.setDataFormat(wb.createDataFormat().getFormat("#,##0.0")); cell.setCellStyle(style); } catch (Exception e) { // if the grade is not numeric, let's make it as String type // No need to remove the cell and create a new one, as the existing one is String type. cell = row.getCell(cellNum); cell.setCellType(1); // Setting grade display instead grade. cell.setCellValue(submission.getGradeDisplay()); } } else { // String cell type cell = row.getCell(cellNum); cell.setCellValue(submission.getGradeDisplay()); } } else if (submission.getSubmitted() && submission.getTimeSubmitted() != null) { // submitted, but no grade available yet cell = row.getCell(cellNum); cell.setCellValue(rb.getString("gen.nograd")); } } // if } } } index++; } // output Blob b = new Blob(); try { wb.write(b.outputStream()); } catch (IOException e) { M_log.warn(" getGradesSpreadsheet Can not output the grade spread sheet for reference= " + ref); } return b.getBytes(); } } // getGradesSpreadsheet @SuppressWarnings("deprecation") public Collection<Group> getSubmitterGroupList(String searchFilterOnly, String allOrOneGroup, String searchString, String aRef, String contextString) { Collection<Group> rv = new ArrayList<Group>(); allOrOneGroup = StringUtil.trimToNull(allOrOneGroup); searchString = StringUtil.trimToNull(searchString); boolean bSearchFilterOnly = "true".equalsIgnoreCase(searchFilterOnly); try { Assignment a = getAssignment(aRef); if (a != null) { Site st = SiteService.getSite(contextString); if (a.getAccess().equals(Assignment.AssignmentAccess.SITE)) { Collection<Group> groupRefs = st.getGroups(); for (Iterator gIterator = groupRefs.iterator(); gIterator.hasNext();) { Group _gg = (Group)gIterator.next(); //if (_gg.getProperties().get(GROUP_SECTION_PROPERTY) == null) { // NO SECTIONS (this might not be valid test for manually created sections) rv.add(_gg); //} } } else { Collection<String> groupRefs = a.getGroups(); for (Iterator gIterator = groupRefs.iterator(); gIterator.hasNext();) { Group _gg = st.getGroup((String)gIterator.next()); // NO SECTIONS (this might not be valid test for manually created sections) if (_gg != null) {// && _gg.getProperties().get(GROUP_SECTION_PROPERTY) == null) { rv.add(_gg); } } } for (Iterator uIterator = rv.iterator(); uIterator.hasNext();) { Group g = (Group) uIterator.next(); AssignmentSubmission uSubmission = getSubmission(aRef, g.getId()); if (uSubmission == null) { if (allowGradeSubmission(a.getReference())) { if (a.isGroup()) { // temporarily allow the user to read and write from assignments (asn.revise permission) SecurityService.pushAdvisor( new MySecurityAdvisor( SessionManager.getCurrentSessionUserId(), new ArrayList<String>(Arrays.asList("asn.revise permission")), ""/* no submission id yet, pass the empty string to advisor*/)); M_log.debug(this + " getSubmitterGroupList context " + contextString + " for assignment " + a.getId() + " for group " + g.getId()); AssignmentSubmissionEdit s = addSubmission(contextString, a.getId(), g.getId()); s.setSubmitted(false); s.setAssignment(a); // set the resubmission properties // get the assignment setting for resubmitting ResourceProperties assignmentProperties = a.getProperties(); String assignmentAllowResubmitNumber = assignmentProperties.getProperty(AssignmentSubmission.ALLOW_RESUBMIT_NUMBER); if (assignmentAllowResubmitNumber != null) { s.getPropertiesEdit().addProperty(AssignmentSubmission.ALLOW_RESUBMIT_NUMBER, assignmentAllowResubmitNumber); String assignmentAllowResubmitCloseDate = assignmentProperties.getProperty(AssignmentSubmission.ALLOW_RESUBMIT_CLOSETIME); // if assignment's setting of resubmit close time is null, use assignment close time as the close time for resubmit s.getPropertiesEdit().addProperty(AssignmentSubmission.ALLOW_RESUBMIT_CLOSETIME, assignmentAllowResubmitCloseDate != null?assignmentAllowResubmitCloseDate:String.valueOf(a.getCloseTime().getTime())); } commitEdit(s); // clear the permission SecurityService.popAdvisor(); } } } } } } catch (IdUnusedException aIdException) { M_log.warn(":getSubmitterGroupList: Assignme id not used: " + aRef + " " + aIdException.getMessage()); } catch (PermissionException aPerException) { M_log.warn(":getSubmitterGroupList: Not allowed to get assignment " + aRef + " " + aPerException.getMessage()); } return rv; } /** * {@inheritDoc}} */ public List<String> getSubmitterIdList(String searchFilterOnly, String allOrOneGroup, String searchString, String aRef, String contextString) { List<String> rv = new ArrayList<String>(); List<User> rvUsers = new ArrayList<User>(); allOrOneGroup = StringUtils.trimToNull(allOrOneGroup); searchString = StringUtils.trimToNull(searchString); boolean bSearchFilterOnly = "true".equalsIgnoreCase(searchFilterOnly); try { Assignment a = getAssignment(aRef); if (a != null) { if (bSearchFilterOnly) { if (allOrOneGroup == null && searchString == null) { // if the option is set to "Only show user submissions according to Group Filter and Search result" // if no group filter and no search string is specified, no user will be shown first by default; return rv; } else { List allowAddSubmissionUsers = allowAddSubmissionUsers(aRef); if (allOrOneGroup == null && searchString != null) { // search is done for all submitters rvUsers = getSearchedUsers(searchString, allowAddSubmissionUsers, false); } else { // group filter first rvUsers = getSelectedGroupUsers(allOrOneGroup, contextString, a, allowAddSubmissionUsers); if (searchString != null) { // then search rvUsers = getSearchedUsers(searchString, rvUsers, true); } } } } else { List allowAddSubmissionUsers = allowAddSubmissionUsers(aRef); // Step 1: get group if any that is selected rvUsers = getSelectedGroupUsers(allOrOneGroup, contextString, a, allowAddSubmissionUsers); // Step 2: get all student that meets the search criteria based on previous group users. If search is null or empty string, return all users. rvUsers = getSearchedUsers(searchString, rvUsers, true); } if (!rvUsers.isEmpty()) { for (Iterator uIterator = rvUsers.iterator(); uIterator.hasNext();) { User u = (User) uIterator.next(); AssignmentSubmission uSubmission = getSubmission(aRef, u); if (uSubmission != null) { rv.add(u.getId()); } // add those users who haven't made any submissions and with submission rights else { // construct fake submissions for grading purpose if the user has right for grading if (allowGradeSubmission(a.getReference())) { try { // temporarily allow the user to read and write from assignments (asn.revise permission) SecurityService.pushAdvisor( new MySecurityAdvisor( SessionManager.getCurrentSessionUserId(), new ArrayList<String>(Arrays.asList(SECURE_ADD_ASSIGNMENT_SUBMISSION, SECURE_UPDATE_ASSIGNMENT_SUBMISSION)), ""/* no submission id yet, pass the empty string to advisor*/)); AssignmentSubmissionEdit s = addSubmission(contextString, a.getId(), u.getId()); if (s != null) { s.setSubmitted(false); s.setAssignment(a); // set the resubmission properties // get the assignment setting for resubmitting ResourceProperties assignmentProperties = a.getProperties(); String assignmentAllowResubmitNumber = assignmentProperties.getProperty(AssignmentSubmission.ALLOW_RESUBMIT_NUMBER); if (assignmentAllowResubmitNumber != null) { s.getPropertiesEdit().addProperty(AssignmentSubmission.ALLOW_RESUBMIT_NUMBER, assignmentAllowResubmitNumber); String assignmentAllowResubmitCloseDate = assignmentProperties.getProperty(AssignmentSubmission.ALLOW_RESUBMIT_CLOSETIME); // if assignment's setting of resubmit close time is null, use assignment close time as the close time for resubmit s.getPropertiesEdit().addProperty(AssignmentSubmission.ALLOW_RESUBMIT_CLOSETIME, assignmentAllowResubmitCloseDate != null?assignmentAllowResubmitCloseDate:String.valueOf(a.getCloseTime().getTime())); } commitEdit(s); rv.add(u.getId()); } } catch (Exception e) { // exception } finally { // clear the permission SecurityService.popAdvisor(); } } } } } } } catch (IdUnusedException aIdException) { M_log.warn(":getSubmitterIdList: Assignme id not used: " + aRef + " " + aIdException.getMessage()); } catch (PermissionException aPerException) { M_log.warn(":getSubmitterIdList: Not allowed to get assignment " + aRef + " " + aPerException.getMessage()); } return rv; } private List<User> getSelectedGroupUsers(String allOrOneGroup, String contextString, Assignment a, List allowAddSubmissionUsers) { Collection groups = new ArrayList(); List<User> selectedGroupUsers = new ArrayList<User>(); if (allOrOneGroup != null && allOrOneGroup.length() > 0) { // now are we view all sections/groups or just specific one? if (allOrOneGroup.equals(AssignmentConstants.ALL)) { if (a.getAccess() == Assignment.AssignmentAccess.SITE) { // site range try { groups.add(SiteService.getSite(contextString)); } catch (IdUnusedException e) { M_log.warn(":getSelectedGroupUsers cannot find site " + " " + contextString + e.getMessage()); } } else { // get all those groups that user is allowed to grade groups = getGroupsAllowGradeAssignment(contextString, a.getReference()); } } else { // filter out only those submissions from the selected-group members try { Group group = SiteService.getSite(contextString).getGroup(allOrOneGroup); groups.add(group); } catch (Exception e) { M_log.warn(":getSelectedGroupUsers " + e.getMessage() + " groupId=" + allOrOneGroup); } } for (Iterator iGroup=groups.iterator(); iGroup.hasNext();) { Object nGroup = iGroup.next(); String authzGroupRef = (nGroup instanceof Group)? ((Group) nGroup).getReference():((nGroup instanceof Site))?((Site) nGroup).getReference():null; if (authzGroupRef != null) { try { AuthzGroup group = AuthzGroupService.getAuthzGroup(authzGroupRef); Set grants = group.getUsers(); for (Iterator iUserIds = grants.iterator(); iUserIds.hasNext();) { String userId = (String) iUserIds.next(); // don't show user multiple times try { User u = UserDirectoryService.getUser(userId); if (u != null && allowAddSubmissionUsers.contains(u)) { if (!selectedGroupUsers.contains(u)) { selectedGroupUsers.add(u); } } } catch (UserNotDefinedException uException) { M_log.warn(":getSelectedGroupUsers " + uException.getMessage() + " userId =" + userId); } } } catch (GroupNotDefinedException gException) { M_log.warn(":getSelectedGroupUsers " + gException.getMessage() + " authGroupId=" + authzGroupRef); } } } } return selectedGroupUsers; } /** * keep the users that match search string in sortname, eid, email field * @param searchString * @param userList * @param retain If true, the original list will be kept if there is no search string specified * @return */ private List getSearchedUsers(String searchString, List userList, boolean retain) { List rv = new ArrayList(); if (searchString != null && searchString.length() > 0) { searchString = searchString.toLowerCase(); for(Iterator iUserList = userList.iterator(); iUserList.hasNext();) { User u = (User) iUserList.next(); // search on user sortname, eid, email String[] fields = {u.getSortName(), u.getEid(), u.getEmail()}; List<String> l = new ArrayList(Arrays.asList(fields)); for (String s : l) { s = s.toLowerCase(); if (s != null && s.indexOf(searchString) != -1) { rv.add(u); break; } } } } else if (retain) { // retain the original list rv = userList; } return rv; } /** * {@inheritDoc} */ public void getSubmissionsZip(OutputStream outputStream, String ref) throws IdUnusedException, PermissionException { M_log.debug(this + ": getSubmissionsZip reference=" + ref); getSubmissionsZip(outputStream, ref, null); } /** * depends on the query string from ui, determine what to include inside the submission zip * @param outputStream * @param ref * @param queryString * @throws IdUnusedException * @throws PermissionException */ protected void getSubmissionsZip(OutputStream out, String ref, String queryString) throws IdUnusedException, PermissionException { M_log.debug(this + ": getSubmissionsZip 2 reference=" + ref); boolean withStudentSubmissionText = false; boolean withStudentSubmissionAttachment = false; boolean withGradeFile = false; boolean withFeedbackText = false; boolean withFeedbackComment = false; boolean withFeedbackAttachment = false; String viewString = ""; String contextString = ""; String searchString = ""; String searchFilterOnly = ""; if (queryString != null) { StringTokenizer queryTokens = new StringTokenizer(queryString, "&"); // Parsing the range list while (queryTokens.hasMoreTokens()) { String token = queryTokens.nextToken().trim(); // check against the content elements selection if (token.contains("studentSubmissionText")) { // should contain student submission text information withStudentSubmissionText = true; } else if (token.contains("studentSubmissionAttachment")) { // should contain student submission attachment information withStudentSubmissionAttachment = true; } else if (token.contains("gradeFile")) { // should contain grade file withGradeFile = true; } else if (token.contains("feedbackTexts")) { // inline text withFeedbackText = true; } else if (token.contains("feedbackComments")) { // comments should be available withFeedbackComment = true; } else if (token.contains("feedbackAttachments")) { // feedback attachment withFeedbackAttachment = true; } else if (token.contains("contextString")) { // context contextString = token.indexOf("=") != -1 ? token.substring(token.indexOf("=") + 1) : ""; } else if (token.contains("viewString")) { // view viewString = token.indexOf("=") != -1 ? token.substring(token.indexOf("=") + 1) : ""; } else if (token.contains("searchString")) { // search searchString = token.indexOf("=") != -1 ? token.substring(token.indexOf("=") + 1) : ""; } else if (token.contains("searchFilterOnly")) { // search and group filter only searchFilterOnly = token.indexOf("=") != -1 ? token.substring(token.indexOf("=") + 1) : ""; } } } byte[] rv = null; try { String aRef = assignmentReferenceFromSubmissionsZipReference(ref); Assignment a = getAssignment(aRef); if (a.isGroup()) { Collection<Group> submitterGroups = getSubmitterGroupList(searchFilterOnly, viewString.length() == 0 ? AssignmentConstants.ALL:viewString, searchString, aRef, contextString == null ? a.getContext(): contextString); if (submitterGroups != null && !submitterGroups.isEmpty()) { List<GroupSubmission> submissions = new ArrayList<GroupSubmission>(); for (Iterator<Group> iSubmitterGroupsIterator = submitterGroups.iterator(); iSubmitterGroupsIterator.hasNext();) { Group g = iSubmitterGroupsIterator.next(); M_log.debug(this + " ZIP GROUP " + g.getTitle() ); AssignmentSubmission sub = getSubmission(aRef, g.getId()); M_log.debug(this + " ZIP GROUP " + g.getTitle() + " SUB " + (sub == null ? "null": sub.getId() )); if (g != null) { GroupSubmission gs = new GroupSubmission(g, sub); submissions.add(gs); } } StringBuilder exceptionMessage = new StringBuilder(); if (allowGradeSubmission(aRef)) { zipGroupSubmissions(aRef, a.getTitle(), a.getContent().getTypeOfGradeString(a.getContent().getTypeOfGrade()), a.getContent().getTypeOfSubmission(), new SortedIterator(submissions.iterator(), new AssignmentComparator("submitterName", "true")), out, exceptionMessage, withStudentSubmissionText, withStudentSubmissionAttachment, withGradeFile, withFeedbackText, withFeedbackComment, withFeedbackAttachment); if (exceptionMessage.length() > 0) { // log any error messages M_log.warn(" getSubmissionsZip ref=" + ref + exceptionMessage.toString()); } } } } else { List<String> submitterIds = getSubmitterIdList(searchFilterOnly, viewString.length() == 0 ? AssignmentConstants.ALL:viewString, searchString, aRef, contextString == null? a.getContext():contextString); if (submitterIds != null && !submitterIds.isEmpty()) { List<AssignmentSubmission> submissions = new ArrayList<AssignmentSubmission>(); for (Iterator<String> iSubmitterIdsIterator = submitterIds.iterator(); iSubmitterIdsIterator.hasNext();) { String uId = iSubmitterIdsIterator.next(); try { User u = UserDirectoryService.getUser(uId); AssignmentSubmission sub = getSubmission(aRef, u); if (sub != null) submissions.add(sub); } catch (UserNotDefinedException e) { M_log.warn(":getSubmissionsZip cannot find user id=" + uId + e.getMessage() + ""); } } StringBuilder exceptionMessage = new StringBuilder(); if (allowGradeSubmission(aRef)) { zipSubmissions(aRef, a.getTitle(), a.getContent().getTypeOfGradeString(a.getContent().getTypeOfGrade()), a.getContent().getTypeOfSubmission(), new SortedIterator(submissions.iterator(), new AssignmentComparator("submitterName", "true")), out, exceptionMessage, withStudentSubmissionText, withStudentSubmissionAttachment, withGradeFile, withFeedbackText, withFeedbackComment, withFeedbackAttachment); if (exceptionMessage.length() > 0) { // log any error messages M_log.warn(" getSubmissionsZip ref=" + ref + exceptionMessage.toString()); } } } } } catch (IdUnusedException e) { M_log.debug(this + "getSubmissionsZip -IdUnusedException Unable to get assignment " + ref); throw new IdUnusedException(ref); } catch (PermissionException e) { M_log.warn(" getSubmissionsZip -PermissionException Not permitted to get assignment " + ref); throw new PermissionException(SessionManager.getCurrentSessionUserId(), SECURE_ACCESS_ASSIGNMENT, ref); } } // getSubmissionsZip public String escapeInvalidCharsEntry(String accentedString) { String decomposed = Normalizer.normalize(accentedString, Normalizer.Form.NFD); String cleanString = decomposed.replaceAll("\\p{InCombiningDiacriticalMarks}+", ""); return cleanString; } protected void zipGroupSubmissions(String assignmentReference, String assignmentTitle, String gradeTypeString, int typeOfSubmission, Iterator submissions, OutputStream outputStream, StringBuilder exceptionMessage, boolean withStudentSubmissionText, boolean withStudentSubmissionAttachment, boolean withGradeFile, boolean withFeedbackText, boolean withFeedbackComment, boolean withFeedbackAttachment) { ZipOutputStream out = null; try { out = new ZipOutputStream(outputStream); // create the folder structure - named after the assignment's title String root = Validator.escapeZipEntry(assignmentTitle) + Entity.SEPARATOR; String submittedText = ""; if (!submissions.hasNext()) { exceptionMessage.append("There is no submission yet. "); } // the buffer used to store grade information StringBuilder gradesBuffer = new StringBuilder(assignmentTitle + "," + gradeTypeString + "\n\n"); gradesBuffer.append("Group" + "," + rb.getString("grades.eid") + "," + "Users" + "," + rb.getString("grades.grade") + "\n"); // allow add assignment members List allowAddSubmissionUsers = allowAddSubmissionUsers(assignmentReference); // Create the ZIP file String submittersName = ""; int count = 1; String caughtException = null; while (submissions.hasNext()) { GroupSubmission gs = (GroupSubmission) submissions.next(); AssignmentSubmission s = gs.getSubmission(); M_log.debug( this + " ZIPGROUP " + ( s == null ? "null": s.getId() )); if (s.getSubmitted()) { try { count = 1; submittersName = root; User[] submitters = s.getSubmitters(); String submitterString = gs.getGroup().getTitle() + " (" + gs.getGroup().getId() + ")"; String submittersString = ""; String submitters2String = ""; for (int i = 0; i < submitters.length; i++) { if (i > 0) { submittersString = submittersString.concat("; "); submitters2String = submitters2String.concat("; "); } String fullName = submitters[i].getSortName(); // in case the user doesn't have first name or last name if (fullName.indexOf(",") == -1) { fullName=fullName.concat(","); } submittersString = submittersString.concat(fullName); submitters2String = submitters2String.concat(submitters[i].getDisplayName()); // add the eid to the end of it to guarantee folder name uniqness submittersString = submittersString + "(" + submitters[i].getEid() + ")"; } gradesBuffer.append( gs.getGroup().getTitle() + "," + gs.getGroup().getId() + "," + submitters2String + "," + s.getGradeDisplay() + "\n"); if (StringUtil.trimToNull(submitterString) != null) { submittersName = submittersName.concat(StringUtil.trimToNull(submitterString)); submittedText = s.getSubmittedText(); submittersName = submittersName.concat("/"); // record submission timestamp if (s.getSubmitted() && s.getTimeSubmitted() != null) { ZipEntry textEntry = new ZipEntry(submittersName + "timestamp.txt"); out.putNextEntry(textEntry); byte[] b = (s.getTimeSubmitted().toString()).getBytes(); out.write(b); textEntry.setSize(b.length); out.closeEntry(); } // create the folder structure - named after the submitter's name if (typeOfSubmission != Assignment.ATTACHMENT_ONLY_ASSIGNMENT_SUBMISSION && typeOfSubmission != Assignment.NON_ELECTRONIC_ASSIGNMENT_SUBMISSION) { // include student submission text if (withStudentSubmissionText) { // create the text file only when a text submission is allowed ZipEntry textEntry = new ZipEntry(submittersName + submitterString + "_submissionText" + ZIP_SUBMITTED_TEXT_FILE_TYPE); out.putNextEntry(textEntry); byte[] text = submittedText.getBytes(); out.write(text); textEntry.setSize(text.length); out.closeEntry(); } // include student submission feedback text if (withFeedbackText) { // create a feedbackText file into zip ZipEntry fTextEntry = new ZipEntry(submittersName + "feedbackText.html"); out.putNextEntry(fTextEntry); byte[] fText = s.getFeedbackText().getBytes(); out.write(fText); fTextEntry.setSize(fText.length); out.closeEntry(); } } if (typeOfSubmission != Assignment.TEXT_ONLY_ASSIGNMENT_SUBMISSION && typeOfSubmission != Assignment.NON_ELECTRONIC_ASSIGNMENT_SUBMISSION) { // include student submission attachment if (withStudentSubmissionAttachment) { // create a attachment folder for the submission attachments String sSubAttachmentFolder = submittersName + rb.getString("stuviewsubm.submissatt") + "/"; ZipEntry sSubAttachmentFolderEntry = new ZipEntry(sSubAttachmentFolder); out.putNextEntry(sSubAttachmentFolderEntry); // add all submission attachment into the submission attachment folder zipAttachments(out, submittersName, sSubAttachmentFolder, s.getSubmittedAttachments()); out.closeEntry(); } } if (withFeedbackComment) { // the comments.txt file to show instructor's comments ZipEntry textEntry = new ZipEntry(submittersName + "comments" + ZIP_COMMENT_FILE_TYPE); out.putNextEntry(textEntry); byte[] b = FormattedText.encodeUnicode(s.getFeedbackComment()).getBytes(); out.write(b); textEntry.setSize(b.length); out.closeEntry(); } if (withFeedbackAttachment) { // create an attachment folder for the feedback attachments String feedbackSubAttachmentFolder = submittersName + rb.getString("download.feedback.attachment") + "/"; ZipEntry feedbackSubAttachmentFolderEntry = new ZipEntry(feedbackSubAttachmentFolder); out.putNextEntry(feedbackSubAttachmentFolderEntry); // add all feedback attachment folder zipAttachments(out, submittersName, feedbackSubAttachmentFolder, s.getFeedbackAttachments()); out.closeEntry(); } if (submittersString.trim().length() > 0) { // the comments.txt file to show instructor's comments ZipEntry textEntry = new ZipEntry(submittersName + "members" + ZIP_COMMENT_FILE_TYPE); out.putNextEntry(textEntry); byte[] b = FormattedText.encodeUnicode(submittersString).getBytes(); out.write(b); textEntry.setSize(b.length); out.closeEntry(); } } // if } catch (Exception e) { caughtException = e.toString(); break; } } // if the user is still in site } // while -- there is submission if (caughtException == null) { // continue if (withGradeFile) { // create a grades.csv file into zip ZipEntry gradesCSVEntry = new ZipEntry(root + "grades.csv"); out.putNextEntry(gradesCSVEntry); byte[] grades = gradesBuffer.toString().getBytes(); out.write(grades); gradesCSVEntry.setSize(grades.length); out.closeEntry(); } } else { // log the error exceptionMessage.append(" Exception " + caughtException + " for creating submission zip file for assignment " + "\"" + assignmentTitle + "\"\n"); } } catch (IOException e) { exceptionMessage.append("IOException for creating submission zip file for assignment " + "\"" + assignmentTitle + "\" exception: " + e + "\n"); } finally { // Complete the ZIP file if (out != null) { try { out.finish(); out.flush(); } catch (IOException e) { // tried } try { out.close(); } catch (IOException e) { // tried } } } } protected void zipSubmissions(String assignmentReference, String assignmentTitle, String gradeTypeString, int typeOfSubmission, Iterator submissions, OutputStream outputStream, StringBuilder exceptionMessage, boolean withStudentSubmissionText, boolean withStudentSubmissionAttachment, boolean withGradeFile, boolean withFeedbackText, boolean withFeedbackComment, boolean withFeedbackAttachment) { ZipOutputStream out = null; try { out = new ZipOutputStream(outputStream); // create the folder structure - named after the assignment's title String root = escapeInvalidCharsEntry(Validator.escapeZipEntry(assignmentTitle)) + Entity.SEPARATOR; String submittedText = ""; if (!submissions.hasNext()) { exceptionMessage.append("There is no submission yet. "); } // the buffer used to store grade information ByteArrayOutputStream gradesBAOS = new ByteArrayOutputStream(); CSVWriter gradesBuffer = new CSVWriter(new OutputStreamWriter(gradesBAOS)); String [] values = {assignmentTitle,gradeTypeString}; gradesBuffer.writeNext(values); //Blank line was in original gradefile values = new String[] {""}; gradesBuffer.writeNext(values); values = new String[] {rb.getString("grades.id"),rb.getString("grades.eid"),rb.getString("grades.lastname"),rb.getString("grades.firstname"),rb.getString("grades.grade")}; gradesBuffer.writeNext(values); // allow add assignment members List allowAddSubmissionUsers = allowAddSubmissionUsers(assignmentReference); // Create the ZIP file String submittersName = ""; int count = 1; String caughtException = null; while (submissions.hasNext()) { AssignmentSubmission s = (AssignmentSubmission) submissions.next(); if (s.getSubmitted()) { // get the submission user id and see if the user is still in site String userId = s.getSubmitterId(); try { User u = UserDirectoryService.getUser(userId); if (allowAddSubmissionUsers.contains(u)) { count = 1; submittersName = root; User[] submitters = s.getSubmitters(); String submittersString = ""; for (int i = 0; i < submitters.length; i++) { if (i > 0) { submittersString = submittersString.concat("; "); } String fullName = submitters[i].getSortName(); // in case the user doesn't have first name or last name if (fullName.indexOf(",") == -1) { fullName=fullName.concat(","); } submittersString = submittersString.concat(fullName); // add the eid to the end of it to guarantee folder name uniqness // if user Eid contains non ascii characters, the user internal id will be used String userEid = submitters[i].getEid(); String candidateEid = escapeInvalidCharsEntry(userEid); if (candidateEid.equals(userEid)){ submittersString = submittersString + "(" + candidateEid + ")"; } else{ submittersString = submittersString + "(" + submitters[i].getId() + ")"; } submittersString = escapeInvalidCharsEntry(submittersString); // in grades file, Eid is used values = new String [] {submitters[i].getDisplayId(), submitters[i].getEid(), submitters[i].getLastName(), submitters[i].getFirstName(), s.getGradeDisplay()}; gradesBuffer.writeNext(values); } if (StringUtils.trimToNull(submittersString) != null) { submittersName = submittersName.concat(StringUtils.trimToNull(submittersString)); submittedText = s.getSubmittedText(); submittersName = submittersName.concat("/"); // record submission timestamp if (s.getSubmitted() && s.getTimeSubmitted() != null) { ZipEntry textEntry = new ZipEntry(submittersName + "timestamp.txt"); out.putNextEntry(textEntry); byte[] b = (s.getTimeSubmitted().toString()).getBytes(); out.write(b); textEntry.setSize(b.length); out.closeEntry(); } // create the folder structure - named after the submitter's name if (typeOfSubmission != Assignment.ATTACHMENT_ONLY_ASSIGNMENT_SUBMISSION && typeOfSubmission != Assignment.NON_ELECTRONIC_ASSIGNMENT_SUBMISSION) { // include student submission text if (withStudentSubmissionText) { // create the text file only when a text submission is allowed ZipEntry textEntry = new ZipEntry(submittersName + submittersString + "_submissionText" + ZIP_SUBMITTED_TEXT_FILE_TYPE); out.putNextEntry(textEntry); byte[] text = submittedText.getBytes(); out.write(text); textEntry.setSize(text.length); out.closeEntry(); } // include student submission feedback text if (withFeedbackText) { // create a feedbackText file into zip ZipEntry fTextEntry = new ZipEntry(submittersName + "feedbackText.html"); out.putNextEntry(fTextEntry); byte[] fText = s.getFeedbackText().getBytes(); out.write(fText); fTextEntry.setSize(fText.length); out.closeEntry(); } } if (typeOfSubmission != Assignment.TEXT_ONLY_ASSIGNMENT_SUBMISSION && typeOfSubmission != Assignment.NON_ELECTRONIC_ASSIGNMENT_SUBMISSION) { // include student submission attachment if (withStudentSubmissionAttachment) { // create a attachment folder for the submission attachments String sSubAttachmentFolder = submittersName + rb.getString("stuviewsubm.submissatt") + "/"; sSubAttachmentFolder = escapeInvalidCharsEntry(sSubAttachmentFolder); ZipEntry sSubAttachmentFolderEntry = new ZipEntry(sSubAttachmentFolder); out.putNextEntry(sSubAttachmentFolderEntry); // add all submission attachment into the submission attachment folder zipAttachments(out, submittersName, sSubAttachmentFolder, s.getSubmittedAttachments()); out.closeEntry(); } } if (withFeedbackComment) { // the comments.txt file to show instructor's comments ZipEntry textEntry = new ZipEntry(submittersName + "comments" + ZIP_COMMENT_FILE_TYPE); out.putNextEntry(textEntry); byte[] b = FormattedText.encodeUnicode(s.getFeedbackComment()).getBytes(); out.write(b); textEntry.setSize(b.length); out.closeEntry(); } if (withFeedbackAttachment) { // create an attachment folder for the feedback attachments String feedbackSubAttachmentFolder = submittersName + rb.getString("download.feedback.attachment") + "/"; feedbackSubAttachmentFolder = escapeInvalidCharsEntry(feedbackSubAttachmentFolder); ZipEntry feedbackSubAttachmentFolderEntry = new ZipEntry(feedbackSubAttachmentFolder); out.putNextEntry(feedbackSubAttachmentFolderEntry); // add all feedback attachment folder zipAttachments(out, submittersName, feedbackSubAttachmentFolder, s.getFeedbackAttachments()); out.closeEntry(); } } // if } } catch (Exception e) { caughtException = e.toString(); break; } } // if the user is still in site } // while -- there is submission if (caughtException == null) { // continue if (withGradeFile) { // create a grades.csv file into zip ZipEntry gradesCSVEntry = new ZipEntry(root + "grades.csv"); out.putNextEntry(gradesCSVEntry); gradesBuffer.close(); out.write(gradesBAOS.toByteArray()); gradesCSVEntry.setSize(gradesBAOS.size()); out.closeEntry(); } } else { // log the error exceptionMessage.append(" Exception " + caughtException + " for creating submission zip file for assignment " + "\"" + assignmentTitle + "\"\n"); } } catch (IOException e) { exceptionMessage.append("IOException for creating submission zip file for assignment " + "\"" + assignmentTitle + "\" exception: " + e + "\n"); } finally { // Complete the ZIP file if (out != null) { try { out.finish(); out.flush(); } catch (IOException e) { // tried } try { out.close(); } catch (IOException e) { // tried } } } } private void zipAttachments(ZipOutputStream out, String submittersName, String sSubAttachmentFolder, List attachments) { int attachedUrlCount = 0; InputStream content = null; HashMap<String, Integer> done = new HashMap<String, Integer> (); for (int j = 0; j < attachments.size(); j++) { Reference r = (Reference) attachments.get(j); try { ContentResource resource = m_contentHostingService.getResource(r.getId()); String contentType = resource.getContentType(); ResourceProperties props = r.getProperties(); String displayName = props.getPropertyFormatted(props.getNamePropDisplayName()); displayName = escapeInvalidCharsEntry(displayName); // for URL content type, encode a redirect to the body URL if (contentType.equalsIgnoreCase(ResourceProperties.TYPE_URL)) { displayName = "attached_URL_" + attachedUrlCount; attachedUrlCount++; } // buffered stream input content = resource.streamContent(); byte data[] = new byte[1024 * 10]; BufferedInputStream bContent = null; try { bContent = new BufferedInputStream(content, data.length); String candidateName = sSubAttachmentFolder + displayName; String realName = null; Integer already = done.get(candidateName); if (already == null) { realName = candidateName; done.put(candidateName, 1); } else { realName = candidateName + "+" + already; done.put(candidateName, already + 1); } ZipEntry attachmentEntry = new ZipEntry(realName); out.putNextEntry(attachmentEntry); int bCount = -1; while ((bCount = bContent.read(data, 0, data.length)) != -1) { out.write(data, 0, bCount); } try { out.closeEntry(); // The zip entry need to be closed } catch (IOException ioException) { M_log.warn(":zipAttachments: problem closing zip entry " + ioException); } } catch (IllegalArgumentException iException) { M_log.warn(":zipAttachments: problem creating BufferedInputStream with content and length " + data.length + iException); } finally { if (bContent != null) { try { bContent.close(); // The BufferedInputStream needs to be closed } catch (IOException ioException) { M_log.warn(":zipAttachments: problem closing FileChannel " + ioException); } } } } catch (PermissionException e) { M_log.warn(" zipAttachments--PermissionException submittersName=" + submittersName + " attachment reference=" + r); } catch (IdUnusedException e) { M_log.warn(" zipAttachments--IdUnusedException submittersName=" + submittersName + " attachment reference=" + r); } catch (TypeException e) { M_log.warn(" zipAttachments--TypeException: submittersName=" + submittersName + " attachment reference=" + r); } catch (IOException e) { M_log.warn(" zipAttachments--IOException: Problem in creating the attachment file: submittersName=" + submittersName + " attachment reference=" + r + " error " + e); } catch (ServerOverloadException e) { M_log.warn(" zipAttachments--ServerOverloadException: submittersName=" + submittersName + " attachment reference=" + r); } finally { if (content != null) { try { content.close(); // The input stream needs to be closed } catch (IOException ioException) { M_log.warn(":zipAttachments: problem closing Inputstream content " + ioException); } } } } // for } /** * Get the string to form an assignment grade spreadsheet * * @param context * The assignment context String * @param assignmentId * The id for the assignment object; when null, indicates all assignment in that context */ public String gradesSpreadsheetReference(String context, String assignmentId) { // based on all assignment in that context String s = REFERENCE_ROOT + Entity.SEPARATOR + REF_TYPE_GRADES + Entity.SEPARATOR + context; if (assignmentId != null) { // based on the specified assignment only s = s.concat(Entity.SEPARATOR + assignmentId); } return s; } // gradesSpreadsheetReference /** * Get the string to form an assignment submissions zip file * * @param context * The assignment context String * @param assignmentReference * The reference for the assignment object; */ public String submissionsZipReference(String context, String assignmentReference) { // based on the specified assignment return REFERENCE_ROOT + Entity.SEPARATOR + REF_TYPE_SUBMISSIONS + Entity.SEPARATOR + context + Entity.SEPARATOR + assignmentReference; } // submissionsZipReference /** * Decode the submissionsZipReference string to get the assignment reference String * * @param sReference * The submissionZipReference String * @return The assignment reference String */ private String assignmentReferenceFromSubmissionsZipReference(String sReference) { // remove the String part relating to submissions zip reference if (sReference.indexOf(Entity.SEPARATOR +"site") == -1) { return sReference.substring(sReference.lastIndexOf(Entity.SEPARATOR + "assignment")); } else { return sReference.substring(sReference.lastIndexOf(Entity.SEPARATOR + "assignment"), sReference.indexOf(Entity.SEPARATOR +"site")); } } // assignmentReferenceFromSubmissionsZipReference /** * Decode the submissionsZipReference string to get the group reference String * * @param sReference * The submissionZipReference String * @return The group reference String */ private String groupReferenceFromSubmissionsZipReference(String sReference) { // remove the String part relating to submissions zip reference if (sReference.indexOf(Entity.SEPARATOR +"site") != -1) { return sReference.substring(sReference.lastIndexOf(Entity.SEPARATOR + "site")); } else { return null; } } // assignmentReferenceFromSubmissionsZipReference /********************************************************************************************************************************************************************************************************************************************************** * ResourceService implementation *********************************************************************************************************************************************************************************************************************************************************/ /** * {@inheritDoc} */ public String getLabel() { return "assignment"; } /** * {@inheritDoc} */ public boolean willArchiveMerge() { return true; } /** * {@inheritDoc} */ public HttpAccess getHttpAccess() { return new HttpAccess() { public void handleAccess(HttpServletRequest req, HttpServletResponse res, Reference ref, Collection copyrightAcceptedRefs) throws EntityPermissionException, EntityNotDefinedException, EntityAccessOverloadException, EntityCopyrightException { if (SessionManager.getCurrentSessionUserId() == null) { // fail the request, user not logged in yet. } else { try { if (REF_TYPE_SUBMISSIONS.equals(ref.getSubType())) { String queryString = req.getQueryString(); res.setContentType("application/zip"); res.setHeader("Content-Disposition", "attachment; filename = bulk_download.zip"); OutputStream out = null; try { out = res.getOutputStream(); // get the submissions zip blob getSubmissionsZip(out, ref.getReference(), queryString); } catch (Throwable ignore) { M_log.error(this + " getHttpAccess handleAccess " + ignore.getMessage() + " ref=" + ref.getReference()); } finally { if (out != null) { try { out.flush(); out.close(); } catch (Throwable ignore) { M_log.warn(": handleAccess 1 " + ignore.getMessage()); } } } } else if (REF_TYPE_GRADES.equals(ref.getSubType())) { // get the grades spreadsheet blob byte[] spreadsheet = getGradesSpreadsheet(ref.getReference()); if (spreadsheet != null) { res.setContentType("application/vnd.ms-excel"); res.setHeader("Content-Disposition", "attachment; filename = export_grades_file.xls"); OutputStream out = null; try { out = res.getOutputStream(); out.write(spreadsheet); out.flush(); out.close(); } catch (Throwable ignore) { M_log.warn(": handleAccess 2 " + ignore.getMessage()); } finally { if (out != null) { try { out.close(); } catch (Throwable ignore) { M_log.warn(": handleAccess 3 " + ignore.getMessage()); } } } } } else { M_log.warn("handleAccess: throw IdUnusedException " + ref.getReference()); throw new IdUnusedException(ref.getReference()); } } catch (Throwable t) { M_log.warn(" HandleAccess: caught exception " + t.toString() + " and rethrow it!"); throw new EntityNotDefinedException(ref.getReference()); } } } }; } /** * {@inheritDoc} */ public boolean parseEntityReference(String reference, Reference ref) { if (reference.startsWith(REFERENCE_ROOT)) { String id = null; String subType = null; String container = null; String context = null; // Note: StringUtils.split would not produce the following first null part // Still use StringUtil here. String[] parts = StringUtil.split(reference, Entity.SEPARATOR); // we will get null, assignment, [a|c|s|grades|submissions], context, [auid], id if (parts.length > 2) { subType = parts[2]; if (parts.length > 3) { // context is the container context = parts[3]; // submissions have the assignment unique id as a container if ("s".equals(subType)) { if (parts.length > 5) { container = parts[4]; id = parts[5]; } } // others don't else { if (parts.length > 4) { id = parts[4]; } } } } ref.set(APPLICATION_ID, subType, id, container, context); return true; } return false; } /** * {@inheritDoc} */ public Entity getEntity(Reference ref) { Entity rv = null; try { // is it an AssignmentContent object if (REF_TYPE_CONTENT.equals(ref.getSubType())) { rv = getAssignmentContent(ref.getReference()); } // is it an Assignment object else if (REF_TYPE_ASSIGNMENT.equals(ref.getSubType())) { rv = getAssignment(ref.getReference()); } // is it an AssignmentSubmission object else if (REF_TYPE_SUBMISSION.equals(ref.getSubType())) { rv = getSubmission(ref.getReference()); } else M_log.warn("getEntity(): unknown message ref subtype: " + ref.getSubType() + " in ref: " + ref.getReference()); } catch (PermissionException e) { M_log.warn("getEntity(): " + e + " ref=" + ref.getReference()); } catch (IdUnusedException e) { M_log.warn("getEntity(): " + e + " ref=" + ref.getReference()); } catch (NullPointerException e) { M_log.warn("getEntity(): " + e + " ref=" + ref.getReference()); } return rv; } /** * {@inheritDoc} */ public Collection getEntityAuthzGroups(Reference ref, String userId) { Collection rv = new ArrayList(); // for AssignmentService assignments: // if access set to SITE, use the assignment and site authzGroups. // if access set to GROUPED, use the assignment, and the groups, but not the site authzGroups. // if the user has SECURE_ALL_GROUPS in the context, ignore GROUPED access and treat as if SITE try { // for assignment if (REF_TYPE_ASSIGNMENT.equals(ref.getSubType())) { // assignment rv.add(ref.getReference()); boolean grouped = false; Collection groups = null; // check SECURE_ALL_GROUPS - if not, check if the assignment has groups or not // TODO: the last param needs to be a ContextService.getRef(ref.getContext())... or a ref.getContextAuthzGroup() -ggolden if ((userId == null) || ((!SecurityService.isSuperUser(userId)) && (!SecurityService.unlock(userId, SECURE_ALL_GROUPS, SiteService.siteReference(ref.getContext()))))) { // get the channel to get the message to get group information // TODO: check for efficiency, cache and thread local caching usage -ggolden if (ref.getId() != null) { Assignment a = findAssignment(ref.getReference()); if (a != null) { grouped = Assignment.AssignmentAccess.GROUPED == a.getAccess(); groups = a.getGroups(); } } } if (grouped) { // groups rv.addAll(groups); } // not grouped else { // site ref.addSiteContextAuthzGroup(rv); } } else { rv.add(ref.getReference()); // for content and submission, use site security setting ref.addSiteContextAuthzGroup(rv); } } catch (Throwable e) { M_log.warn(" getEntityAuthzGroups(): " + e.getMessage() + " ref=" + ref.getReference()); } return rv; } /** * {@inheritDoc} */ public String getEntityUrl(Reference ref) { String rv = null; try { // is it an AssignmentContent object if (REF_TYPE_CONTENT.equals(ref.getSubType())) { AssignmentContent c = getAssignmentContent(ref.getReference()); rv = c.getUrl(); } // is it an Assignment object else if (REF_TYPE_ASSIGNMENT.equals(ref.getSubType())) { Assignment a = getAssignment(ref.getReference()); rv = a.getUrl(); } // is it an AssignmentSubmission object else if (REF_TYPE_SUBMISSION.equals(ref.getSubType())) { AssignmentSubmission s = getSubmission(ref.getReference()); rv = s.getUrl(); } else M_log.warn(" getEntityUrl(): unknown message ref subtype: " + ref.getSubType() + " in ref: " + ref.getReference()); } catch (PermissionException e) { M_log.warn("getEntityUrl(): " + e + " ref=" + ref.getReference()); } catch (IdUnusedException e) { M_log.warn("getEntityUrl(): " + e + " ref=" + ref.getReference()); } catch (NullPointerException e) { M_log.warn("getEntityUrl(): " + e + " ref=" + ref.getReference()); } return rv; } /** * {@inheritDoc} */ public String archive(String siteId, Document doc, Stack stack, String archivePath, List attachments) { // prepare the buffer for the results log StringBuilder results = new StringBuilder(); // String assignRef = assignmentReference(siteId, SiteService.MAIN_CONTAINER); results.append("archiving " + getLabel() + " context " + Entity.SEPARATOR + siteId + Entity.SEPARATOR + SiteService.MAIN_CONTAINER + ".\n"); // start with an element with our very own (service) name Element element = doc.createElement(AssignmentService.class.getName()); ((Element) stack.peek()).appendChild(element); stack.push(element); Iterator assignmentsIterator = getAssignmentsForContext(siteId); while (assignmentsIterator.hasNext()) { Assignment assignment = (Assignment) assignmentsIterator.next(); // archive this assignment Element el = assignment.toXml(doc, stack); element.appendChild(el); // in order to make the assignment.xml have a better structure // the content id attribute removed from the assignment node // the content will be a child of assignment node el.removeAttribute("assignmentcontent"); // then archive the related content AssignmentContent content = (AssignmentContent) assignment.getContent(); if (content != null) { Element contentEl = content.toXml(doc, stack); // assignment node has already kept the context info contentEl.removeAttribute("context"); // collect attachments List atts = content.getAttachments(); for (int i = 0; i < atts.size(); i++) { Reference ref = (Reference) atts.get(i); // if it's in the attachment area, and not already in the list if ((ref.getReference().startsWith("/content/attachment/")) && (!attachments.contains(ref))) { attachments.add(ref); } // in order to make assignment.xml has the consistent format with the other xml files // move the attachments to be the children of the content, instead of the attributes String attributeString = "attachment" + i; String attRelUrl = contentEl.getAttribute(attributeString); contentEl.removeAttribute(attributeString); Element attNode = doc.createElement("attachment"); attNode.setAttribute("relative-url", attRelUrl); contentEl.appendChild(attNode); } // for // make the content a childnode of the assignment node el.appendChild(contentEl); Iterator submissionsIterator = getSubmissions(assignment).iterator(); while (submissionsIterator.hasNext()) { AssignmentSubmission submission = (AssignmentSubmission) submissionsIterator.next(); // archive this assignment Element submissionEl = submission.toXml(doc, stack); el.appendChild(submissionEl); } } // if } // while stack.pop(); return results.toString(); } // archive /** * Replace the WT user id with the new qualified id * * @param el * The XML element holding the perproties * @param useIdTrans * The HashMap to track old WT id to new CTools id */ protected void WTUserIdTrans(Element el, Map userIdTrans) { NodeList children4 = el.getChildNodes(); int length4 = children4.getLength(); for (int i4 = 0; i4 < length4; i4++) { Node child4 = children4.item(i4); if (child4.getNodeType() == Node.ELEMENT_NODE) { Element element4 = (Element) child4; if (element4.getTagName().equals("property")) { String creatorId = ""; String modifierId = ""; if (element4.hasAttribute("CHEF:creator")) { if ("BASE64".equalsIgnoreCase(element4.getAttribute("enc"))) { creatorId = Xml.decodeAttribute(element4, "CHEF:creator"); } else { creatorId = element4.getAttribute("CHEF:creator"); } String newCreatorId = (String) userIdTrans.get(creatorId); if (newCreatorId != null) { Xml.encodeAttribute(element4, "CHEF:creator", newCreatorId); element4.setAttribute("enc", "BASE64"); } } else if (element4.hasAttribute("CHEF:modifiedby")) { if ("BASE64".equalsIgnoreCase(element4.getAttribute("enc"))) { modifierId = Xml.decodeAttribute(element4, "CHEF:modifiedby"); } else { modifierId = element4.getAttribute("CHEF:modifiedby"); } String newModifierId = (String) userIdTrans.get(modifierId); if (newModifierId != null) { Xml.encodeAttribute(element4, "CHEF:creator", newModifierId); element4.setAttribute("enc", "BASE64"); } } } } } } // WTUserIdTrans /** * {@inheritDoc} */ public String merge(String siteId, Element root, String archivePath, String fromSiteId, Map attachmentNames, Map userIdTrans, Set userListAllowImport) { // prepare the buffer for the results log StringBuilder results = new StringBuilder(); int count = 0; try { // pass the DOM to get new assignment ids, and adjust attachments NodeList children2 = root.getChildNodes(); int length2 = children2.getLength(); for (int i2 = 0; i2 < length2; i2++) { Node child2 = children2.item(i2); if (child2.getNodeType() == Node.ELEMENT_NODE) { Element element2 = (Element) child2; if (element2.getTagName().equals("assignment")) { // a flag showing if continuing merging the assignment boolean goAhead = true; AssignmentContentEdit contentEdit = null; // element2 now - assignment node // adjust the id of this assignment // String newId = IdManager.createUuid(); element2.setAttribute("id", IdManager.createUuid()); element2.setAttribute("context", siteId); // cloneNode(false) - no children cloned Element el2clone = (Element) element2.cloneNode(false); // traverse this assignment node first to check if the person who last modified, has the right role. // if no right role, mark the flag goAhead to be false. NodeList children3 = element2.getChildNodes(); int length3 = children3.getLength(); for (int i3 = 0; i3 < length3; i3++) { Node child3 = children3.item(i3); if (child3.getNodeType() == Node.ELEMENT_NODE) { Element element3 = (Element) child3; // add the properties childnode to the clone of assignment node if (element3.getTagName().equals("properties")) { NodeList children6 = element3.getChildNodes(); int length6 = children6.getLength(); for (int i6 = 0; i6 < length6; i6++) { Node child6 = children6.item(i6); if (child6.getNodeType() == Node.ELEMENT_NODE) { Element element6 = (Element) child6; if (element6.getTagName().equals("property")) { if (element6.getAttribute("name").equalsIgnoreCase("CHEF:modifiedby")) { if ("BASE64".equalsIgnoreCase(element6.getAttribute("enc"))) { String creatorId = Xml.decodeAttribute(element6, "value"); if (!userListAllowImport.contains(creatorId)) goAhead = false; } else { String creatorId = element6.getAttribute("value"); if (!userListAllowImport.contains(creatorId)) goAhead = false; } } } } } } } } // for // then, go ahead to merge the content and assignment if (goAhead) { for (int i3 = 0; i3 < length3; i3++) { Node child3 = children3.item(i3); if (child3.getNodeType() == Node.ELEMENT_NODE) { Element element3 = (Element) child3; // add the properties childnode to the clone of assignment node if (element3.getTagName().equals("properties")) { // add the properties childnode to the clone of assignment node el2clone.appendChild(element3.cloneNode(true)); } else if (element3.getTagName().equals("content")) { // element3 now- content node // adjust the id of this content String newContentId = IdManager.createUuid(); element3.setAttribute("id", newContentId); element3.setAttribute("context", siteId); // clone the content node without the children of <properties> Element el3clone = (Element) element3.cloneNode(false); // update the assignmentcontent id in assignment node String assignContentId = "/assignment/c/" + siteId + "/" + newContentId; el2clone.setAttribute("assignmentcontent", assignContentId); // for content node, process the attachment or properties kids NodeList children5 = element3.getChildNodes(); int length5 = children5.getLength(); int attCount = 0; for (int i5 = 0; i5 < length5; i5++) { Node child5 = children5.item(i5); if (child5.getNodeType() == Node.ELEMENT_NODE) { Element element5 = (Element) child5; // for the node of "properties" if (element5.getTagName().equals("properties")) { // for the file from WT, preform userId translation when needed if (!userIdTrans.isEmpty()) { WTUserIdTrans(element3, userIdTrans); } } // for the node of properties el3clone.appendChild(element5.cloneNode(true)); // for "attachment" children if (element5.getTagName().equals("attachment")) { // map the attachment area folder name // filter out the invalid characters in the attachment id // map the attachment area folder name String oldUrl = element5.getAttribute("relative-url"); if (oldUrl.startsWith("/content/attachment/" + fromSiteId + "/")) { String newUrl = "/content/attachment/" + siteId + oldUrl.substring(("/content/attachment" + fromSiteId).length()); element5.setAttribute("relative-url", Validator.escapeQuestionMark(newUrl)); // transfer attachment, replace the context string and add new attachment if necessary newUrl = transferAttachment(fromSiteId, siteId, null, oldUrl.substring("/content".length())); element5.setAttribute("relative-url", Validator.escapeQuestionMark(newUrl)); newUrl = (String) attachmentNames.get(oldUrl); if (newUrl != null) { if (newUrl.startsWith("/attachment/")) newUrl = "/content".concat(newUrl); element5.setAttribute("relative-url", Validator .escapeQuestionMark(newUrl)); } } // map any references to this site to the new site id else if (oldUrl.startsWith("/content/group/" + fromSiteId + "/")) { String newUrl = "/content/group/" + siteId + oldUrl.substring(("/content/group/" + fromSiteId).length()); element5.setAttribute("relative-url", Validator.escapeQuestionMark(newUrl)); } // put the attachment back to the attribute field of content // to satisfy the input need of mergeAssignmentContent String attachmentString = "attachment" + attCount; el3clone.setAttribute(attachmentString, element5.getAttribute("relative-url")); attCount++; } // if } // if } // for // create a newassignment content contentEdit = mergeAssignmentContent(el3clone); commitEdit(contentEdit); } } } // for // when importing, refer to property to determine draft status if ("false".equalsIgnoreCase(m_serverConfigurationService.getString("import.importAsDraft"))) { String draftAttribute = el2clone.getAttribute("draft"); if (draftAttribute.equalsIgnoreCase("true") || draftAttribute.equalsIgnoreCase("false")) el2clone.setAttribute("draft", draftAttribute); else el2clone.setAttribute("draft", "true"); } else { el2clone.setAttribute("draft", "true"); } // merge in this assignment AssignmentEdit edit = mergeAssignment(el2clone); edit.setContent(contentEdit); commitEdit(edit); count++; } // if goAhead } // if } // if } // for } catch (Exception any) { M_log.warn(" merge(): exception: " + any.getMessage() + " siteId=" + siteId + " from site id=" + fromSiteId); } results.append("merging assignment " + siteId + " (" + count + ") assignments.\n"); return results.toString(); } // merge /** * {@inheritDoc} */ public String[] myToolIds() { String[] toolIds = { "sakai.assignment", "sakai.assignment.grades" }; return toolIds; } /** * {@inheritDoc} */ public void transferCopyEntities(String fromContext, String toContext, List resourceIds){ transferCopyEntitiesRefMigrator(fromContext, toContext, resourceIds); } public Map<String, String> transferCopyEntitiesRefMigrator(String fromContext, String toContext, List resourceIds) { Map<String, String> transversalMap = new HashMap<String, String>(); // import Assignment objects Iterator oAssignments = getAssignmentsForContext(fromContext); while (oAssignments.hasNext()) { Assignment oAssignment = (Assignment) oAssignments.next(); String oAssignmentId = oAssignment.getId(); boolean toBeImported = true; if (resourceIds != null && resourceIds.size() > 0) { // if there is a list for import assignments, only import those assignments and relative submissions toBeImported = false; for (int m = 0; m < resourceIds.size() && !toBeImported; m++) { if (((String) resourceIds.get(m)).equals(oAssignmentId)) { toBeImported = true; } } } if (toBeImported) { AssignmentEdit nAssignment = null; AssignmentContentEdit nContent = null; if (!m_assignmentStorage.check(oAssignmentId)) { } else { try { // add new Assignment content String oContentReference = oAssignment.getContentReference(); String oContentId = contentId(oContentReference); if (!m_contentStorage.check(oContentId)) throw new IdUnusedException(oContentId); else { AssignmentContent oContent = getAssignmentContent(oContentReference); nContent = addAssignmentContent(toContext); // attributes nContent.setAllowAttachments(oContent.getAllowAttachments()); nContent.setContext(toContext); nContent.setGroupProject(oContent.getGroupProject()); nContent.setHonorPledge(oContent.getHonorPledge()); nContent.setHideDueDate(oContent.getHideDueDate()); nContent.setIndividuallyGraded(oContent.individuallyGraded()); // replace all occurrence of old context with new context inside instruction text String instructions = oContent.getInstructions(); if (instructions.indexOf(fromContext) != -1) { instructions = instructions.replaceAll(fromContext, toContext); } nContent.setInstructions(instructions); nContent.setMaxGradePoint(oContent.getMaxGradePoint()); nContent.setReleaseGrades(oContent.releaseGrades()); nContent.setTimeLastModified(oContent.getTimeLastModified()); nContent.setTitle(oContent.getTitle()); nContent.setTypeOfGrade(oContent.getTypeOfGrade()); nContent.setTypeOfSubmission(oContent.getTypeOfSubmission()); // review service nContent.setAllowReviewService(oContent.getAllowReviewService()); // properties ResourcePropertiesEdit p = nContent.getPropertiesEdit(); p.clear(); p.addAll(oContent.getProperties()); // update live properties addLiveProperties(p); // attachment List oAttachments = oContent.getAttachments(); List nAttachments = m_entityManager.newReferenceList(); for (int n = 0; n < oAttachments.size(); n++) { Reference oAttachmentRef = (Reference) oAttachments.get(n); String oAttachmentId = ((Reference) oAttachments.get(n)).getId(); if (oAttachmentId.indexOf(fromContext) != -1) { // transfer attachment, replace the context string and add new attachment if necessary transferAttachment(fromContext, toContext, nAttachments, oAttachmentId); } else { nAttachments.add(oAttachmentRef); } } nContent.replaceAttachments(nAttachments); // complete the edit m_contentStorage.commit(nContent); ((BaseAssignmentContentEdit) nContent).closeEdit(); } } catch (Exception e) { if (M_log.isWarnEnabled()) M_log.warn(" transferCopyEntities " + e.toString() + " oAssignmentId=" + oAssignmentId); } if (nContent != null) { try { // add new assignment nAssignment = addAssignment(toContext); // attribute nAssignment.setCloseTime(oAssignment.getCloseTime()); nAssignment.setContentReference(nContent.getReference()); nAssignment.setContext(toContext); // when importing, refer to property to determine draft status if ("false".equalsIgnoreCase(m_serverConfigurationService.getString("import.importAsDraft"))) { nAssignment.setDraft(oAssignment.getDraft()); } else { nAssignment.setDraft(true); } nAssignment.setGroup(oAssignment.isGroup()); nAssignment.setDropDeadTime(oAssignment.getDropDeadTime()); nAssignment.setDueTime(oAssignment.getDueTime()); nAssignment.setOpenTime(oAssignment.getOpenTime()); nAssignment.setSection(oAssignment.getSection()); nAssignment.setTitle(oAssignment.getTitle()); nAssignment.setPosition_order(oAssignment.getPosition_order()); // properties ResourcePropertiesEdit p = nAssignment.getPropertiesEdit(); p.clear(); p.addAll(oAssignment.getProperties()); // one more touch on the gradebook-integration link String associatedGradebookAssignment = StringUtils.trimToNull(p.getProperty(PROP_ASSIGNMENT_ASSOCIATE_GRADEBOOK_ASSIGNMENT)); if (associatedGradebookAssignment != null) { // see if the old assignment's associated gradebook item is an internal gradebook entry or externally defined boolean isExternalAssignmentDefined = m_gradebookExternalAssessmentService.isExternalAssignmentDefined(oAssignment.getContent().getContext(), associatedGradebookAssignment); if (isExternalAssignmentDefined) { // if this is an external defined (came from assignment) // mark the link as "add to gradebook" for the new imported assignment, since the assignment is still of draft state //later when user posts the assignment, the corresponding assignment will be created in gradebook. p.removeProperty(PROP_ASSIGNMENT_ASSOCIATE_GRADEBOOK_ASSIGNMENT); p.addProperty(NEW_ASSIGNMENT_ADD_TO_GRADEBOOK, GRADEBOOK_INTEGRATION_ADD); } } // remove the link btw assignment and announcement item. One can announce the open date afterwards p.removeProperty(ResourceProperties.NEW_ASSIGNMENT_CHECK_AUTO_ANNOUNCE); p.removeProperty("new_assignment_open_date_announced"); p.removeProperty(ResourceProperties.PROP_ASSIGNMENT_OPENDATE_ANNOUNCEMENT_MESSAGE_ID); // remove the link btw assignment and calendar item. One can add the due date to calendar afterwards p.removeProperty(ResourceProperties.NEW_ASSIGNMENT_CHECK_ADD_DUE_DATE); p.removeProperty("new_assignment_due_date_scheduled"); p.removeProperty(ResourceProperties.PROP_ASSIGNMENT_DUEDATE_CALENDAR_EVENT_ID); // update live properties addLiveProperties(p); // complete the edit m_assignmentStorage.commit(nAssignment); ((BaseAssignmentEdit) nAssignment).closeEdit(); transversalMap.put("assignment/" + oAssignment.getId(), "assignment/" + nAssignment.getId()); M_log.info("old assignment id:"+oAssignment.getId()+" - new assignment id:"+nAssignment.getId()); try { if (m_taggingManager.isTaggable()) { for (TaggingProvider provider : m_taggingManager .getProviders()) { provider .transferCopyTags( m_assignmentActivityProducer .getActivity(oAssignment), m_assignmentActivityProducer .getActivity(nAssignment)); } } } catch (PermissionException pe) { M_log.error(this + " transferCopyEntities " + pe.toString() + " oAssignmentId=" + oAssignment.getId() + " nAssignmentId=" + nAssignment.getId()); } } catch (Exception ee) { M_log.error(this + " transferCopyEntities " + ee.toString() + " oAssignmentId=" + oAssignment.getId() + " nAssignmentId=" + nAssignment.getId()); } } } // if-else } // if } // for return transversalMap; } // importResources /** * manipulate the transfered attachment * @param fromContext * @param toContext * @param nAttachments * @param oAttachmentId * @return the new reference */ private String transferAttachment(String fromContext, String toContext, List nAttachments, String oAttachmentId) { String rv = ""; // replace old site id with new site id in attachments String nAttachmentId = oAttachmentId.replaceAll(fromContext, toContext); try { ContentResource attachment = m_contentHostingService.getResource(nAttachmentId); if (nAttachments != null) { nAttachments.add(m_entityManager.newReference(attachment.getReference())); } rv = attachment.getReference(); } catch (IdUnusedException e) { try { ContentResource oAttachment = m_contentHostingService.getResource(oAttachmentId); try { if (m_contentHostingService.isAttachmentResource(nAttachmentId)) { // add the new resource into attachment collection area ContentResource attachment = m_contentHostingService.addAttachmentResource( Validator.escapeResourceName(oAttachment.getProperties().getProperty(ResourceProperties.PROP_DISPLAY_NAME)), toContext, ToolManager.getTool("sakai.assignment.grades").getTitle(), oAttachment.getContentType(), oAttachment.getContent(), oAttachment.getProperties()); rv = attachment.getReference(); // add to attachment list if (nAttachments != null) { nAttachments.add(m_entityManager.newReference(rv)); } } else { // add the new resource into resource area ContentResource attachment = m_contentHostingService.addResource( Validator.escapeResourceName(oAttachment.getProperties().getProperty(ResourceProperties.PROP_DISPLAY_NAME)), toContext, 1, oAttachment.getContentType(), oAttachment.getContent(), oAttachment.getProperties(), NotificationService.NOTI_NONE); rv = attachment.getReference(); // add to attachment list if (nAttachments != null) { nAttachments.add(m_entityManager.newReference(rv)); } } } catch (Exception eeAny) { // if the new resource cannot be added M_log.warn(" transferCopyEntities: cannot add new attachment with id=" + nAttachmentId + " " + eeAny.getMessage()); } } catch (Exception eAny) { // if cannot find the original attachment, do nothing. M_log.warn(" transferCopyEntities: cannot find the original attachment with id=" + oAttachmentId + " " + eAny.getMessage()); } } catch (Exception any) { M_log.warn(" transferCopyEntities" + any.getMessage() + " oAttachmentId=" + oAttachmentId + " nAttachmentId=" + nAttachmentId); } return rv; } /** * {@inheritDoc} */ public String getEntityDescription(Reference ref) { String rv = "Assignment: " + ref.getReference(); try { // is it an AssignmentContent object if (REF_TYPE_CONTENT.equals(ref.getSubType())) { AssignmentContent c = getAssignmentContent(ref.getReference()); rv = "AssignmentContent: " + c.getId() + " (" + c.getContext() + ")"; } // is it an Assignment object else if (REF_TYPE_ASSIGNMENT.equals(ref.getSubType())) { Assignment a = getAssignment(ref.getReference()); rv = "Assignment: " + a.getId() + " (" + a.getContext() + ")"; } // is it an AssignmentSubmission object else if (REF_TYPE_SUBMISSION.equals(ref.getSubType())) { AssignmentSubmission s = getSubmission(ref.getReference()); rv = "AssignmentSubmission: " + s.getId() + " (" + s.getContext() + ")"; } else M_log.warn(" getEntityDescription(): unknown message ref subtype: " + ref.getSubType() + " in ref: " + ref.getReference()); } catch (PermissionException e) { M_log.warn(" getEntityDescription(): " + e.getMessage() + " ref=" + ref.getReference()); } catch (IdUnusedException e) { M_log.warn(" getEntityDescription(): " + e.getMessage() + " ref=" + ref.getReference()); } catch (NullPointerException e) { M_log.warn(" getEntityDescription(): " + e.getMessage() + " ref=" + ref.getReference()); } return rv; } /** * {@inheritDoc} */ public ResourceProperties getEntityResourceProperties(Reference ref) { ResourceProperties rv = null; try { // is it an AssignmentContent object if (REF_TYPE_CONTENT.equals(ref.getSubType())) { AssignmentContent c = getAssignmentContent(ref.getReference()); rv = c.getProperties(); } // is it an Assignment object else if (REF_TYPE_ASSIGNMENT.equals(ref.getSubType())) { Assignment a = getAssignment(ref.getReference()); rv = a.getProperties(); } // is it an AssignmentSubmission object else if (REF_TYPE_SUBMISSION.equals(ref.getSubType())) { AssignmentSubmission s = getSubmission(ref.getReference()); rv = s.getProperties(); } else M_log.warn(" getEntityResourceProperties: unknown message ref subtype: " + ref.getSubType() + " in ref: " + ref.getReference()); } catch (PermissionException e) { M_log.warn(" getEntityResourceProperties(): " + e.getMessage() + " ref=" + ref.getReference()); } catch (IdUnusedException e) { M_log.warn(" getEntityResourceProperties(): " + e.getMessage() + " ref=" + ref.getReference()); } catch (NullPointerException e) { M_log.warn(" getEntityResourceProperties(): " + e.getMessage() + " ref=" + ref.getReference()); } return rv; } /** * {@inheritDoc} */ public boolean canSubmit(String context, Assignment a) { // return false if not allowed to submit at all if (!allowAddSubmissionCheckGroups(context, a)) return false; String userId = SessionManager.getCurrentSessionUserId(); // if user can submit to this assignment List visibleAssignments = assignments(context, userId); if (visibleAssignments == null || !visibleAssignments.contains(a)) return false; try { // get user User u = UserDirectoryService.getUser(userId); Time currentTime = TimeService.newTime(); // return false if the assignment is draft or is not open yet Time openTime = a.getOpenTime(); if (a.getDraft() || (openTime != null && openTime.after(currentTime))) { return false; } // return false if the current time has passed the assignment close time Time closeTime = a.getCloseTime(); // get user's submission AssignmentSubmission submission = null; submission = getSubmission(a.getReference(), u); // check for allow resubmission or not first // return true if resubmission is allowed and current time is before resubmission close time // get the resubmit settings from submission object first String allowResubmitNumString = submission != null?submission.getProperties().getProperty(AssignmentSubmission.ALLOW_RESUBMIT_NUMBER):null; if (allowResubmitNumString != null && submission.getTimeSubmitted() != null) { try { int allowResubmitNumber = Integer.parseInt(allowResubmitNumString); String allowResubmitCloseTime = submission != null ? (String) submission.getProperties().getProperty(AssignmentSubmission.ALLOW_RESUBMIT_CLOSETIME):null; Time resubmitCloseTime = null; if (allowResubmitCloseTime != null) { // see if a resubmission close time is set on submission level resubmitCloseTime = TimeService.newTime(Long.parseLong(allowResubmitCloseTime)); } else { // otherwise, use assignment close time as the resubmission close time resubmitCloseTime = a.getCloseTime(); } return (allowResubmitNumber > 0 /* additional submission number allowed */ || allowResubmitNumber == -1 /* unlimited submission times allowed */) && resubmitCloseTime != null && currentTime.before(resubmitCloseTime); } catch (NumberFormatException e) { M_log.warn(" canSubmit(String, Assignment) " + e.getMessage() + " allowResubmitNumString=" + allowResubmitNumString); } } if (submission == null || (submission != null && submission.getTimeSubmitted() == null)) { // if there is no submission yet if (closeTime != null && currentTime.after(closeTime)) { return false; } else { return true; } } else { if (!submission.getSubmitted() && !(closeTime != null && currentTime.after(closeTime))) { // return true for drafted submissions return true; } else return false; } } catch (UserNotDefinedException e) { // cannot find user M_log.warn(" canSubmit(String, Assignment) " + e.getMessage() + " assignment ref=" + a.getReference()); return false; } } /********************************************************************************************************************************************************************************************************************************************************** * Assignment Implementation *********************************************************************************************************************************************************************************************************************************************************/ public class BaseAssignment implements Assignment { protected ResourcePropertiesEdit m_properties; protected String m_id; protected String m_assignmentContent; protected String m_title; protected String m_context; protected String m_section; protected Time m_visibleTime; protected Time m_openTime; protected Time m_dueTime; protected Time m_closeTime; protected Time m_dropDeadTime; protected List m_authors; protected boolean m_draft; protected boolean m_hideDueDate; protected boolean m_group; protected int m_position_order; /** The Collection of groups (authorization group id strings). */ protected Collection m_groups = new ArrayList(); /** The assignment access. */ protected AssignmentAccess m_access = AssignmentAccess.SITE; protected boolean m_allowPeerAssessment; protected Time m_peerAssessmentPeriodTime; protected boolean m_peerAssessmentAnonEval; protected boolean m_peerAssessmentStudentViewReviews; protected int m_peerAssessmentNumReviews; protected String m_peerAssessmentInstructions; /** * constructor */ public BaseAssignment() { m_properties = new BaseResourcePropertiesEdit(); }// constructor /** * Copy constructor */ public BaseAssignment(Assignment assignment) { setAll(assignment); }// copy constructor /** * Constructor used in addAssignment */ public BaseAssignment(String id, String context) { m_properties = new BaseResourcePropertiesEdit(); addLiveProperties(m_properties); m_id = id; m_assignmentContent = ""; m_title = ""; m_context = context; m_section = ""; m_authors = new ArrayList(); m_draft = true; m_hideDueDate = false; m_groups = new ArrayList(); m_position_order = 0; m_allowPeerAssessment = false; m_peerAssessmentPeriodTime = null; m_peerAssessmentAnonEval = false; m_peerAssessmentStudentViewReviews = false; m_peerAssessmentNumReviews = 0; m_peerAssessmentInstructions = null; } /** * Reads the Assignment's attribute values from xml. * * @param s - * Data structure holding the xml info. */ public BaseAssignment(Element el) { M_log.debug(" BASE ASSIGNMENT : ENTERING STORAGE CONSTRUCTOR"); m_properties = new BaseResourcePropertiesEdit(); int numAttributes = 0; String intString = null; String attributeString = null; String tempString = null; m_id = el.getAttribute("id"); M_log.debug(" BASE ASSIGNMENT : STORAGE CONSTRUCTOR : ASSIGNMENT ID : " + m_id); m_title = el.getAttribute("title"); m_section = el.getAttribute("section"); m_draft = getBool(el.getAttribute("draft")); m_hideDueDate = getBool(el.getAttribute("hideduedate")); m_group = getBool(el.getAttribute("group")); M_log.debug(" BASE ASSIGNMENT : STORAGE CONSTRUCTOR : READ THROUGH REG ATTS"); m_assignmentContent = el.getAttribute("assignmentcontent"); M_log.debug(" BASE ASSIGNMENT : STORAGE CONSTRUCTOR : CONTENT ID : " + m_assignmentContent); m_openTime = getTimeObject(el.getAttribute("opendate")); m_dueTime = getTimeObject(el.getAttribute("duedate")); m_visibleTime = getTimeObject(el.getAttribute("visibledate")); m_dropDeadTime = getTimeObject(el.getAttribute("dropdeaddate")); m_closeTime = getTimeObject(el.getAttribute("closedate")); m_context = el.getAttribute("context"); m_position_order = 0; // prevents null pointer if there is no position_order defined as well as helps with the sorting try { m_position_order = Long.valueOf(el.getAttribute("position_order")).intValue(); } catch (Exception e) { M_log.warn(": BaseAssignment(Element) " + e.getMessage()); } m_allowPeerAssessment = getBool(el.getAttribute("allowpeerassessment")); m_peerAssessmentPeriodTime = getTimeObject(el.getAttribute("peerassessmentperiodtime")); m_peerAssessmentAnonEval = getBool(el.getAttribute("peerassessmentanoneval")); m_peerAssessmentStudentViewReviews = getBool(el.getAttribute("peerassessmentstudentviewreviews")); String numReviews = el.getAttribute("peerassessmentnumreviews"); m_peerAssessmentNumReviews = 0; if(numReviews != null && !"".equals(numReviews)){ try{ m_peerAssessmentNumReviews = Integer.parseInt(numReviews); }catch(Exception e){} } m_peerAssessmentInstructions = el.getAttribute("peerassessmentinstructions"); // READ THE AUTHORS m_authors = new ArrayList(); intString = el.getAttribute("numberofauthors"); M_log.debug(" BASE ASSIGNMENT : STORAGE CONSTRUCTOR : number of authors : " + intString); try { numAttributes = Integer.parseInt(intString); for (int x = 0; x < numAttributes; x++) { M_log.debug(" BASE ASSIGNMENT : STORAGE CONSTRUCTOR : reading author # " + x); attributeString = "author" + x; tempString = el.getAttribute(attributeString); if (tempString != null) { M_log.debug(" BASE ASSIGNMENT : STORAGE CONSTRUCTOR : adding author # " + x + " id : " + tempString); m_authors.add(tempString); } } } catch (Exception e) { M_log.warn(" BASE ASSIGNMENT : STORAGE CONSTRUCTOR : Exception reading authors : " + e); } // READ THE PROPERTIES AND INSTRUCTIONS NodeList children = el.getChildNodes(); final 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; // look for properties if (element.getTagName().equals("properties")) { // re-create properties m_properties = new BaseResourcePropertiesEdit(element); } // look for an group else if (element.getTagName().equals("group")) { m_groups.add(element.getAttribute("authzGroup")); } } // extract access AssignmentAccess access = AssignmentAccess.fromString(el.getAttribute("access")); if (access != null) { m_access = access; } M_log.debug(" BASE ASSIGNMENT : LEAVING STORAGE CONSTRUCTOR"); }// storage constructor /** * @param services * @return */ public ContentHandler getContentHandler(Map<String, Object> services) { final Entity thisEntity = this; return new DefaultEntityHandler() { /* * (non-Javadoc) * * @see org.sakaiproject.util.DefaultEntityHandler#startElement(java.lang.String, * java.lang.String, java.lang.String, * org.xml.sax.Attributes) */ @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { if (doStartElement(uri, localName, qName, attributes)) { if ("assignment".equals(qName) && entity == null) { m_id = attributes.getValue("id"); m_properties = new BaseResourcePropertiesEdit(); int numAttributes = 0; String intString = null; String attributeString = null; String tempString = null; m_title = attributes.getValue("title"); m_section = attributes.getValue("section"); m_draft = getBool(attributes.getValue("draft")); m_hideDueDate = getBool(attributes.getValue("hideduedate")); m_group = getBool(attributes.getValue("group")); M_log.debug(this + " getContentHandler: READ THROUGH REG ATTS"); m_assignmentContent = attributes.getValue("assignmentcontent"); M_log.debug(this + " getContentHandler: STORAGE CONSTRUCTOR : CONTENT ID : " + m_assignmentContent); m_openTime = getTimeObject(attributes.getValue("opendate")); m_dueTime = getTimeObject(attributes.getValue("duedate")); m_visibleTime = getTimeObject(attributes.getValue("visibledate")); m_dropDeadTime = getTimeObject(attributes.getValue("dropdeaddate")); m_closeTime = getTimeObject(attributes.getValue("closedate")); m_context = attributes.getValue("context"); try { m_position_order = NumberUtils.toInt(attributes.getValue("position_order")); } catch (Exception e) { m_position_order = 0; // prevents null pointer if there is no position_order defined as well as helps with the sorting } m_allowPeerAssessment = getBool(attributes.getValue("allowpeerassessment")); m_peerAssessmentPeriodTime = getTimeObject(attributes.getValue("peerassessmentperiodtime")); m_peerAssessmentAnonEval = getBool(attributes.getValue("peerassessmentanoneval")); m_peerAssessmentStudentViewReviews = getBool(attributes.getValue("peerassessmentstudentviewreviews")); String numReviews = attributes.getValue("peerassessmentnumreviews"); m_peerAssessmentNumReviews = 0; if(numReviews != null && !"".equals(numReviews)){ try{ m_peerAssessmentNumReviews = Integer.parseInt(numReviews); }catch(Exception e){} } m_peerAssessmentInstructions = attributes.getValue("peerassessmentinstructions"); // READ THE AUTHORS m_authors = new ArrayList(); intString = attributes.getValue("numberofauthors"); try { numAttributes = Integer.parseInt(intString); for (int x = 0; x < numAttributes; x++) { attributeString = "author" + x; tempString = attributes.getValue(attributeString); if (tempString != null) { m_authors.add(tempString); } } } catch (Exception e) { M_log.warn(" BASE ASSIGNMENT getContentHandler startElement : Exception reading authors : " + e.toString()); } // extract access AssignmentAccess access = AssignmentAccess.fromString(attributes.getValue("access")); if (access != null) { m_access = access; } entity = thisEntity; } else if (GROUP_LIST.equals(qName)) { String groupRef = attributes.getValue(GROUP_NAME); if (groupRef != null) { m_groups.add(groupRef); } } else { M_log.debug(this + " BaseAssignment getContentHandler Unexpected Element " + qName); } } } }; } /** * Takes the Assignment's attribute values and puts them into the xml document. * * @param s - * Data structure holding the object to be stored. * @param doc - * The xml document. */ public Element toXml(Document doc, Stack stack) { M_log.debug(this + " BASE ASSIGNMENT : ENTERING TOXML"); Element assignment = doc.createElement("assignment"); if (stack.isEmpty()) { doc.appendChild(assignment); } else { ((Element) stack.peek()).appendChild(assignment); } stack.push(assignment); // SET ASSIGNMENT ATTRIBUTES String numItemsString = null; String attributeString = null; String itemString = null; // SAK-13408 -The XML implementation in Websphere throws an LSException if the // attribute is null, while in Tomcat it assumes an empty string. The following // sets the attribute to an empty string if the value is null. assignment.setAttribute("id", m_id == null ? "" : m_id); assignment.setAttribute("title", m_title == null ? "" : m_title); assignment.setAttribute("section", m_section == null ? "" : m_section); assignment.setAttribute("context", m_context == null ? "" : m_context); assignment.setAttribute("assignmentcontent", m_assignmentContent == null ? "" : m_assignmentContent); assignment.setAttribute("draft", getBoolString(m_draft)); assignment.setAttribute("group", getBoolString(m_group)); assignment.setAttribute("hideduedate", getBoolString(m_hideDueDate)); assignment.setAttribute("opendate", getTimeString(m_openTime)); assignment.setAttribute("duedate", getTimeString(m_dueTime)); assignment.setAttribute("visibledate", getTimeString(m_visibleTime)); assignment.setAttribute("dropdeaddate", getTimeString(m_dropDeadTime)); assignment.setAttribute("closedate", getTimeString(m_closeTime)); assignment.setAttribute("position_order", Long.valueOf(m_position_order).toString().trim()); assignment.setAttribute("allowpeerassessment", getBoolString(m_allowPeerAssessment)); assignment.setAttribute("peerassessmentperiodtime", getTimeString(m_peerAssessmentPeriodTime)); assignment.setAttribute("peerassessmentanoneval", getBoolString(m_peerAssessmentAnonEval)); assignment.setAttribute("peerassessmentstudentviewreviews", getBoolString(m_peerAssessmentStudentViewReviews)); assignment.setAttribute("peerassessmentnumreviews", "" + m_peerAssessmentNumReviews); assignment.setAttribute("peerassessmentinstructions", m_peerAssessmentInstructions); M_log.debug(this + " BASE ASSIGNMENT : TOXML : saved regular properties"); // SAVE THE AUTHORS numItemsString = "" + m_authors.size(); M_log.debug(this + " BASE ASSIGNMENT : TOXML : saving " + numItemsString + " authors"); assignment.setAttribute("numberofauthors", numItemsString); for (int x = 0; x < m_authors.size(); x++) { attributeString = "author" + x; itemString = (String) m_authors.get(x); if (itemString != null) { assignment.setAttribute(attributeString, itemString); M_log.debug(this + " BASE ASSIGNMENT : TOXML : saving author : " + itemString); } } // add groups if ((m_groups != null) && (m_groups.size() > 0)) { for (Iterator i = m_groups.iterator(); i.hasNext();) { String group = (String) i.next(); Element sect = doc.createElement("group"); assignment.appendChild(sect); sect.setAttribute("authzGroup", group); } } // add access assignment.setAttribute("access", m_access.toString()); // SAVE THE PROPERTIES m_properties.toXml(doc, stack); M_log.debug(this + " BASE ASSIGNMENT : TOXML : SAVED PROPERTIES"); stack.pop(); M_log.debug("ASSIGNMENT : BASE ASSIGNMENT : LEAVING TOXML"); return assignment; }// toXml protected void setAll(Assignment assignment) { if (assignment != null) { m_id = assignment.getId(); m_assignmentContent = assignment.getContentReference(); m_authors = assignment.getAuthors(); m_title = assignment.getTitle(); m_context = assignment.getContext(); m_section = assignment.getSection(); m_openTime = assignment.getOpenTime(); m_dueTime = assignment.getDueTime(); m_visibleTime = assignment.getVisibleTime(); m_closeTime = assignment.getCloseTime(); m_dropDeadTime = assignment.getDropDeadTime(); m_draft = assignment.getDraft(); m_group = assignment.isGroup(); m_position_order = 0; try { m_position_order = assignment.getPosition_order(); } catch (Exception e) { M_log.warn(": setAll(Assignment) get position order " + e.getMessage()); } m_properties = new BaseResourcePropertiesEdit(); m_properties.addAll(assignment.getProperties()); m_groups = assignment.getGroups(); m_access = assignment.getAccess(); m_allowPeerAssessment = assignment.getAllowPeerAssessment(); m_peerAssessmentPeriodTime = assignment.getPeerAssessmentPeriod(); m_peerAssessmentAnonEval = assignment.getPeerAssessmentAnonEval(); m_peerAssessmentStudentViewReviews = assignment.getPeerAssessmentStudentViewReviews(); m_peerAssessmentNumReviews = assignment.getPeerAssessmentNumReviews(); m_peerAssessmentInstructions = assignment.getPeerAssessmentInstructions(); } } public String getId() { return m_id; } /** * Access the URL which can be used to access the resource. * * @return The URL which can be used to access the resource. */ public String getUrl() { return getAccessPoint(false) + Entity.SEPARATOR + "a" + Entity.SEPARATOR + m_context + Entity.SEPARATOR + m_id; } // getUrl /** * Access the internal reference which can be used to access the resource from within the system. * * @return The the internal reference which can be used to access the resource from within the system. */ public String getReference() { return assignmentReference(m_context, m_id); } // getReference /** * @inheritDoc */ public String getReference(String rootProperty) { return getReference(); } /** * @inheritDoc */ public String getUrl(String rootProperty) { return getUrl(); } /** * Access the resource's properties. * * @return The resource's properties. */ public ResourceProperties getProperties() { return m_properties; } /** * Access the list of authors. * * @return FlexStringArray of user ids. */ public List getAuthors() { return m_authors; } /** * Add an author to the author list. * * @param author - * The User to add to the author list. */ public void addAuthor(User author) { if (author != null) m_authors.add(author.getId()); } /** * Remove an author from the author list. * * @param author - * the User to remove from the author list. */ public void removeAuthor(User author) { if (author != null) m_authors.remove(author.getId()); } /** * Access the creator of this object. * * @return String The creator's user id. */ public String getCreator() { return m_properties.getProperty(ResourceProperties.PROP_CREATOR); } /** * Access the person of last modificaiton * * @return the User's Id */ public String getAuthorLastModified() { return m_properties.getProperty(ResourceProperties.PROP_MODIFIED_BY); } /** * Access the title. * * @return The Assignment's title. */ public String getTitle() { return m_title; } public Time getPeerAssessmentPeriod() { return m_peerAssessmentPeriodTime; } public boolean getPeerAssessmentAnonEval(){ return m_peerAssessmentAnonEval; } public boolean getPeerAssessmentStudentViewReviews(){ return m_peerAssessmentStudentViewReviews; } public int getPeerAssessmentNumReviews(){ return m_peerAssessmentNumReviews; } public String getPeerAssessmentInstructions(){ return m_peerAssessmentInstructions; } public boolean getAllowPeerAssessment() { return m_allowPeerAssessment; } /** * peer assessment is set for this assignment and the current time * falls between the assignment close time and the peer asseessment period time * @return */ public boolean isPeerAssessmentOpen(){ if(getAllowPeerAssessment()){ Time now = TimeService.newTime(); return now.before(getPeerAssessmentPeriod()) && now.after(getCloseTime()); }else{ return false; } } /** * peer assessment is set for this assignment but the close time hasn't passed * @return */ public boolean isPeerAssessmentPending(){ if(getAllowPeerAssessment()){ Time now = TimeService.newTime(); return now.before(getCloseTime()); }else{ return false; } } /** * peer assessment is set for this assignment but the current time is passed * the peer assessment period * @return */ public boolean isPeerAssessmentClosed(){ if(getAllowPeerAssessment()){ Time now = TimeService.newTime(); return now.after(getPeerAssessmentPeriod()); }else{ return false; } } /** * @inheritDoc */ public String getStatus() { Time currentTime = TimeService.newTime(); if (this.getDraft()) return rb.getString("gen.dra1"); else if (this.getOpenTime().after(currentTime)) return rb.getString("gen.notope"); else if (this.getDueTime().after(currentTime)) return rb.getString("gen.open"); else if ((this.getCloseTime() != null) && (this.getCloseTime().before(currentTime))) return rb.getString("gen.closed"); else return rb.getString("gen.due"); } /** * Access the time that this object was created. * * @return The Time object representing the time of creation. */ public Time getTimeCreated() { try { return m_properties.getTimeProperty(ResourceProperties.PROP_CREATION_DATE); } catch (EntityPropertyNotDefinedException e) { M_log.warn(":getTimeCreated() no time property defined " + e.getMessage()); } catch (EntityPropertyTypeException e) { M_log.warn(":getTimeCreated() no time property defined " + e.getMessage()); } return null; } /** * Access the time of last modificaiton. * * @return The Time of last modification. */ public Time getTimeLastModified() { try { return m_properties.getTimeProperty(ResourceProperties.PROP_MODIFIED_DATE); } catch (EntityPropertyNotDefinedException e) { M_log.warn(":getTimeLastModified() no time property defined " + e.getMessage()); } catch (EntityPropertyTypeException e) { M_log.warn(":getTimeLastModified() no time property defined " + e.getMessage()); } return null; } /** * Access the AssignmentContent of this Assignment. * * @return The Assignment's AssignmentContent. */ public AssignmentContent getContent() { AssignmentContent retVal = null; if (m_assignmentContent != null) { try { retVal = getAssignmentContent(m_assignmentContent); } catch (Exception e) { M_log.warn(":getContent() " + e.getMessage()); } } return retVal; } /** * Access the reference of the AssignmentContent of this Assignment. * * @return The Assignment's reference. */ public String getContentReference() { return m_assignmentContent; } /** * Access the id of the Assignment's group. * * @return The id of the group for which this Assignment is designed. */ public String getContext() { return m_context; } /** * Access the section info * * @return The section String */ public String getSection() { return m_section; } /** * Access the first time at which the assignment can be viewed; may be null. * * @return The Time at which the assignment is due, or null if unspecified. */ public Time getOpenTime() { return m_openTime; } /** * @inheritDoc */ public String getOpenTimeString() { if ( m_openTime == null ) return ""; else return m_openTime.toStringLocalFull(); } /** * Access the time at which the assignment is due; may be null. * * @return The Time at which the Assignment is due, or null if unspecified. */ public Time getDueTime() { return m_dueTime; } /** * Access the time at which the assignment is visible; may be null. * * @return The Time at which the Assignment is visible, or null if unspecified. */ public Time getVisibleTime() { return m_visibleTime; } /** * @inheritDoc */ public String getDueTimeString() { if ( m_dueTime == null ) return ""; else return m_dueTime.toStringLocalFull(); } public String getVisibleTimeString() { if ( m_visibleTime == null ) return ""; else return m_visibleTime.toStringLocalFull(); } /** * Access the drop dead time after which responses to this assignment are considered late; may be null. * * @return The Time object representing the drop dead time, or null if unspecified. */ public Time getDropDeadTime() { return m_dropDeadTime; } /** * @inheritDoc */ public String getDropDeadTimeString() { if ( m_dropDeadTime == null ) return ""; else return m_dropDeadTime.toStringLocalFull(); } /** * Access the close time after which this assignment can no longer be viewed, and after which submissions will not be accepted. May be null. * * @return The Time after which the Assignment is closed, or null if unspecified. */ public Time getCloseTime() { if (m_closeTime == null) { m_closeTime = m_dueTime; } return m_closeTime; } /** * @inheritDoc */ public String getCloseTimeString() { if ( m_closeTime == null ) return ""; else return m_closeTime.toStringLocalFull(); } /** * Get whether this is a draft or final copy. * * @return True if this is a draft, false if it is a final copy. */ public boolean getDraft() { return m_draft; } public boolean getHideDueDate() { return m_hideDueDate; } public boolean isGroup() { return m_group; } /** * Access the position order. * * @return The Assignment's positionorder. */ public int getPosition_order() { return m_position_order; } /** * @inheritDoc */ public Collection getGroups() { return new ArrayList(m_groups); } /** * @inheritDoc */ public AssignmentAccess getAccess() { return m_access; } /** * Are these objects equal? If they are both Assignment objects, and they have matching id's, they are. * * @return true if they are equal, false if not. */ public boolean equals(Object obj) { if (!(obj instanceof Assignment)) return false; return ((Assignment) obj).getId().equals(getId()); } // equals /** * Make a hash code that reflects the equals() logic as well. We want two objects, even if different instances, if they have the same id to hash the same. */ public int hashCode() { return getId().hashCode(); } // hashCode /** * Compare this object with the specified object for order. * * @return A negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object. */ public int compareTo(Object obj) { if (!(obj instanceof Assignment)) throw new ClassCastException(); // if the object are the same, say so if (obj == this) return 0; // start the compare by comparing their sort names int compare = getTitle().compareTo(((Assignment) obj).getTitle()); // if these are the same if (compare == 0) { // sort based on (unique) id compare = getId().compareTo(((Assignment) obj).getId()); } return compare; } // compareTo } // BaseAssignment /********************************************************************************************************************************************************************************************************************************************************** * AssignmentEdit implementation *********************************************************************************************************************************************************************************************************************************************************/ /** * <p> * BaseAssignmentEdit is an implementation of the CHEF AssignmentEdit object. * </p> * * @author University of Michigan, CHEF Software Development Team */ public class BaseAssignmentEdit extends BaseAssignment implements AssignmentEdit, SessionBindingListener { /** The event code for this edit. */ protected String m_event = null; /** Active flag. */ protected boolean m_active = false; /** * Construct from another Assignment object. * * @param Assignment * The Assignment object to use for values. */ public BaseAssignmentEdit(Assignment assignment) { super(assignment); } // BaseAssignmentEdit /** * Construct. * * @param id * The assignment id. */ public BaseAssignmentEdit(String id, String context) { super(id, context); } // BaseAssignmentEdit /** * Construct from information in XML. * * @param el * The XML DOM Element definining the Assignment. */ public BaseAssignmentEdit(Element el) { super(el); } // BaseAssignmentEdit /** * Clean up. */ protected void finalize() { // catch the case where an edit was made but never resolved if (m_active) { cancelEdit(this); } } // finalize /** * Set the title. * * @param title - * The Assignment's title. */ public void setTitle(String title) { m_title = title; } public void setPeerAssessmentPeriod(Time time) { m_peerAssessmentPeriodTime = time; } public void setAllowPeerAssessment(boolean allow) { m_allowPeerAssessment = allow; } public void setPeerAssessmentAnonEval(boolean anonEval){ m_peerAssessmentAnonEval = anonEval; } public void setPeerAssessmentStudentViewReviews(boolean studentViewReviews){ m_peerAssessmentStudentViewReviews = studentViewReviews; } public void setPeerAssessmentNumReviews(int numReviews){ m_peerAssessmentNumReviews = numReviews; } public void setPeerAssessmentInstructions(String instructions){ m_peerAssessmentInstructions = instructions; } /** * Set the reference of the AssignmentContent of this Assignment. * * @param String - * the reference of the AssignmentContent. */ public void setContentReference(String contentReference) { if (contentReference != null) m_assignmentContent = contentReference; } /** * Set the AssignmentContent of this Assignment. * * @param content - * the Assignment's AssignmentContent. */ public void setContent(AssignmentContent content) { if (content != null) m_assignmentContent = content.getReference(); } /** * Set the context at the time of creation. * * @param context - * the context string. */ public void setContext(String context) { m_context = context; } /** * Set the section info * * @param sectionId - * The section id */ public void setSection(String sectionId) { m_section = sectionId; } /** * Set the first time at which the assignment can be viewed; may be null. * * @param opentime - * The Time at which the Assignment opens. */ public void setOpenTime(Time opentime) { m_openTime = opentime; } /** * Set the time at which the assignment is due; may be null. * * @param dueTime - * The Time at which the Assignment is due. */ public void setDueTime(Time duetime) { m_dueTime = duetime; } /** * Set the time at which the assignment is visible; may be null. * * @param visibleTime - * The Time at which the Assignment is visible */ public void setVisibleTime(Time visibletime) { m_visibleTime = visibletime; } /** * Set the drop dead time after which responses to this assignment are considered late; may be null. * * @param dropdeadtime - * The Time object representing the drop dead time. */ public void setDropDeadTime(Time dropdeadtime) { m_dropDeadTime = dropdeadtime; } /** * Set the time after which this assignment can no longer be viewed, and after which submissions will not be accepted. May be null. * * @param closetime - * The Time after which the Assignment is closed, or null if unspecified. */ public void setCloseTime(Time closetime) { m_closeTime = closetime; } /** * Set whether this is a draft or final copy. * * @param draft - * true if this is a draft, false if it is a final copy. */ public void setDraft(boolean draft) { m_draft = draft; } public void setHideDueDate (boolean hide) { m_hideDueDate = hide; } public void setGroup(boolean group) { m_group = group; } /** * Set the position order field for the an assignment. * * @param position_order - * The position order. */ public void setPosition_order(int position_order) { m_position_order = position_order; } /** * Take all values from this object. * * @param user * The user object to take values from. */ protected void set(Assignment assignment) { setAll(assignment); } // set /** * Access the event code for this edit. * * @return The event code for this edit. */ protected String getEvent() { return m_event; } /** * Set the event code for this edit. * * @param event * The event code for this edit. */ protected void setEvent(String event) { m_event = event; } /** * Access the resource's properties for modification * * @return The resource's properties. */ public ResourcePropertiesEdit getPropertiesEdit() { return m_properties; } // getPropertiesEdit /** * Enable editing. */ protected void activate() { m_active = true; } // activate /** * Check to see if the edit is still active, or has already been closed. * * @return true if the edit is active, false if it's been closed. */ public boolean isActiveEdit() { return m_active; } // isActiveEdit /** * Close the edit object - it cannot be used after this. */ protected void closeEdit() { m_active = false; } // closeEdit /****************************************************************************************************************************************************************************************************************************************************** * Group awareness implementation *****************************************************************************************************************************************************************************************************************************************************/ /** * @inheritDoc */ public void setAccess(AssignmentAccess access) { m_access = access; } /** * @inheritDoc */ public void setGroupAccess(Collection groups) throws PermissionException { // convenience (and what else are we going to do?) if ((groups == null) || (groups.size() == 0)) { clearGroupAccess(); return; } // is there any change? If we are already grouped, and the group list is the same, ignore the call if ((m_access == AssignmentAccess.GROUPED) && (EntityCollections.isEqualEntityRefsToEntities(m_groups, groups))) return; // there should not be a case where there's no context if (m_context == null) { M_log.warn(" setGroupAccess() called with null context: " + getReference()); throw new PermissionException(SessionManager.getCurrentSessionUserId(), "access:site", getReference()); } // isolate any groups that would be removed or added Collection addedGroups = new ArrayList(); Collection removedGroups = new ArrayList(); EntityCollections.computeAddedRemovedEntityRefsFromNewEntitiesOldRefs(addedGroups, removedGroups, groups, m_groups); // verify that the user has permission to remove if (removedGroups.size() > 0) { // the Group objects the user has remove permission Collection allowedGroups = getGroupsAllowRemoveAssignment(m_context); for (Iterator i = removedGroups.iterator(); i.hasNext();) { String ref = (String) i.next(); // is ref a group the user can remove from? if (!EntityCollections.entityCollectionContainsRefString(allowedGroups, ref)) { throw new PermissionException(SessionManager.getCurrentSessionUserId(), "access:group:remove", ref); } } } // verify that the user has permission to add in those contexts if (addedGroups.size() > 0) { // the Group objects the user has add permission Collection allowedGroups = getGroupsAllowAddAssignment(m_context); for (Iterator i = addedGroups.iterator(); i.hasNext();) { String ref = (String) i.next(); // is ref a group the user can remove from? if (!EntityCollections.entityCollectionContainsRefString(allowedGroups, ref)) { throw new PermissionException(SessionManager.getCurrentSessionUserId(), "access:group:add", ref); } } } // we are clear to perform this m_access = AssignmentAccess.GROUPED; EntityCollections.setEntityRefsFromEntities(m_groups, groups); } /** * @inheritDoc */ public void clearGroupAccess() throws PermissionException { // is there any change? If we are already site, ignore the call if (m_access == AssignmentAccess.SITE) { m_groups.clear(); return; } if (m_context == null) { // there should not be a case where there's no context M_log.warn(" clearGroupAccess() called with null context. " + getReference()); throw new PermissionException(SessionManager.getCurrentSessionUserId(), "access:site", getReference()); } else { // verify that the user has permission to add in the site context if (!allowAddSiteAssignment(m_context)) { throw new PermissionException(SessionManager.getCurrentSessionUserId(), "access:site", getReference()); } } // we are clear to perform this m_access = AssignmentAccess.SITE; m_groups.clear(); } /****************************************************************************************************************************************************************************************************************************************************** * SessionBindingListener implementation *****************************************************************************************************************************************************************************************************************************************************/ public void valueBound(SessionBindingEvent event) { } public void valueUnbound(SessionBindingEvent event) { M_log.debug(this + " BaseAssignmentEdit valueUnbound()"); // catch the case where an edit was made but never resolved if (m_active) { cancelEdit(this); } } // valueUnbound } // BaseAssignmentEdit /********************************************************************************************************************************************************************************************************************************************************** * AssignmentContent Implementation *********************************************************************************************************************************************************************************************************************************************************/ public class BaseAssignmentContent implements AssignmentContent { protected ResourcePropertiesEdit m_properties; protected String m_id; protected String m_context; protected List m_attachments; protected List m_authors; protected String m_title; protected String m_instructions; protected int m_honorPledge; protected int m_typeOfSubmission; protected int m_typeOfGrade; protected int m_maxGradePoint; protected boolean m_groupProject; protected boolean m_individuallyGraded; protected boolean m_releaseGrades; protected boolean m_hideDueDate; protected boolean m_allowAttachments; protected boolean m_allowReviewService; protected boolean m_allowStudentViewReport; String m_submitReviewRepo; String m_generateOriginalityReport; boolean m_checkTurnitin = true; boolean m_checkInternet = true; boolean m_checkPublications = true; boolean m_checkInstitution = true; boolean m_excludeBibliographic = true; boolean m_excludeQuoted = true; int m_excludeType = 0; int m_excludeValue = 1; protected Time m_timeCreated; protected Time m_timeLastModified; /** * constructor */ public BaseAssignmentContent() { m_properties = new BaseResourcePropertiesEdit(); }// constructor /** * Copy constructor. */ public BaseAssignmentContent(AssignmentContent content) { setAll(content); } /** * Constructor used in addAssignmentContent. */ public BaseAssignmentContent(String id, String context) { m_id = id; m_context = context; m_properties = new BaseResourcePropertiesEdit(); addLiveProperties(m_properties); m_authors = new ArrayList(); m_attachments = m_entityManager.newReferenceList(); m_title = ""; m_instructions = ""; m_honorPledge = Assignment.HONOR_PLEDGE_NOT_SET; m_typeOfSubmission = Assignment.ASSIGNMENT_SUBMISSION_TYPE_NOT_SET; m_typeOfGrade = Assignment.GRADE_TYPE_NOT_SET; m_maxGradePoint = 0; m_timeCreated = TimeService.newTime(); m_timeLastModified = TimeService.newTime(); } /** * Reads the AssignmentContent's attribute values from xml. * * @param s - * Data structure holding the xml info. */ public BaseAssignmentContent(Element el) { int numAttributes = 0; String intString = null; String attributeString = null; String tempString = null; Reference tempReference = null; M_log.debug(" BaseAssignmentContent : Entering read"); m_id = el.getAttribute("id"); m_context = el.getAttribute("context"); m_title = el.getAttribute("title"); m_groupProject = getBool(el.getAttribute("groupproject")); m_individuallyGraded = getBool(el.getAttribute("indivgraded")); m_releaseGrades = getBool(el.getAttribute("releasegrades")); m_allowAttachments = getBool(el.getAttribute("allowattach")); m_hideDueDate = getBool(el.getAttribute("hideduedate")); m_allowReviewService = getBool(el.getAttribute("allowreview")); m_allowStudentViewReport = getBool(el.getAttribute("allowstudentview")); m_submitReviewRepo = el.getAttribute("submitReviewRepo"); m_generateOriginalityReport = el.getAttribute("generateOriginalityReport"); m_checkTurnitin = getBool(el.getAttribute("checkTurnitin")); m_checkInternet = getBool(el.getAttribute("checkInternet")); m_checkPublications = getBool(el.getAttribute("checkPublications")); m_checkInstitution = getBool(el.getAttribute("checkInstitution")); m_excludeBibliographic = getBool(el.getAttribute("excludeBibliographic")); m_excludeQuoted = getBool(el.getAttribute("excludeQuoted")); String excludeTypeStr = el.getAttribute("excludeType"); try{ m_excludeType = Integer.parseInt(excludeTypeStr); if(m_excludeType != 0 && m_excludeType != 1 && m_excludeType != 2){ m_excludeType = 0; } }catch (Exception e) { m_excludeType = 0; } String excludeValueStr = el.getAttribute("excludeValue"); try{ m_excludeValue = Integer.parseInt(excludeValueStr); if(m_excludeValue < 0 || m_excludeValue > 100){ m_excludeValue = 1; } }catch (Exception e) { m_excludeValue = 1; } m_timeCreated = getTimeObject(el.getAttribute("datecreated")); m_timeLastModified = getTimeObject(el.getAttribute("lastmod")); m_instructions = FormattedText.decodeFormattedTextAttribute(el, "instructions"); try { m_honorPledge = Integer.parseInt(el.getAttribute("honorpledge")); } catch (Exception e) { M_log.warn(" BaseAssignmentContent Exception parsing honor pledge int from xml file string : " + e); } try { m_typeOfSubmission = Integer.parseInt(el.getAttribute("submissiontype")); } catch (Exception e) { M_log.warn(" BaseAssignmentContent Exception parsing submission type int from xml file string : " + e); } try { m_typeOfGrade = Integer.parseInt(el.getAttribute("typeofgrade")); } catch (Exception e) { M_log.warn(" BaseAssignmentContent Exception parsing grade type int from xml file string : " + e); } try { // %%%zqian // read the scaled max grade point first; if there is none, get the old max grade value and multiple by 10 String maxGradePoint = StringUtils.trimToNull(el.getAttribute("scaled_maxgradepoint")); if (maxGradePoint == null) { maxGradePoint = StringUtils.trimToNull(el.getAttribute("maxgradepoint")); if (maxGradePoint != null) { maxGradePoint = maxGradePoint + "0"; } } if (maxGradePoint != null) { m_maxGradePoint = Integer.parseInt(maxGradePoint); } } catch (Exception e) { M_log.warn(" BaseAssignmentContent Exception parsing maxgradepoint int from xml file string : " + e); } // READ THE AUTHORS m_authors = new ArrayList(); intString = el.getAttribute("numberofauthors"); try { numAttributes = Integer.parseInt(intString); for (int x = 0; x < numAttributes; x++) { attributeString = "author" + x; tempString = el.getAttribute(attributeString); if (tempString != null) m_authors.add(tempString); } } catch (Exception e) { M_log.warn(" BaseAssignmentContent: Exception reading authors : " + e); } // READ THE ATTACHMENTS m_attachments = m_entityManager.newReferenceList(); M_log.debug(" BaseAssignmentContent: Reading attachments : "); intString = el.getAttribute("numberofattachments"); M_log.debug(" BaseAssignmentContent: num attachments : " + intString); try { numAttributes = Integer.parseInt(intString); for (int x = 0; x < numAttributes; x++) { attributeString = "attachment" + x; tempString = el.getAttribute(attributeString); if (tempString != null) { tempReference = m_entityManager.newReference(tempString); m_attachments.add(tempReference); M_log.debug(" BaseAssignmentContent: " + attributeString + " : " + tempString); } } } catch (Exception e) { M_log.warn(" BaseAssignmentContent: Exception reading attachments : " + e); } // READ THE PROPERTIES NodeList children = el.getChildNodes(); final 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; // look for properties if (element.getTagName().equals("properties")) { // re-create properties m_properties = new BaseResourcePropertiesEdit(element); } // old style of encoding else if (element.getTagName().equals("instructions-html") || element.getTagName().equals("instructions-formatted") || element.getTagName().equals("instructions")) { if ((element.getChildNodes() != null) && (element.getChildNodes().item(0) != null)) { m_instructions = element.getChildNodes().item(0).getNodeValue(); if (element.getTagName().equals("instructions")) m_instructions = FormattedText.convertPlaintextToFormattedText(m_instructions); if (element.getTagName().equals("instructions-formatted")) m_instructions = FormattedText.convertOldFormattedText(m_instructions); M_log.debug(" BaseAssignmentContent(Element): instructions : " + m_instructions); } if (m_instructions == null) { m_instructions = ""; } } } M_log.debug(" BaseAssignmentContent(Element): LEAVING STORAGE CONSTRUTOR"); }// storage constructor /** * @param services * @return */ public ContentHandler getContentHandler(Map<String, Object> services) { final Entity thisEntity = this; return new DefaultEntityHandler() { /* * (non-Javadoc) * * @see org.sakaiproject.util.DefaultEntityHandler#startElement(java.lang.String, * java.lang.String, java.lang.String, * org.xml.sax.Attributes) */ @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { if (doStartElement(uri, localName, qName, attributes)) { if ("content".equals(qName) && entity == null) { int numAttributes = 0; String intString = null; String attributeString = null; String tempString = null; Reference tempReference = null; m_id = attributes.getValue("id"); m_context = attributes.getValue("context"); m_title = attributes.getValue("title"); m_groupProject = getBool(attributes.getValue("groupproject")); m_individuallyGraded = getBool(attributes.getValue("indivgraded")); m_releaseGrades = getBool(attributes.getValue("releasegrades")); m_allowAttachments = getBool(attributes.getValue("allowattach")); m_hideDueDate = getBool(attributes.getValue("hideduedate")); m_allowReviewService = getBool(attributes.getValue("allowreview")); m_allowStudentViewReport = getBool(attributes.getValue("allowstudentview")); m_submitReviewRepo = attributes.getValue("submitReviewRepo"); m_generateOriginalityReport = attributes.getValue("generateOriginalityReport"); m_checkTurnitin = getBool(attributes.getValue("checkTurnitin")); m_checkInternet = getBool(attributes.getValue("checkInternet")); m_checkPublications = getBool(attributes.getValue("checkPublications")); m_checkInstitution = getBool(attributes.getValue("checkInstitution")); m_excludeBibliographic = getBool(attributes.getValue("excludeBibliographic")); m_excludeQuoted = getBool(attributes.getValue("excludeQuoted")); String excludeTypeStr = attributes.getValue("excludeType"); try{ m_excludeType = Integer.parseInt(excludeTypeStr); if(m_excludeType != 0 && m_excludeType != 1 && m_excludeType != 2){ m_excludeType = 0; } }catch (Exception e) { m_excludeType = 0; } String excludeValueStr = attributes.getValue("excludeValue"); try{ m_excludeValue = Integer.parseInt(excludeValueStr); if(m_excludeValue < 0 || m_excludeValue > 100){ m_excludeValue = 1; } }catch (Exception e) { m_excludeValue = 1; } m_timeCreated = getTimeObject(attributes.getValue("datecreated")); m_timeLastModified = getTimeObject(attributes.getValue("lastmod")); m_instructions = formattedTextDecodeFormattedTextAttribute(attributes, "instructions"); try { m_honorPledge = Integer.parseInt(attributes.getValue("honorpledge")); } catch (Exception e) { M_log.warn(" getContentHandler startElement Exception parsing honor pledge int from xml file string : " + e); } try { m_typeOfSubmission = Integer.parseInt(attributes.getValue("submissiontype")); } catch (Exception e) { M_log.warn(" getContentHandler startElement Exception parsing submission type int from xml file string : " + e); } try { m_typeOfGrade = Integer.parseInt(attributes.getValue("typeofgrade")); } catch (Exception e) { M_log.warn(" getContentHandler startElement Exception parsing grade type int from xml file string : " + e); } try { // %%%zqian // read the scaled max grade point first; if there is none, get the old max grade value and multiple by 10 String maxGradePoint = StringUtils.trimToNull(attributes.getValue("scaled_maxgradepoint")); if (maxGradePoint == null) { maxGradePoint = StringUtils.trimToNull(attributes.getValue("maxgradepoint")); if (maxGradePoint != null) { maxGradePoint = maxGradePoint + "0"; } } m_maxGradePoint = maxGradePoint != null ? Integer.parseInt(maxGradePoint) : m_maxGradePoint; } catch (Exception e) { M_log.warn(" getContentHandler startElement Exception parsing maxgradepoint int from xml file string : " + e); } // READ THE AUTHORS m_authors = new ArrayList(); intString = attributes.getValue("numberofauthors"); try { numAttributes = Integer.parseInt(intString); for (int x = 0; x < numAttributes; x++) { attributeString = "author" + x; tempString = attributes.getValue(attributeString); if (tempString != null) m_authors.add(tempString); } } catch (Exception e) { M_log.warn(" getContentHandler startElement Exception reading authors : " + e); } // READ THE ATTACHMENTS m_attachments = m_entityManager.newReferenceList(); intString = attributes.getValue("numberofattachments"); try { numAttributes = Integer.parseInt(intString); for (int x = 0; x < numAttributes; x++) { attributeString = "attachment" + x; tempString = attributes.getValue(attributeString); if (tempString != null) { tempReference = m_entityManager.newReference(tempString); m_attachments.add(tempReference); } } } catch (Exception e) { M_log.warn(" getContentHandler startElement DbCachedContent : Exception reading attachments : " + e); } entity = thisEntity; } else { M_log.warn(" getContentHandler startElement Unexpected Element " + qName); } } } }; } /** * Takes the AssignmentContent's attribute values and puts them into the xml document. * * @param s - * Data structure holding the object to be stored. * @param doc - * The xml document. */ public Element toXml(Document doc, Stack stack) { M_log.debug(this + " BASE ASSIGNMENT : ENTERING TOXML"); Element content = doc.createElement("content"); if (stack.isEmpty()) { doc.appendChild(content); } else { ((Element) stack.peek()).appendChild(content); } stack.push(content); String numItemsString = null; String attributeString = null; String itemString = null; Reference tempReference = null; // SAK-13408 -The XML implementation in Websphere throws an LSException if the // attribute is null, while in Tomcat it assumes an empty string. The following // sets the attribute to an empty string if the value is null. content.setAttribute("id", m_id == null ? "" : m_id); content.setAttribute("context", m_context == null ? "" : m_context); content.setAttribute("title", m_title == null ? "" : m_title); content.setAttribute("groupproject", getBoolString(m_groupProject)); content.setAttribute("indivgraded", getBoolString(m_individuallyGraded)); content.setAttribute("releasegrades", getBoolString(m_releaseGrades)); content.setAttribute("allowattach", getBoolString(m_allowAttachments)); content.setAttribute("hideduedate", getBoolString(m_hideDueDate)); content.setAttribute("allowreview", getBoolString(m_allowReviewService)); content.setAttribute("allowstudentview", getBoolString(m_allowStudentViewReport)); content.setAttribute("submitReviewRepo", m_submitReviewRepo); content.setAttribute("generateOriginalityReport", m_generateOriginalityReport); content.setAttribute("checkTurnitin", getBoolString(m_checkTurnitin)); content.setAttribute("checkInternet", getBoolString(m_checkInternet)); content.setAttribute("checkPublications", getBoolString(m_checkPublications)); content.setAttribute("checkInstitution", getBoolString(m_checkInstitution)); content.setAttribute("excludeBibliographic", getBoolString(m_excludeBibliographic)); content.setAttribute("excludeQuoted", getBoolString(m_excludeQuoted)); content.setAttribute("excludeType", Integer.toString(m_excludeType)); content.setAttribute("excludeValue", Integer.toString(m_excludeValue)); content.setAttribute("honorpledge", String.valueOf(m_honorPledge)); content.setAttribute("submissiontype", String.valueOf(m_typeOfSubmission)); content.setAttribute("typeofgrade", String.valueOf(m_typeOfGrade)); content.setAttribute("scaled_maxgradepoint", String.valueOf(m_maxGradePoint)); content.setAttribute("datecreated", getTimeString(m_timeCreated)); content.setAttribute("lastmod", getTimeString(m_timeLastModified)); M_log.debug(this + " BASE CONTENT : TOXML : SAVED REGULAR PROPERTIES"); // SAVE THE AUTHORS numItemsString = "" + m_authors.size(); content.setAttribute("numberofauthors", numItemsString); for (int x = 0; x < m_authors.size(); x++) { attributeString = "author" + x; itemString = (String) m_authors.get(x); if (itemString != null) content.setAttribute(attributeString, itemString); } M_log.debug(this + " BASE CONTENT : TOXML : SAVED AUTHORS"); // SAVE THE ATTACHMENTS numItemsString = "" + m_attachments.size(); content.setAttribute("numberofattachments", numItemsString); for (int x = 0; x < m_attachments.size(); x++) { attributeString = "attachment" + x; tempReference = (Reference) m_attachments.get(x); itemString = tempReference.getReference(); if (itemString != null) content.setAttribute(attributeString, itemString); } // SAVE THE PROPERTIES m_properties.toXml(doc, stack); M_log.debug(this + " BASE CONTENT : TOXML : SAVED REGULAR PROPERTIES"); stack.pop(); // SAVE THE INSTRUCTIONS FormattedText.encodeFormattedTextAttribute(content, "instructions", m_instructions); return content; }// toXml protected void setAll(AssignmentContent content) { if (content != null) { m_id = content.getId(); m_context = content.getContext(); m_authors = content.getAuthors(); m_attachments = content.getAttachments(); m_title = content.getTitle(); m_instructions = content.getInstructions(); m_honorPledge = content.getHonorPledge(); m_typeOfSubmission = content.getTypeOfSubmission(); m_typeOfGrade = content.getTypeOfGrade(); m_maxGradePoint = content.getMaxGradePoint(); m_groupProject = content.getGroupProject(); m_individuallyGraded = content.individuallyGraded(); m_releaseGrades = content.releaseGrades(); m_allowAttachments = content.getAllowAttachments(); m_hideDueDate = content.getHideDueDate(); //Uct m_allowReviewService = content.getAllowReviewService(); m_allowStudentViewReport = content.getAllowStudentViewReport(); m_submitReviewRepo = content.getSubmitReviewRepo(); m_generateOriginalityReport = content.getGenerateOriginalityReport(); m_checkTurnitin = content.isCheckTurnitin(); m_checkInternet = content.isCheckInternet(); m_checkPublications = content.isCheckPublications(); m_checkInstitution = content.isCheckInstitution(); m_excludeBibliographic = content.isExcludeBibliographic(); m_excludeQuoted = content.isExcludeQuoted(); m_excludeType = content.getExcludeType(); m_excludeValue = content.getExcludeValue(); m_timeCreated = content.getTimeCreated(); m_timeLastModified = content.getTimeLastModified(); m_properties = new BaseResourcePropertiesEdit(); m_properties.addAll(content.getProperties()); } } public String getId() { return m_id; } /** * Access the URL which can be used to access the resource. * * @return The URL which can be used to access the resource. */ public String getUrl() { return getAccessPoint(false) + Entity.SEPARATOR + "c" + Entity.SEPARATOR + m_context + Entity.SEPARATOR + m_id; } // getUrl /** * Access the internal reference which can be used to access the resource from within the system. * * @return The the internal reference which can be used to access the resource from within the system. */ public String getReference() { return contentReference(m_context, m_id); } // getReference /** * @inheritDoc */ public String getReference(String rootProperty) { return getReference(); } /** * @inheritDoc */ public String getUrl(String rootProperty) { return getUrl(); } /** * Access the resource's properties. * * @return The resource's properties. */ public ResourceProperties getProperties() { return m_properties; } /****************************************************************************************************************************************************************************************************************************************************** * AttachmentContainer Implementation *****************************************************************************************************************************************************************************************************************************************************/ /** * Access the attachments. * * @return The set of attachments (a ReferenceVector containing Reference objects) (may be empty). */ public List getAttachments() { return m_attachments; } /****************************************************************************************************************************************************************************************************************************************************** * AssignmentContent Implementation *****************************************************************************************************************************************************************************************************************************************************/ /** * Access the AssignmentContent's context at the time of creation. * * @return String - the context string. */ public String getContext() { return m_context; } /** * Access the list of authors. * * @return FlexStringArray of user ids. */ public List getAuthors() { return m_authors; } /** * Access the creator of this object. * * @return The User object representing the creator. */ public String getCreator() { return m_properties.getProperty(ResourceProperties.PROP_CREATOR); } /** * Access the person of last modificaiton * * @return the User */ public String getAuthorLastModified() { return m_properties.getProperty(ResourceProperties.PROP_MODIFIED_BY); } /** * Access the title. * * @return The Assignment's title. */ public String getTitle() { return m_title; } /** * Access the instructions. * * @return The Assignment Content's instructions. */ public String getInstructions() { return m_instructions; } /** * Get the type of valid submission. * * @return int - Type of Submission. */ public int getTypeOfSubmission() { return m_typeOfSubmission; } /** * Access a string describing the type of grade. * * @param gradeType - * The integer representing the type of grade. * @return Description of the type of grade. */ public String getTypeOfGradeString(int type) { String retVal = null; switch (type) { case 1: retVal = rb.getString("ungra"); break; case 2: retVal = rb.getString("letter"); break; case 3: retVal = rb.getString("points"); break; case 4: retVal = rb.getString("passfail"); break; case 5: retVal = rb.getString("check"); break; default: retVal = "Unknown Grade Type"; break; } return retVal; } /** * Get the grade type. * * @return gradeType - The type of grade. */ public int getTypeOfGrade() { return m_typeOfGrade; } /** * Get the maximum grade for grade type = SCORE_GRADE_TYPE(3) * * @return The maximum grade score. */ public int getMaxGradePoint() { return m_maxGradePoint; } /** * Get the maximum grade for grade type = SCORE_GRADE_TYPE(3) Formated to show one decimal place * * @return The maximum grade score. */ public String getMaxGradePointDisplay() { // formated to show one decimal place, for example, 1000 to 100.0 String one_decimal_maxGradePoint = m_maxGradePoint / 10 + "." + (m_maxGradePoint % 10); // get localized number format NumberFormat nbFormat = NumberFormat.getInstance(); try { Locale locale = null; ResourceLoader rb = new ResourceLoader(); locale = rb.getLocale(); nbFormat = NumberFormat.getNumberInstance(locale); } catch (Exception e) { M_log.warn("Error while retrieving local number format, using default ", e); } nbFormat.setMaximumFractionDigits(1); nbFormat.setMinimumFractionDigits(1); nbFormat.setGroupingUsed(false); // show grade in localized number format Double dblGrade = new Double(one_decimal_maxGradePoint); one_decimal_maxGradePoint = nbFormat.format(dblGrade); return one_decimal_maxGradePoint; } /** * Get whether this project can be a group project. * * @return True if this can be a group project, false otherwise. */ public boolean getGroupProject() { return m_groupProject; } /** * Get whether group projects should be individually graded. * * @return individGraded - true if projects are individually graded, false if grades are given to the group. */ public boolean individuallyGraded() { return m_individuallyGraded; } /** * Gets whether grades can be released once submissions are graded. * * @return true if grades can be released once submission are graded, false if they must be released manually. */ public boolean releaseGrades() { return m_releaseGrades; } /** * Get the Honor Pledge type; values are NONE and ENGINEERING_HONOR_PLEDGE. * * @return the Honor Pledge value. */ public int getHonorPledge() { return m_honorPledge; } /** * Does this Assignment allow attachments? * * @return true if the Assignment allows attachments, false otherwise? */ public boolean getAllowAttachments() { return m_allowAttachments; } /** * Does this Assignment have a hidden due date * * @return true if the Assignment due date hidden, false otherwise? */ public boolean getHideDueDate() { return m_hideDueDate; } /** * Does this Assignment allow review service? * * @return true if the Assignment allows review service, false otherwise? */ public boolean getAllowReviewService() { return m_allowReviewService; } public boolean getAllowStudentViewReport() { return m_allowStudentViewReport; } /** * Access the time that this object was created. * * @return The Time object representing the time of creation. */ public Time getTimeCreated() { return m_timeCreated; } /** * Access the time of last modificaiton. * * @return The Time of last modification. */ public Time getTimeLastModified() { return m_timeLastModified; } /** * Is this AssignmentContent selected for use by an Assignment ? */ public boolean inUse() { boolean retVal = false; Assignment assignment = null; List allAssignments = getAssignments(m_context); for (int x = 0; x < allAssignments.size(); x++) { assignment = (Assignment) allAssignments.get(x); if (assignment.getContentReference().equals(getReference())) return true; } return retVal; } /** * Are these objects equal? If they are both AssignmentContent objects, and they have matching id's, they are. * * @return true if they are equal, false if not. */ public boolean equals(Object obj) { if (!(obj instanceof AssignmentContent)) return false; return ((AssignmentContent) obj).getId().equals(getId()); } // equals /** * Make a hash code that reflects the equals() logic as well. We want two objects, even if different instances, if they have the same id to hash the same. */ public int hashCode() { return getId().hashCode(); } // hashCode /** * Compare this object with the specified object for order. * * @return A negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object. */ public int compareTo(Object obj) { if (!(obj instanceof AssignmentContent)) throw new ClassCastException(); // if the object are the same, say so if (obj == this) return 0; // start the compare by comparing their sort names int compare = getTitle().compareTo(((AssignmentContent) obj).getTitle()); // if these are the same if (compare == 0) { // sort based on (unique) id compare = getId().compareTo(((AssignmentContent) obj).getId()); } return compare; } // compareTo public String getSubmitReviewRepo() { return m_submitReviewRepo; } public void setSubmitReviewRepo(String m_submitReviewRepo) { this.m_submitReviewRepo = m_submitReviewRepo; } public String getGenerateOriginalityReport() { return m_generateOriginalityReport; } public void setGenerateOriginalityReport(String m_generateOriginalityReport) { this.m_generateOriginalityReport = m_generateOriginalityReport; } public boolean isCheckTurnitin() { return m_checkTurnitin; } public void setCheckTurnitin(boolean m_checkTurnitin) { this.m_checkTurnitin = m_checkTurnitin; } public boolean isCheckInternet() { return m_checkInternet; } public void setCheckInternet(boolean m_checkInternet) { this.m_checkInternet = m_checkInternet; } public boolean isCheckPublications() { return m_checkPublications; } public void setCheckPublications(boolean m_checkPublications) { this.m_checkPublications = m_checkPublications; } public boolean isCheckInstitution() { return m_checkInstitution; } public void setCheckInstitution(boolean m_checkInstitution) { this.m_checkInstitution = m_checkInstitution; } public boolean isExcludeBibliographic() { return m_excludeBibliographic; } public void setExcludeBibliographic(boolean m_excludeBibliographic) { this.m_excludeBibliographic = m_excludeBibliographic; } public boolean isExcludeQuoted() { return m_excludeQuoted; } public void setExcludeQuoted(boolean m_excludeQuoted) { this.m_excludeQuoted = m_excludeQuoted; } public int getExcludeType(){ return m_excludeType; } public void setExcludeType(int m_excludeType){ this.m_excludeType = m_excludeType; } public int getExcludeValue(){ return m_excludeValue; } public void setExcludeValue(int m_excludeValue){ this.m_excludeValue = m_excludeValue; } }// BaseAssignmentContent /********************************************************************************************************************************************************************************************************************************************************** * AssignmentContentEdit implementation *********************************************************************************************************************************************************************************************************************************************************/ /** * <p> * BaseAssignmentContentEdit is an implementation of the CHEF AssignmentContentEdit object. * </p> * * @author University of Michigan, CHEF Software Development Team */ public class BaseAssignmentContentEdit extends BaseAssignmentContent implements AttachmentContainer, AssignmentContentEdit, SessionBindingListener { /** The event code for this edit. */ protected String m_event = null; /** Active flag. */ protected boolean m_active = false; /** * Construct from another AssignmentContent object. * * @param AssignmentContent * The AssignmentContent object to use for values. */ public BaseAssignmentContentEdit(AssignmentContent assignmentContent) { super(assignmentContent); } // BaseAssignmentContentEdit /** * Construct. * * @param id * The AssignmentContent id. */ public BaseAssignmentContentEdit(String id, String context) { super(id, context); } // BaseAssignmentContentEdit /** * Construct from information in XML. * * @param el * The XML DOM Element definining the AssignmentContent. */ public BaseAssignmentContentEdit(Element el) { super(el); } // BaseAssignmentContentEdit /** * Clean up. */ protected void finalize() { // catch the case where an edit was made but never resolved if (m_active) { cancelEdit(this); } } // finalize /****************************************************************************************************************************************************************************************************************************************************** * AttachmentContainer Implementation *****************************************************************************************************************************************************************************************************************************************************/ /** * Add an attachment. * * @param ref - * The attachment Reference. */ public void addAttachment(Reference ref) { if (ref != null) m_attachments.add(ref); } /** * Remove an attachment. * * @param ref - * The attachment Reference to remove (the one removed will equal this, they need not be ==). */ public void removeAttachment(Reference ref) { if (ref != null) m_attachments.remove(ref); } /** * Replace the attachment set. * * @param attachments - * A ReferenceVector that will become the new set of attachments. */ public void replaceAttachments(List attachments) { m_attachments = attachments; } /** * Clear all attachments. */ public void clearAttachments() { m_attachments.clear(); } /****************************************************************************************************************************************************************************************************************************************************** * AssignmentContentEdit Implementation *****************************************************************************************************************************************************************************************************************************************************/ /** * Set the title. * * @param title - * The Assignment's title. */ public void setTitle(String title) { m_title = title; } /** * Set the instructions. * * @param instructions - * The Assignment's instructions. */ public void setInstructions(String instructions) { m_instructions = instructions; } /** * Set the context at the time of creation. * * @param context - * the context string. */ public void setContext(String context) { m_context = context; } /** * Set the type of valid submission. * * @param int - * Type of Submission. */ public void setTypeOfSubmission(int type) { m_typeOfSubmission = type; } /** * Set the grade type. * * @param gradeType - * The type of grade. */ public void setTypeOfGrade(int gradeType) { m_typeOfGrade = gradeType; } /** * Set the maximum grade for grade type = SCORE_GRADE_TYPE(3) * * @param maxPoints - * The maximum grade score. */ public void setMaxGradePoint(int maxPoints) { m_maxGradePoint = maxPoints; } /** * Set whether this project can be a group project. * * @param groupProject - * True if this can be a group project, false otherwise. */ public void setGroupProject(boolean groupProject) { m_groupProject = groupProject; } /** * Set whether group projects should be individually graded. * * @param individGraded - * true if projects are individually graded, false if grades are given to the group. */ public void setIndividuallyGraded(boolean individGraded) { m_individuallyGraded = individGraded; } /** * Sets whether grades can be released once submissions are graded. * * @param release - * true if grades can be released once submission are graded, false if they must be released manually. */ public void setReleaseGrades(boolean release) { m_releaseGrades = release; } public void setHideDueDate(boolean hide) { m_hideDueDate = hide; } /** * Set the Honor Pledge type; values are NONE and ENGINEERING_HONOR_PLEDGE. * * @param pledgeType - * the Honor Pledge value. */ public void setHonorPledge(int pledgeType) { m_honorPledge = pledgeType; } /** * Does this Assignment allow using the review service? * * @param allow - * true if the Assignment allows review service, false otherwise? */ public void setAllowReviewService(boolean allow) { m_allowReviewService = allow; } /** * Does this Assignment allow students to view the report? * * @param allow - * true if the Assignment allows students to view the report, false otherwise? */ public void setAllowStudentViewReport(boolean allow) { m_allowStudentViewReport = allow; } /** * Does this Assignment allow attachments? * * @param allow - * true if the Assignment allows attachments, false otherwise? */ public void setAllowAttachments(boolean allow) { m_allowAttachments = allow; } /** * Add an author to the author list. * * @param author - * The User to add to the author list. */ public void addAuthor(User author) { if (author != null) m_authors.add(author.getId()); } /** * Remove an author from the author list. * * @param author - * the User to remove from the author list. */ public void removeAuthor(User author) { if (author != null) m_authors.remove(author.getId()); } /** * Set the time last modified. * * @param lastmod - * The Time at which the Content was last modified. */ public void setTimeLastModified(Time lastmod) { if (lastmod != null) m_timeLastModified = lastmod; } /** * Take all values from this object. * * @param AssignmentContent * The AssignmentContent object to take values from. */ protected void set(AssignmentContent assignmentContent) { setAll(assignmentContent); } // set /** * Access the event code for this edit. * * @return The event code for this edit. */ protected String getEvent() { return m_event; } /** * Set the event code for this edit. * * @param event * The event code for this edit. */ protected void setEvent(String event) { m_event = event; } /** * Access the resource's properties for modification * * @return The resource's properties. */ public ResourcePropertiesEdit getPropertiesEdit() { return m_properties; } // getPropertiesEdit /** * Enable editing. */ protected void activate() { m_active = true; } // activate /** * Check to see if the edit is still active, or has already been closed. * * @return true if the edit is active, false if it's been closed. */ public boolean isActiveEdit() { return m_active; } // isActiveEdit /** * Close the edit object - it cannot be used after this. */ protected void closeEdit() { m_active = false; } // closeEdit /****************************************************************************************************************************************************************************************************************************************************** * SessionBindingListener implementation *****************************************************************************************************************************************************************************************************************************************************/ public void valueBound(SessionBindingEvent event) { } public void valueUnbound(SessionBindingEvent event) { M_log.debug(" BaseAssignmentContent valueUnbound()"); // catch the case where an edit was made but never resolved if (m_active) { cancelEdit(this); } } // valueUnbound } // BaseAssignmentContentEdit /********************************************************************************************************************************************************************************************************************************************************** * AssignmentSubmission implementation *********************************************************************************************************************************************************************************************************************************************************/ public class BaseAssignmentSubmission implements AssignmentSubmission { protected final String STATUS_DRAFT = "Drafted"; protected final String STATUS_SUBMITTED = "Submitted"; protected final String STATUS_RETURNED = "Returned"; protected final String STATUS_GRADED = "Graded"; protected ResourcePropertiesEdit m_properties; protected String m_id; protected String m_assignment; protected String m_context; protected List m_submitters; protected String m_submitterId; protected List m_submissionLog; protected List m_grades; protected Time m_timeSubmitted; protected Time m_timeReturned; protected Time m_timeLastModified; protected List m_submittedAttachments; protected List m_feedbackAttachments; protected String m_submittedText; protected String m_feedbackComment; protected String m_feedbackText; protected String m_grade; protected boolean m_submitted; protected boolean m_returned; protected boolean m_graded; protected boolean m_gradeReleased; protected boolean m_honorPledgeFlag; protected boolean m_hideDueDate; //The score given by the review service protected Integer m_reviewScore; // The report given by the content review service protected String m_reviewReport; // The status of the review service protected String m_reviewStatus; protected String m_reviewIconUrl; protected String m_reviewError; // return the variables // Get new values from review service if defaults public int getReviewScore() { // Code to get updated score if default M_log.debug(this + " getReviewScore for submission " + this.getId() + " and review service is: " + (this.getAssignment().getContent().getAllowReviewService())); if (!this.getAssignment().getContent().getAllowReviewService()) { M_log.debug(this + " getReviewScore Content review is not enabled for this assignment"); return -2; } if (m_submittedAttachments.isEmpty()) { M_log.debug(this + " getReviewScore No attachments submitted."); return -2; } else { //we may have already retrived this one if (m_reviewScore != null && m_reviewScore > -1) { M_log.debug("returning stored value of " + m_reviewScore); return m_reviewScore.intValue(); } ContentResource cr = getFirstAcceptableAttachement(); if (cr == null ) { M_log.debug(this + " getReviewScore No suitable attachments found in list"); return -2; } try { //we need to find the first attachment the CR will accept String contentId = cr.getId(); M_log.debug(this + " getReviewScore checking for score for content: " + contentId); Long status = contentReviewService.getReviewStatus(contentId); if (status != null && (status.equals(ContentReviewItem.NOT_SUBMITTED_CODE) || status.equals(ContentReviewItem.SUBMITTED_AWAITING_REPORT_CODE))) { M_log.debug(this + " getReviewStatus returned a status of: " + status); return -2; } int score = contentReviewService.getReviewScore(contentId); m_reviewScore = score; M_log.debug(this + " getReviewScore CR returned a score of: " + score); return score; } catch (QueueException cie) { //should we add the item try { M_log.debug(this + " getReviewScore Item is not in queue we will try add it"); String contentId = cr.getId(); String userId = this.getSubmitterId(); try { contentReviewService.queueContent(userId, null, getAssignment().getReference(), contentId); } catch (QueueException qe) { M_log.warn(" getReviewScore Unable to queue content with content review Service: " + qe.getMessage()); } } catch (Exception e) { e.printStackTrace(); } return -1; } catch (Exception e) { M_log.warn(this + " getReviewScore " + e.getMessage()); return -1; } } } public String getReviewReport() { // Code to get updated report if default if (m_submittedAttachments.isEmpty()) { M_log.debug(this.getId() + " getReviewReport No attachments submitted."); return "Error"; } else { try { ContentResource cr = getFirstAcceptableAttachement(); if (cr == null ) { M_log.debug(this + " getReviewReport No suitable attachments found in list"); return "error"; } String contentId = cr.getId(); if (allowGradeSubmission(getReference())) return contentReviewService.getReviewReportInstructor(contentId); else return contentReviewService.getReviewReportStudent(contentId); } catch (Exception e) { M_log.warn(":getReviewReport() " + e.getMessage()); return "Error"; } } } private ContentResource getFirstAcceptableAttachement() { String contentId = null; try { for( int i =0; i < m_submittedAttachments.size();i++ ) { Reference ref = (Reference)m_submittedAttachments.get(i); ContentResource contentResource = (ContentResource)ref.getEntity(); if (contentReviewService.isAcceptableContent(contentResource)) { return (ContentResource)contentResource; } } } catch (Exception e) { M_log.warn(":getFirstAcceptableAttachment() " + e.getMessage()); e.printStackTrace(); } return null; } public String getReviewStatus() { return m_reviewStatus; } public String getReviewError() { // Code to get error report if (m_submittedAttachments.isEmpty()) { M_log.debug(this.getId() + " getReviewError No attachments submitted."); return null; } else { try { ContentResource cr = getFirstAcceptableAttachement(); if (cr == null ) { M_log.debug(this + " getReviewError No suitable attachments found in list"); return null; } String contentId = cr.getId(); // This should use getLocalizedReviewErrorMessage(contentId) // to get a i18n message of the error Long status = contentReviewService.getReviewStatus(contentId); String errorMessage = null; if (status != null) { if (status.equals(ContentReviewItem.REPORT_ERROR_NO_RETRY_CODE)) { errorMessage = rb.getString("content_review.error.REPORT_ERROR_NO_RETRY_CODE"); } else if (status.equals(ContentReviewItem.REPORT_ERROR_RETRY_CODE)) { errorMessage = rb.getString("content_review.error.REPORT_ERROR_RETRY_CODE"); } else if (status.equals(ContentReviewItem.SUBMISSION_ERROR_NO_RETRY_CODE)) { errorMessage = rb.getString("content_review.error.SUBMISSION_ERROR_NO_RETRY_CODE"); } else if (status.equals(ContentReviewItem.SUBMISSION_ERROR_RETRY_CODE)) { errorMessage = rb.getString("content_review.error.SUBMISSION_ERROR_RETRY_CODE"); } else if (status.equals(ContentReviewItem.SUBMISSION_ERROR_RETRY_EXCEEDED)) { errorMessage = rb.getString("content_review.error.SUBMISSION_ERROR_RETRY_EXCEEDED_CODE"); } else if (status.equals(ContentReviewItem.SUBMISSION_ERROR_USER_DETAILS_CODE)) { errorMessage = rb.getString("content_review.error.SUBMISSION_ERROR_USER_DETAILS_CODE"); } else if (ContentReviewItem.SUBMITTED_AWAITING_REPORT_CODE.equals(status) || ContentReviewItem.NOT_SUBMITTED_CODE.equals(status)) { errorMessage = rb.getString("content_review.pending.info"); } } if (errorMessage == null) { errorMessage = rb.getString("content_review.error"); } return errorMessage; } catch (Exception e) { //e.printStackTrace(); M_log.warn(this + ":getReviewError() " + e.getMessage()); return null; } } } public String getReviewIconUrl() { if (m_reviewIconUrl == null ) m_reviewIconUrl = contentReviewService.getIconUrlforScore(Long.valueOf(this.getReviewScore())); return m_reviewIconUrl; } /** * constructor */ public BaseAssignmentSubmission() { m_properties = new BaseResourcePropertiesEdit(); }// constructor /** * Copy constructor. */ public BaseAssignmentSubmission(AssignmentSubmission submission) { setAll(submission); } /** * Constructor used by addSubmission. */ public BaseAssignmentSubmission(String id, String assignId, String submitterId, String submitTime, String submitted, String graded) { // must set initial review status m_reviewStatus = ""; m_reviewScore = -1; m_reviewReport = "Not available yet"; m_reviewError = ""; m_id = id; m_assignment = assignId; m_properties = new BaseResourcePropertiesEdit(); addLiveProperties(m_properties); m_submitters = new ArrayList(); m_submissionLog = new ArrayList(); m_grades = new ArrayList(); m_feedbackAttachments = m_entityManager.newReferenceList(); m_submittedAttachments = m_entityManager.newReferenceList(); m_submitted = false; m_returned = false; m_graded = false; m_gradeReleased = false; m_submittedText = ""; m_feedbackComment = ""; m_feedbackText = ""; m_grade = ""; m_timeLastModified = TimeService.newTime(); m_submitterId = submitterId; if (submitterId == null) { String currentUser = SessionManager.getCurrentSessionUserId(); if (currentUser == null) currentUser = ""; m_submitters.add(currentUser); m_submitterId = currentUser; } else { m_submitters.add(submitterId); } if (submitted != null) { m_submitted = Boolean.valueOf(submitted).booleanValue(); } if (graded != null) { m_graded = Boolean.valueOf(graded).booleanValue(); } } // todo work out what this does /** * Reads the AssignmentSubmission's attribute values from xml. * * @param s - * Data structure holding the xml info. */ public BaseAssignmentSubmission(Element el) { int numAttributes = 0; String intString = null; String attributeString = null; String tempString = null; Reference tempReference = null; M_log.debug(" BaseAssigmentSubmission : ENTERING STORAGE CONSTRUCTOR"); m_id = el.getAttribute("id"); m_context = el.getAttribute("context"); // %%%zqian // read the scaled grade point first; if there is none, get the old grade value String grade = StringUtils.trimToNull(el.getAttribute("scaled_grade")); if (grade == null) { grade = StringUtils.trimToNull(el.getAttribute("grade")); if (grade != null) { try { Integer.parseInt(grade); // for the grades in points, multiple those by 10 grade = grade + "0"; } catch (Exception e) { M_log.warn(":BaseAssignmentSubmission(Element el) " + e.getMessage()); } } } m_grade = grade; m_assignment = el.getAttribute("assignment"); m_timeSubmitted = getTimeObject(el.getAttribute("datesubmitted")); m_timeReturned = getTimeObject(el.getAttribute("datereturned")); m_assignment = el.getAttribute("assignment"); m_timeLastModified = getTimeObject(el.getAttribute("lastmod")); m_submitted = getBool(el.getAttribute("submitted")); m_returned = getBool(el.getAttribute("returned")); m_graded = getBool(el.getAttribute("graded")); m_gradeReleased = getBool(el.getAttribute("gradereleased")); m_honorPledgeFlag = getBool(el.getAttribute("pledgeflag")); m_hideDueDate = getBool(el.getAttribute("hideduedate")); m_submittedText = FormattedText.decodeFormattedTextAttribute(el, "submittedtext"); m_feedbackComment = FormattedText.decodeFormattedTextAttribute(el, "feedbackcomment"); m_feedbackText = FormattedText.decodeFormattedTextAttribute(el, "feedbacktext"); m_submitterId = el.getAttribute("submitterid"); m_submissionLog = new ArrayList(); m_grades = new ArrayList(); intString = el.getAttribute("numberoflogs"); try { numAttributes = Integer.parseInt(intString); for (int x = 0; x < numAttributes; x++) { attributeString = "log" + x; tempString = el.getAttribute(attributeString); if (tempString != null) m_submissionLog.add(tempString); } } catch (Exception e) { M_log.debug(" BaseAssignmentSubmission: CONSTRUCTOR : Exception reading logs : " + e); } intString = el.getAttribute("numberofgrades"); try { numAttributes = Integer.parseInt(intString); for (int x = 0; x < numAttributes; x++) { attributeString = "grade" + x; tempString = el.getAttribute(attributeString); if (tempString != null) m_grades.add(tempString); } } catch (Exception e) { M_log.warn(" BaseAssignmentSubmission: CONSTRUCTOR : Exception reading grades : " + e); } // READ THE SUBMITTERS m_submitters = new ArrayList(); M_log.debug(" BaseAssignmentSubmission : CONSTRUCTOR : Reading submitters : "); intString = el.getAttribute("numberofsubmitters"); try { numAttributes = Integer.parseInt(intString); for (int x = 0; x < numAttributes; x++) { attributeString = "submitter" + x; tempString = el.getAttribute(attributeString); if (tempString != null) m_submitters.add(tempString); } } catch (Exception e) { M_log.debug(" BaseAssignmentSubmission: CONSTRUCTOR : Exception reading submitters : " + e); } // READ THE FEEDBACK ATTACHMENTS m_feedbackAttachments = m_entityManager.newReferenceList(); intString = el.getAttribute("numberoffeedbackattachments"); M_log.debug(" BaseAssignmentSubmission: CONSTRUCTOR : num feedback attachments : " + intString); try { numAttributes = Integer.parseInt(intString); for (int x = 0; x < numAttributes; x++) { attributeString = "feedbackattachment" + x; tempString = el.getAttribute(attributeString); if (tempString != null) { tempReference = m_entityManager.newReference(tempString); m_feedbackAttachments.add(tempReference); M_log.debug(" BaseAssignmentSubmission: CONSTRUCTOR : " + attributeString + " : " + tempString); } } } catch (Exception e) { M_log.warn(" BaseAssignmentSubmission: CONSTRUCTOR : Exception reading feedback attachments : " + e); } // READ THE SUBMITTED ATTACHMENTS m_submittedAttachments = m_entityManager.newReferenceList(); intString = el.getAttribute("numberofsubmittedattachments"); M_log.debug(" BaseAssignmentSubmission: CONSTRUCTOR : num submitted attachments : " + intString); try { numAttributes = Integer.parseInt(intString); for (int x = 0; x < numAttributes; x++) { attributeString = "submittedattachment" + x; tempString = el.getAttribute(attributeString); if (tempString != null) { tempReference = m_entityManager.newReference(tempString); m_submittedAttachments.add(tempReference); M_log.debug(" BaseAssignmentSubmission: CONSTRUCTOR : " + attributeString + " : " + tempString); } } } catch (Exception e) { M_log.debug(" BaseAssignmentSubmission: CONSTRUCTOR : Exception reading submitted attachments : " + e); } // READ THE PROPERTIES, SUBMITTED TEXT, FEEDBACK COMMENT, FEEDBACK TEXT NodeList children = el.getChildNodes(); final 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; // look for properties if (element.getTagName().equals("properties")) { // re-create properties m_properties = new BaseResourcePropertiesEdit(element); } // old style encoding else if (element.getTagName().equals("submittedtext")) { if ((element.getChildNodes() != null) && (element.getChildNodes().item(0) != null)) { m_submittedText = element.getChildNodes().item(0).getNodeValue(); M_log.debug(" BaseAssignmentSubmission: CONSTRUCTOR : submittedtext : " + m_submittedText); } if (m_submittedText == null) { m_submittedText = ""; } } // old style encoding else if (element.getTagName().equals("feedbackcomment")) { if ((element.getChildNodes() != null) && (element.getChildNodes().item(0) != null)) { m_feedbackComment = element.getChildNodes().item(0).getNodeValue(); M_log.debug(" BaseAssignmentSubmission: CONSTRUCTOR : feedbackcomment : " + m_feedbackComment); } if (m_feedbackComment == null) { m_feedbackComment = ""; } } // old style encoding else if (element.getTagName().equals("feedbacktext")) { if ((element.getChildNodes() != null) && (element.getChildNodes().item(0) != null)) { m_feedbackText = element.getChildNodes().item(0).getNodeValue(); M_log.debug(" BaseAssignmentSubmission: CONSTRUCTOR : FEEDBACK TEXT : " + m_feedbackText); } if (m_feedbackText == null) { m_feedbackText = ""; } } } try { if (el.getAttribute("reviewScore")!=null) m_reviewScore = Integer.parseInt(el.getAttribute("reviewScore")); else m_reviewScore = -1; } catch (NumberFormatException nfe) { m_reviewScore = -1; M_log.warn(":BaseAssignmentSubmission(Element) " + nfe.getMessage()); } try { // The report given by the content review service if (el.getAttribute("reviewReport")!=null) m_reviewReport = el.getAttribute("reviewReport"); else m_reviewReport = "no report available"; // The status of the review service if (el.getAttribute("reviewStatus")!=null) m_reviewStatus = el.getAttribute("reviewStatus"); else m_reviewStatus = ""; // The status of the review service if (el.getAttribute("reviewError")!=null) m_reviewError = el.getAttribute("reviewError"); else m_reviewError = ""; } catch (Exception e) { M_log.error("error constructing Submission: " + e); } //get the review Status from ContentReview rather than using old ones if (contentReviewService != null) { m_reviewStatus = this.getReviewStatus(); m_reviewScore = this.getReviewScore(); m_reviewError = this.getReviewError(); } M_log.debug(" BaseAssignmentSubmission: LEAVING STORAGE CONSTRUCTOR"); }// storage constructor /** * @param services * @return */ public ContentHandler getContentHandler(Map<String, Object> services) { final Entity thisEntity = this; return new DefaultEntityHandler() { /* * (non-Javadoc) * * @see org.sakaiproject.util.DefaultEntityHandler#startElement(java.lang.String, * java.lang.String, java.lang.String, * org.xml.sax.Attributes) */ @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { if (doStartElement(uri, localName, qName, attributes)) { if ("submission".equals(qName) && entity == null) { try { if (StringUtils.trimToNull(attributes.getValue("reviewScore"))!=null) m_reviewScore = Integer.parseInt(attributes.getValue("reviewScore")); else m_reviewScore = -1; } catch (NumberFormatException nfe) { m_reviewScore = -1; M_log.warn(":AssignmentSubmission:getContentHandler:DefaultEntityHandler " + nfe.getMessage()); } try { // The report given by the content review service if (attributes.getValue("reviewReport")!=null) m_reviewReport = attributes.getValue("reviewReport"); else m_reviewReport = "no report available"; // The status of the review service if (attributes.getValue("reviewStatus")!=null) m_reviewStatus = attributes.getValue("reviewStatus"); else m_reviewStatus = ""; // The status of the review service if (attributes.getValue("reviewError")!=null) { m_reviewError = attributes.getValue("reviewError"); } else { m_reviewError = ""; } } catch (Exception e) { M_log.error("error constructing Submission: " + e); } int numAttributes = 0; String intString = null; String attributeString = null; String tempString = null; Reference tempReference = null; m_id = attributes.getValue("id"); // M_log.info(this + " BASE SUBMISSION : CONSTRUCTOR : m_id : " + m_id); m_context = attributes.getValue("context"); // M_log.info(this + " BASE SUBMISSION : CONSTRUCTOR : m_context : " + m_context); // %%%zqian // read the scaled grade point first; if there is none, get the old grade value String grade = StringUtils.trimToNull(attributes.getValue("scaled_grade")); if (grade == null) { grade = StringUtils.trimToNull(attributes.getValue("grade")); if (grade != null) { try { Integer.parseInt(grade); // for the grades in points, multiple those by 10 grade = grade + "0"; } catch (Exception e) { M_log.warn(":BaseAssignmentSubmission:getContentHanler:DefaultEnityHandler " + e.getMessage()); } } } m_grade = grade; m_assignment = attributes.getValue("assignment"); m_timeSubmitted = getTimeObject(attributes.getValue("datesubmitted")); m_timeReturned = getTimeObject(attributes.getValue("datereturned")); m_assignment = attributes.getValue("assignment"); m_timeLastModified = getTimeObject(attributes.getValue("lastmod")); m_submitted = getBool(attributes.getValue("submitted")); m_returned = getBool(attributes.getValue("returned")); m_graded = getBool(attributes.getValue("graded")); m_gradeReleased = getBool(attributes.getValue("gradereleased")); m_honorPledgeFlag = getBool(attributes.getValue("pledgeflag")); m_hideDueDate = getBool(attributes.getValue("hideduedate")); m_submittedText = formattedTextDecodeFormattedTextAttribute(attributes, "submittedtext"); m_feedbackComment = formattedTextDecodeFormattedTextAttribute(attributes, "feedbackcomment"); m_feedbackText = formattedTextDecodeFormattedTextAttribute(attributes, "feedbacktext"); m_submitterId = attributes.getValue("submitterid"); m_submissionLog = new ArrayList(); m_grades = new ArrayList(); intString = attributes.getValue("numberoflogs"); try { numAttributes = Integer.parseInt(intString); for (int x = 0; x < numAttributes; x++) { attributeString = "log" + x; tempString = attributes.getValue(attributeString); if (tempString != null) { m_submissionLog.add(tempString); } } } catch (Exception e) { M_log.debug(" BaseAssignmentSubmission: CONSTRUCTOR : Exception reading logs : " + e); } intString = attributes.getValue("numberofgrades"); try { numAttributes = Integer.parseInt(intString); for (int x = 0; x < numAttributes; x++) { attributeString = "grade" + x; tempString = attributes.getValue(attributeString); if (tempString != null) m_grades.add(tempString); } } catch (Exception e) { M_log.warn(" BaseAssignmentSubmission: CONSTRUCTOR : Exception reading logs : " + e); } // READ THE SUBMITTERS m_submitters = new ArrayList(); intString = attributes.getValue("numberofsubmitters"); try { numAttributes = Integer.parseInt(intString); for (int x = 0; x < numAttributes; x++) { attributeString = "submitter" + x; tempString = attributes.getValue(attributeString); if (tempString != null) { m_submitters.add(tempString); } // for backward compatibility of assignments without submitter ids if (m_submitterId == null) { m_submitterId = tempString; } } } catch (Exception e) { M_log.warn(" BaseAssignmentSubmission getContentHandler : Exception reading submitters : " + e); } // READ THE FEEDBACK ATTACHMENTS m_feedbackAttachments = m_entityManager.newReferenceList(); intString = attributes.getValue("numberoffeedbackattachments"); try { numAttributes = Integer.parseInt(intString); for (int x = 0; x < numAttributes; x++) { attributeString = "feedbackattachment" + x; tempString = attributes.getValue(attributeString); if (tempString != null) { tempReference = m_entityManager.newReference(tempString); m_feedbackAttachments.add(tempReference); } } } catch (Exception e) { M_log.warn(" BaseAssignmentSubmission getContentHandler : Exception reading feedback attachments : " + e); } // READ THE SUBMITTED ATTACHMENTS m_submittedAttachments = m_entityManager.newReferenceList(); intString = attributes.getValue("numberofsubmittedattachments"); try { numAttributes = Integer.parseInt(intString); for (int x = 0; x < numAttributes; x++) { attributeString = "submittedattachment" + x; tempString = attributes.getValue(attributeString); if (tempString != null) { tempReference = m_entityManager.newReference(tempString); m_submittedAttachments.add(tempReference); } } } catch (Exception e) { M_log.warn(" BaseAssignmentSubmission getContentHandler: Exception reading submitted attachments : " + e); } entity = thisEntity; } } } }; } /** * Takes the AssignmentContent's attribute values and puts them into the xml document. * * @param s - * Data structure holding the object to be stored. * @param doc - * The xml document. */ public Element toXml(Document doc, Stack stack) { if (M_log.isDebugEnabled()) M_log.debug(this + " BaseAssignmentSubmission : ENTERING TOXML"); Element submission = doc.createElement("submission"); if (stack.isEmpty()) { doc.appendChild(submission); } else { ((Element) stack.peek()).appendChild(submission); } stack.push(submission); String numItemsString = null; String attributeString = null; String itemString = null; Reference tempReference = null; // SAK-13408 -The XML implementation in Websphere throws an LSException if the // attribute is null, while in Tomcat it assumes an empty string. The following // sets the attribute to an empty string if the value is null. submission.setAttribute("reviewScore",m_reviewScore == null ? "" : Integer.toString(m_reviewScore)); submission.setAttribute("reviewReport",m_reviewReport == null ? "" : m_reviewReport); submission.setAttribute("reviewStatus",m_reviewStatus == null ? "" : m_reviewStatus); submission.setAttribute("reviewError",m_reviewError == null ? "" : m_reviewError); submission.setAttribute("id", m_id == null ? "" : m_id); submission.setAttribute("context", m_context == null ? "" : m_context); submission.setAttribute("scaled_grade", m_grade == null ? "" : m_grade); submission.setAttribute("assignment", m_assignment == null ? "" : m_assignment); submission.setAttribute("datesubmitted", getTimeString(m_timeSubmitted)); submission.setAttribute("datereturned", getTimeString(m_timeReturned)); submission.setAttribute("lastmod", getTimeString(m_timeLastModified)); submission.setAttribute("submitted", getBoolString(m_submitted)); submission.setAttribute("returned", getBoolString(m_returned)); submission.setAttribute("graded", getBoolString(m_graded)); submission.setAttribute("gradereleased", getBoolString(m_gradeReleased)); submission.setAttribute("pledgeflag", getBoolString(m_honorPledgeFlag)); submission.setAttribute("hideduedate", getBoolString(m_hideDueDate)); if (M_log.isDebugEnabled()) M_log.debug(this + " BaseAssignmentSubmission: SAVED REGULAR PROPERTIES"); submission.setAttribute("submitterid", m_submitterId == null ? "": m_submitterId); if (M_log.isDebugEnabled()) M_log.debug(this + " BaseAssignmentSubmission: SAVED SUBMITTER ID : " + m_submitterId); numItemsString = "" + m_submissionLog.size(); if (M_log.isDebugEnabled()) M_log.debug(this + " BaseAssignmentSubmission: # logs " + numItemsString); submission.setAttribute("numberoflogs", numItemsString); for (int x = 0; x < m_submissionLog.size(); x++) { attributeString = "log" + x; itemString = (String) m_submissionLog.get(x); if (itemString != null) { submission.setAttribute(attributeString, itemString); } } numItemsString = "" + m_grades.size(); if (M_log.isDebugEnabled()) M_log.debug(this + " BaseAssignmentSubmission: # grades " + numItemsString); submission.setAttribute("numberofgrades", numItemsString); for (int x = 0; x < m_grades.size(); x++) { attributeString = "grade" + x; itemString = (String) m_grades.get(x); if (itemString != null) { submission.setAttribute(attributeString, itemString); } } // SAVE THE SUBMITTERS numItemsString = "" + m_submitters.size(); if (M_log.isDebugEnabled()) M_log.debug(this + " BaseAssignmentSubmission: # submitters " + numItemsString); submission.setAttribute("numberofsubmitters", numItemsString); for (int x = 0; x < m_submitters.size(); x++) { attributeString = "submitter" + x; itemString = (String) m_submitters.get(x); if (itemString != null) { submission.setAttribute(attributeString, itemString); } } if (M_log.isDebugEnabled()) M_log.debug(this + " BaseAssignmentSubmission: SAVED SUBMITTERS"); // SAVE THE FEEDBACK ATTACHMENTS numItemsString = "" + m_feedbackAttachments.size(); submission.setAttribute("numberoffeedbackattachments", numItemsString); if (M_log.isDebugEnabled()) M_log.debug("DB : DbCachedStorage : DbCachedAssignmentSubmission : entering fb attach loop : size : " + numItemsString); for (int x = 0; x < m_feedbackAttachments.size(); x++) { attributeString = "feedbackattachment" + x; tempReference = (Reference) m_feedbackAttachments.get(x); itemString = tempReference.getReference(); if (itemString != null) { submission.setAttribute(attributeString, itemString); } } if (M_log.isDebugEnabled()) M_log.debug(this + " BaseAssignmentSubmission: SAVED FEEDBACK ATTACHMENTS"); // SAVE THE SUBMITTED ATTACHMENTS numItemsString = "" + m_submittedAttachments.size(); submission.setAttribute("numberofsubmittedattachments", numItemsString); for (int x = 0; x < m_submittedAttachments.size(); x++) { attributeString = "submittedattachment" + x; tempReference = (Reference) m_submittedAttachments.get(x); itemString = tempReference.getReference(); if (itemString != null) { submission.setAttribute(attributeString, itemString); } } if (M_log.isDebugEnabled()) M_log.debug(this + " BaseAssignmentSubmission: SAVED SUBMITTED ATTACHMENTS"); // SAVE THE PROPERTIES m_properties.toXml(doc, stack); stack.pop(); FormattedText.encodeFormattedTextAttribute(submission, "submittedtext", m_submittedText); FormattedText.encodeFormattedTextAttribute(submission, "feedbackcomment", m_feedbackComment); FormattedText.encodeFormattedTextAttribute(submission, "feedbacktext", m_feedbackText); if (M_log.isDebugEnabled()) M_log.debug(this + " BaseAssignmentSubmission: LEAVING TOXML"); return submission; }// toXml protected void setAll(AssignmentSubmission submission) { if (contentReviewService != null) { m_reviewScore = submission.getReviewScore(); // The report given by the content review service m_reviewReport = submission.getReviewReport(); // The status of the review service m_reviewStatus = submission.getReviewStatus(); // Error msg, if any from review service m_reviewError = submission.getReviewError(); } m_id = submission.getId(); m_context = submission.getContext(); m_assignment = submission.getAssignmentId(); m_grade = submission.getGrade(); m_submitters = submission.getSubmitterIds(); m_submitted = submission.getSubmitted(); m_timeSubmitted = submission.getTimeSubmitted(); m_timeReturned = submission.getTimeReturned(); m_timeLastModified = submission.getTimeLastModified(); m_submittedAttachments = submission.getSubmittedAttachments(); m_feedbackAttachments = submission.getFeedbackAttachments(); m_submittedText = submission.getSubmittedText(); m_submitterId = submission.getSubmitterId(); m_submissionLog = submission.getSubmissionLog(); m_grades = submission.getGrades(); m_feedbackComment = submission.getFeedbackComment(); m_feedbackText = submission.getFeedbackText(); m_returned = submission.getReturned(); m_graded = submission.getGraded(); m_gradeReleased = submission.getGradeReleased(); m_honorPledgeFlag = submission.getHonorPledgeFlag(); m_properties = new BaseResourcePropertiesEdit(); m_properties.addAll(submission.getProperties()); } /** * Access the URL which can be used to access the resource. * * @return The URL which can be used to access the resource. */ public String getUrl() { return getAccessPoint(false) + Entity.SEPARATOR + "s" + Entity.SEPARATOR + m_context + Entity.SEPARATOR + m_id; } // getUrl /** * Access the internal reference which can be used to access the resource from within the system. * * @return The the internal reference which can be used to access the resource from within the system. */ public String getReference() { return submissionReference(m_context, m_id, m_assignment); } // getReference /** * @inheritDoc */ public String getReference(String rootProperty) { return getReference(); } /** * @inheritDoc */ public String getUrl(String rootProperty) { return getUrl(); } /** * Access the id of the resource. * * @return The id. */ public String getId() { return m_id; } /** * Access the resource's properties. * * @return The resource's properties. */ public ResourceProperties getProperties() { return m_properties; } /****************************************************************************************************************************************************************************************************************************************************** * AssignmentSubmission implementation *****************************************************************************************************************************************************************************************************************************************************/ /** * Access the AssignmentSubmission's context at the time of creation. * * @return String - the context string. */ public String getContext() { return m_context; } /** * Access the Assignment for this Submission * * @return the Assignment */ public Assignment getAssignment() { Assignment retVal = null; if (m_assignment != null) { retVal = m_assignmentStorage.get(m_assignment); } // track event //EventTrackingService.post(EventTrackingService.newEvent(AssignmentConstants.EVENT_ACCESS_ASSIGNMENT, retVal.getReference(), false)); return retVal; } /** * Access the Id for the Assignment for this Submission * * @return String - the Assignment Id */ public String getAssignmentId() { return m_assignment; } /** * Get whether this is a final submission. * * @return True if a final submission, false if still a draft. */ public boolean getSubmitted() { return m_submitted; } public String getSubmitterId() { return m_submitterId; } public List getSubmissionLog() { return m_submissionLog; } public List getGrades() { return m_grades; } public String getGradeForUser(String id) { if (m_grades != null) { Iterator<String> _it = m_grades.iterator(); while (_it.hasNext()) { String _s = _it.next(); if (_s.startsWith(id + "::")) { return _s.endsWith("null") ? null: _s.substring(_s.indexOf("::") + 2); } } } return null; } /** * * @return Array of User objects. */ public User[] getSubmitters() { List retVal = new ArrayList(); Assignment a = getAssignment(); if (a.isGroup()) { try { Site site = SiteService.getSite(a.getContext()); Group _g = site.getGroup(m_submitterId); if (_g != null) { Iterator<Member> _members = _g.getMembers().iterator(); while (_members.hasNext()) { Member _member = _members.next(); try { retVal.add(UserDirectoryService.getUser(_member.getUserId())); } catch (Exception e) { M_log.warn(" BaseAssignmentSubmission Group getSubmitters" + e.getMessage() + _member.getUserId()); } } } } catch (IdUnusedException _iue) { throw new IllegalStateException("Site ("+a.getContext()+") not found: "+_iue, _iue); } } else { for (int x = 0; x < m_submitters.size(); x++) { String userId = (String) m_submitters.get(x); try { retVal.add(UserDirectoryService.getUser(userId)); } catch (Exception e) { M_log.warn(" BaseAssignmentSubmission getSubmitters" + e.getMessage() + userId); } } } // compare users on sortname java.util.Collections.sort(retVal, new UserComparator()); // get the User[] array int size = retVal.size(); User[] rv = new User[size]; for(int k = 0; k<size; k++) { rv[k] = (User) retVal.get(k); } return rv; } /** * Access the list of Users who submitted this response to the Assignment. * * @return FlexStringArray of user ids. */ public List getSubmitterIds() { Assignment a = getAssignment(); if (a.isGroup()) { List retVal = new ArrayList(); try { Site site = SiteService.getSite(a.getContext()); Group _g = site.getGroup(m_submitterId); if (_g != null) { Iterator<Member> _members = _g.getMembers().iterator(); while (_members.hasNext()) { Member _member = _members.next(); retVal.add(_member.getUserId()); } } return retVal; } catch (IdUnusedException _iue) { return null; } } else { return m_submitters; } } /** * {@inheritDoc} */ public String getSubmitterIdString () { String rv = ""; if (m_submitters != null) { for (int j = 0; j < m_submitters.size(); j++) { rv = rv.concat((String) m_submitters.get(j)); } } return rv; } /** * Set the time at which this response was submitted; null signifies the response is unsubmitted. * * @return Time of submission. */ public Time getTimeSubmitted() { return m_timeSubmitted; } /** * @inheritDoc */ public String getTimeSubmittedString() { if ( m_timeSubmitted == null ) return ""; else return m_timeSubmitted.toStringLocalFull(); } /** * Get whether the grade has been released. * * @return True if the Submissions's grade has been released, false otherwise. */ public boolean getGradeReleased() { return m_gradeReleased; } /** * Access the grade recieved. * * @return The Submission's grade.. */ public String getGrade() { return getGrade(true); } /** * {@inheritDoc} */ public String getGrade(boolean overrideWithGradebookValue) { String rv = m_grade; if (!overrideWithGradebookValue) { // use assignment submission grade return m_grade; } else { // use grade from associated Gradebook Assignment m = getAssignment(); String gAssignmentName = StringUtils.trimToNull(m.getProperties().getProperty(AssignmentService.PROP_ASSIGNMENT_ASSOCIATE_GRADEBOOK_ASSIGNMENT)); if (gAssignmentName != null) { GradebookService g = (GradebookService) ComponentManager.get("org.sakaiproject.service.gradebook.GradebookService"); String gradebookUid = m.getContext(); // return student score from Gradebook String userId = m_submitterId; try { // add the grade permission ("gradebook.gradeAll", "gradebook.gradeSection", "gradebook.editAssignments", or "gradebook.viewOwnGrades") in order to use g.getAssignmentScoreString() SecurityService.pushAdvisor( new MySecurityAdvisor( SessionManager.getCurrentSessionUserId(), new ArrayList<String>(Arrays.asList("gradebook.gradeAll", "gradebook.gradeSection", "gradebook.editAssignments", "gradebook.viewOwnGrades")), gradebookUid)); if (g.isGradebookDefined(gradebookUid) && g.isAssignmentDefined(gradebookUid, gAssignmentName)) { String gString = StringUtils.trimToNull(g.getAssignmentScoreString(gradebookUid, gAssignmentName, userId)); if (gString != null) { rv = gString; } } } catch (Exception e) { M_log.warn(" BaseAssignmentSubmission getGrade getAssignmentScoreString from GradebookService " + e.getMessage() + " context=" + m_context + " assignment id=" + m_assignment + " userId=" + userId + " gAssignmentName=" + gAssignmentName); } finally { // remove advisor SecurityService.popAdvisor(); } } } return rv; } /** * Access the grade recieved. * * @return The Submission's grade.. */ public String getGradeDisplay() { Assignment m = getAssignment(); String grade = getGrade(); if (m.getContent().getTypeOfGrade() == Assignment.SCORE_GRADE_TYPE) { if (grade != null && grade.length() > 0 && !"0".equals(grade)) { String one_decimal_gradePoint = ""; try { Integer.parseInt(grade); // if point grade, display the grade with one decimal place one_decimal_gradePoint = grade.substring(0, grade.length() - 1) + "." + grade.substring(grade.length() - 1); } catch (NumberFormatException e) { try { Float.parseFloat(grade); one_decimal_gradePoint = grade; } catch (Exception e1) { return grade; } } // get localized number format NumberFormat nbFormat = NumberFormat.getInstance(); try { Locale locale = null; ResourceLoader rb = new ResourceLoader(); locale = rb.getLocale(); nbFormat = NumberFormat.getNumberInstance(locale); } catch (Exception e) { M_log.warn("Error while retrieving local number format, using default ", e); } nbFormat.setMaximumFractionDigits(1); nbFormat.setMinimumFractionDigits(1); nbFormat.setGroupingUsed(false); // show grade in localized number format try { Double dblGrade = new Double(one_decimal_gradePoint); one_decimal_gradePoint = nbFormat.format(dblGrade); } catch (Exception e) { return grade; } return one_decimal_gradePoint; } else { return StringUtils.trimToEmpty(grade); } } else if (m.getContent().getTypeOfGrade() == Assignment.UNGRADED_GRADE_TYPE) { String ret = ""; if (grade != null) { if (grade.equalsIgnoreCase("gen.nograd")) ret = rb.getString("gen.nograd"); } return ret; } else if (m.getContent().getTypeOfGrade() == Assignment.PASS_FAIL_GRADE_TYPE) { String ret = rb.getString("ungra"); if (grade != null) { if (grade.equalsIgnoreCase("Pass")) ret = rb.getString("pass"); else if (grade.equalsIgnoreCase("Fail")) ret = rb.getString("fail"); } return ret; } else if (m.getContent().getTypeOfGrade() == Assignment.CHECK_GRADE_TYPE) { String ret = rb.getString("ungra"); if (grade != null) { if (grade.equalsIgnoreCase("Checked")) ret = rb.getString("gen.checked"); } return ret; } else { if (grade != null && grade.length() > 0) { return StringUtils.trimToEmpty(grade); } else { // return "ungraded" in stead return rb.getString("ungra"); } } } /** * Get the time of last modification; * * @return The time of last modification. */ public Time getTimeLastModified() { return m_timeLastModified; } /** * Text submitted in response to the Assignment. * * @return The text of the submission. */ public String getSubmittedText() { return m_submittedText; } /** * Access the list of attachments to this response to the Assignment. * * @return ReferenceVector of the list of attachments as Reference objects; */ public List getSubmittedAttachments() { return m_submittedAttachments; } /** * Get the general comments by the grader * * @return The text of the grader's comments; may be null. */ public String getFeedbackComment() { return m_feedbackComment; } /** * Access the text part of the instructors feedback; usually an annotated copy of the submittedText * * @return The text of the grader's feedback. */ public String getFeedbackText() { return m_feedbackText; } /** * Access the formatted text part of the instructors feedback; usually an annotated copy of the submittedText * * @return The formatted text of the grader's feedback. */ public String getFeedbackFormattedText() { if (m_feedbackText == null || m_feedbackText.length() == 0) return m_feedbackText; String value = fixAssignmentFeedback(m_feedbackText); StringBuffer buf = new StringBuffer(value); int pos = -1; while ((pos = buf.indexOf("{{")) != -1) { buf.replace(pos, pos + "{{".length(), "<span class='highlight'>"); } while ((pos = buf.indexOf("}}")) != -1) { buf.replace(pos, pos + "}}".length(), "</span>"); } return FormattedText.escapeHtmlFormattedText(buf.toString()); } /** * Apply the fix to pre 1.1.05 assignments submissions feedback. */ private String fixAssignmentFeedback(String value) { if (value == null || value.length() == 0) return value; StringBuffer buf = new StringBuffer(value); int pos = -1; // <br/> -> \n while ((pos = buf.indexOf("<br/>")) != -1) { buf.replace(pos, pos + "<br/>".length(), "\n"); } // <span class='chefAlert'>( -> {{ while ((pos = buf.indexOf("<span class='chefAlert'>(")) != -1) { buf.replace(pos, pos + "<span class='chefAlert'>(".length(), "{{"); } // )</span> -> }} while ((pos = buf.indexOf(")</span>")) != -1) { buf.replace(pos, pos + ")</span>".length(), "}}"); } while ((pos = buf.indexOf("<ins>")) != -1) { buf.replace(pos, pos + "<ins>".length(), "{{"); } while ((pos = buf.indexOf("</ins>")) != -1) { buf.replace(pos, pos + "</ins>".length(), "}}"); } return buf.toString(); } // fixAssignmentFeedback /** * Access the list of attachments returned to the students in the process of grading this assignment; usually a modified or annotated version of the attachment submitted. * * @return ReferenceVector of the Resource objects pointing to the attachments. */ public List getFeedbackAttachments() { return m_feedbackAttachments; } /** * Get whether this Submission was rejected by the grader. * * @return True if this response was rejected by the grader, false otherwise. */ public boolean getReturned() { return m_returned; } /** * Get whether this Submission has been graded. * * @return True if the submission has been graded, false otherwise. */ public boolean getGraded() { return m_graded; } /** * Get the time on which the graded submission was returned; null means the response is not yet graded. * * @return the time (may be null) */ public Time getTimeReturned() { return m_timeReturned; } /** * Access the checked status of the honor pledge flag. * * @return True if the honor pledge is checked, false otherwise. */ public boolean getHonorPledgeFlag() { return m_honorPledgeFlag; } /** * Returns the status of the submission : Not Started, submitted, returned or graded. * * @return The Submission's status. */ public String getStatus() { boolean allowGrade = allowGradeSubmission(getReference()); String retVal = ""; Time submitTime = getTimeSubmitted(); Time returnTime = getTimeReturned(); Time lastModTime = getTimeLastModified(); if (getSubmitted() || (!getSubmitted() && allowGrade)) { if (submitTime != null) { if (getReturned()) { if (returnTime != null && returnTime.before(submitTime)) { if (!getGraded()) { retVal = rb.getString("gen.resub") + " " + submitTime.toStringLocalFull(); if (submitTime.after(getAssignment().getDueTime())) retVal = retVal + rb.getString("gen.late2"); } else retVal = rb.getString("gen.returned"); } else retVal = rb.getString("gen.returned"); } else if (getGraded() && allowGrade) { retVal = getGradeOrComment(); } else { if (allowGrade) { // ungraded submission retVal = rb.getString("ungra"); } else { // submitted retVal = rb.getString("gen.subm4"); if(submitTime != null) { retVal = rb.getString("gen.subm4") + " " + submitTime.toStringLocalFull(); } } } } else { if (getReturned()) { // instructor can return grading to non-submitted user retVal = rb.getString("gen.returned"); } else if (getGraded() && allowGrade) { // instructor can grade non-submitted ones retVal = getGradeOrComment(); } else { if (allowGrade) { // show "no submission" to graders retVal = rb.getString("listsub.nosub"); } else { // show "not started" to students retVal = rb.getString("gen.notsta"); } } } } else { if (getGraded()) { if (getReturned()) { if (lastModTime != null && returnTime != null && lastModTime.after(TimeService.newTime(returnTime.getTime() + 1000 * 10)) && !allowGrade) { // working on a returned submission now retVal = rb.getString("gen.dra2") + " " + rb.getString("gen.inpro"); } else { // not submitted submmission has been graded and returned retVal = rb.getString("gen.returned"); } } else if (allowGrade) // grade saved but not release yet, show this to graders retVal = getGradeOrComment(); } else { if (allowGrade) retVal = rb.getString("ungra"); else // submission saved, not submitted. retVal = rb.getString("gen.dra2") + " " + rb.getString("gen.inpro"); } } return retVal; } private String getGradeOrComment() { String retVal; if (getGrade() != null && getGrade().length() > 0) retVal = rb.getString("grad3"); else retVal = rb.getString("gen.commented"); return retVal; } /** * Are these objects equal? If they are both AssignmentSubmission objects, and they have matching id's, they are. * * @return true if they are equal, false if not. */ public boolean equals(Object obj) { if (!(obj instanceof AssignmentSubmission)) return false; return ((AssignmentSubmission) obj).getId().equals(getId()); } // equals /** * Make a hash code that reflects the equals() logic as well. We want two objects, even if different instances, if they have the same id to hash the same. */ public int hashCode() { return getId().hashCode(); } // hashCode /** * Compare this object with the specified object for order. * * @return A negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object. */ public int compareTo(Object obj) { if (!(obj instanceof AssignmentSubmission)) throw new ClassCastException(); // if the object are the same, say so if (obj == this) return 0; // start the compare by comparing their sort names int compare = getTimeSubmitted().toString().compareTo(((AssignmentSubmission) obj).getTimeSubmitted().toString()); // if these are the same if (compare == 0) { // sort based on (unique) id compare = getId().compareTo(((AssignmentSubmission) obj).getId()); } return compare; } // compareTo /** * {@inheritDoc} */ public int getResubmissionNum() { String numString = StringUtils.trimToNull(m_properties.getProperty(AssignmentSubmission.ALLOW_RESUBMIT_NUMBER)); return numString != null?Integer.valueOf(numString).intValue():0; } /** * {@inheritDoc} */ public Time getCloseTime() { String closeTimeString = StringUtils.trimToNull(m_properties.getProperty(AssignmentSubmission.ALLOW_RESUBMIT_CLOSETIME)); if (closeTimeString != null && getResubmissionNum() != 0) { // return the close time if it is set return TimeService.newTime(Long.parseLong(closeTimeString)); } else { // else use the assignment close time setting Assignment a = getAssignment(); return a!=null?a.getCloseTime():null; } } } // AssignmentSubmission /*************************************************************************** * AssignmentSubmissionEdit implementation **************************************************************************/ /** * <p> * BaseAssignmentSubmissionEdit is an implementation of the CHEF AssignmentSubmissionEdit object. * </p> * * @author University of Michigan, CHEF Software Development Team */ public class BaseAssignmentSubmissionEdit extends BaseAssignmentSubmission implements AssignmentSubmissionEdit, SessionBindingListener { /** The event code for this edit. */ protected String m_event = null; /** Active flag. */ protected boolean m_active = false; /** * Construct from another AssignmentSubmission object. * * @param AssignmentSubmission * The AssignmentSubmission object to use for values. */ public BaseAssignmentSubmissionEdit(AssignmentSubmission assignmentSubmission) { super(assignmentSubmission); } // BaseAssignmentSubmissionEdit /** * Construct. * * @param id * The AssignmentSubmission id. */ public BaseAssignmentSubmissionEdit(String id, String assignmentId, String submitterId, String submitTime, String submitted, String graded) { super(id, assignmentId, submitterId, submitTime, submitted, graded); } // BaseAssignmentSubmissionEdit /** * Construct from information in XML. * * @param el * The XML DOM Element definining the AssignmentSubmission. */ public BaseAssignmentSubmissionEdit(Element el) { super(el); } // BaseAssignmentSubmissionEdit /** * Clean up. */ protected void finalize() { // catch the case where an edit was made but never resolved if (m_active) { cancelEdit(this); } } // finalize /** * Set the context at the time of creation. * * @param context - * the context string. */ public void setContext(String context) { m_context = context; } /** * Set the Assignment for this Submission * * @param assignment - * the Assignment */ public void setAssignment(Assignment assignment) { if (assignment != null) { m_assignment = assignment.getId(); } else m_assignment = ""; } /** * Set whether this is a final submission. * * @param submitted - * True if a final submission, false if still a draft. */ public void setSubmitted(boolean submitted) { m_submitted = submitted; } /** * Add a User to the submitters list. * * @param submitter - * the User to add. */ public void addSubmitter(User submitter) { if (submitter != null) m_submitters.add(submitter.getId()); } public void setSubmitterId(String id) { m_submitterId = id; } public void addSubmissionLogEntry(String entry) { if (m_submissionLog != null) m_submissionLog.add(entry); } public void addGradeForUser(String uid, String grade) { if (m_grades != null) { Iterator<String> _it = m_grades.iterator(); while (_it.hasNext()) { String _val = _it.next(); if (_val.startsWith(uid + "::")) { m_grades.remove(_val); break; } } if (grade != null && !(grade.equals("null"))) { m_grades.add(uid + "::" + grade); } } } /** * Remove an User from the submitter list * * @param submitter - * the User to remove. */ public void removeSubmitter(User submitter) { if (submitter != null) m_submitters.remove(submitter.getId()); } /** * Remove all user from the submitter list */ public void clearSubmitters() { m_submitters.clear(); } /** * Set the time at which this response was submitted; setting it to null signifies the response is unsubmitted. * * @param timeSubmitted - * Time of submission. */ public void setTimeSubmitted(Time value) { m_timeSubmitted = value; } /** * Set whether the grade has been released. * * @param released - * True if the Submissions's grade has been released, false otherwise. */ public void setGradeReleased(boolean released) { m_gradeReleased = released; } /** * Sets the grade for the Submisssion. * * @param grade - * The Submission's grade. */ public void setGrade(String grade) { m_grade = grade; } /** * Text submitted in response to the Assignment. * * @param submissionText - * The text of the submission. */ public void setSubmittedText(String value) { m_submittedText = value; } /** * Add an attachment to the list of submitted attachments. * * @param attachment - * The Reference object pointing to the attachment. */ public void addSubmittedAttachment(Reference attachment) { if (attachment != null) m_submittedAttachments.add(attachment); } /** * Remove an attachment from the list of submitted attachments * * @param attachment - * The Reference object pointing to the attachment. */ public void removeSubmittedAttachment(Reference attachment) { if (attachment != null) m_submittedAttachments.remove(attachment); } /** * Remove all submitted attachments. */ public void clearSubmittedAttachments() { m_submittedAttachments.clear(); } /** * Set the general comments by the grader. * * @param comment - * the text of the grader's comments; may be null. */ public void setFeedbackComment(String value) { m_feedbackComment = value; } /** * Set the text part of the instructors feedback; usually an annotated copy of the submittedText * * @param feedback - * The text of the grader's feedback. */ public void setFeedbackText(String value) { m_feedbackText = value; } /** * Add an attachment to the list of feedback attachments. * * @param attachment - * The Resource object pointing to the attachment. */ public void addFeedbackAttachment(Reference attachment) { if (attachment != null) m_feedbackAttachments.add(attachment); } /** * Remove an attachment from the list of feedback attachments. * * @param attachment - * The Resource pointing to the attachment to remove. */ public void removeFeedbackAttachment(Reference attachment) { if (attachment != null) m_feedbackAttachments.remove(attachment); } /** * Remove all feedback attachments. */ public void clearFeedbackAttachments() { m_feedbackAttachments.clear(); } /** * Set whether this Submission was rejected by the grader. * * @param returned - * true if this response was rejected by the grader, false otherwise. */ public void setReturned(boolean value) { m_returned = value; } /** * Set whether this Submission has been graded. * * @param graded - * true if the submission has been graded, false otherwise. */ public void setGraded(boolean value) { m_graded = value; } /** * Set the time at which the graded Submission was returned; setting it to null means it is not yet graded. * * @param timeReturned - * The time at which the graded Submission was returned. */ public void setTimeReturned(Time timeReturned) { m_timeReturned = timeReturned; } /** * Set the checked status of the honor pledge flag. * * @param honorPledgeFlag - * True if the honor pledge is checked, false otherwise. */ public void setHonorPledgeFlag(boolean honorPledgeFlag) { m_honorPledgeFlag = honorPledgeFlag; } /** * Set the time last modified. * * @param lastmod - * The Time at which the Assignment was last modified. */ public void setTimeLastModified(Time lastmod) { if (lastmod != null) m_timeLastModified = lastmod; } public void postAttachment(List attachments){ //Send the attachment to the review service try { ContentResource cr = getFirstAcceptableAttachement(attachments); Assignment ass = this.getAssignment(); if (ass != null) { contentReviewService.queueContent(null, null, ass.getReference(), cr.getId()); } else { // error, assignment couldn't be found. Log the error M_log.debug(this + " BaseAssignmentSubmissionEdit postAttachment: Unable to find assignment associated with submission id= " + this.m_id + " and assignment id=" + this.m_assignment); } } catch (QueueException qe) { M_log.warn(" BaseAssignmentSubmissionEdit postAttachment: Unable to add content to Content Review queue: " + qe.getMessage()); } catch (Exception e) { e.printStackTrace(); } } private ContentResource getFirstAcceptableAttachement(List attachments) { for( int i =0; i < attachments.size();i++ ) { Reference attachment = (Reference)attachments.get(i); try { ContentResource res = m_contentHostingService.getResource(attachment.getId()); if (contentReviewService.isAcceptableContent(res)) { return res; } } catch (PermissionException e) { // TODO Auto-generated catch block e.printStackTrace(); M_log.warn(":geFirstAcceptableAttachment " + e.getMessage()); } catch (IdUnusedException e) { // TODO Auto-generated catch block e.printStackTrace(); M_log.warn(":geFirstAcceptableAttachment " + e.getMessage()); } catch (TypeException e) { // TODO Auto-generated catch block e.printStackTrace(); M_log.warn(":geFirstAcceptableAttachment " + e.getMessage()); } } return null; } /** * Take all values from this object. * * @param AssignmentSubmission * The AssignmentSubmission object to take values from. */ protected void set(AssignmentSubmission assignmentSubmission) { setAll(assignmentSubmission); } // set /** * Access the event code for this edit. * * @return The event code for this edit. */ protected String getEvent() { return m_event; } /** * Set the event code for this edit. * * @param event * The event code for this edit. */ protected void setEvent(String event) { m_event = event; } /** * Access the resource's properties for modification * * @return The resource's properties. */ public ResourcePropertiesEdit getPropertiesEdit() { return m_properties; } // getPropertiesEdit /** * Enable editing. */ protected void activate() { m_active = true; } // activate /** * Check to see if the edit is still active, or has already been closed. * * @return true if the edit is active, false if it's been closed. */ public boolean isActiveEdit() { return m_active; } // isActiveEdit /** * Close the edit object - it cannot be used after this. */ protected void closeEdit() { m_active = false; } // closeEdit /****************************************************************************************************************************************************************************************************************************************************** * SessionBindingListener implementation *****************************************************************************************************************************************************************************************************************************************************/ public void valueBound(SessionBindingEvent event) { } public void valueUnbound(SessionBindingEvent event) { M_log.debug(this + " BaseAssignmentSubmissionEdit valueUnbound()"); // catch the case where an edit was made but never resolved if (m_active) { cancelEdit(this); } } // valueUnbound public void setReviewScore(int score) { this.m_reviewScore = score; } public void setReviewIconUrl(String url) { this.m_reviewIconUrl = url; } public void setReviewStatus(String status) { this.m_reviewStatus = status; } public void setReviewError(String error) { this.m_reviewError = error; } } // BaseAssignmentSubmissionEdit /********************************************************************************************************************************************************************************************************************************************************** * Assignment Storage *********************************************************************************************************************************************************************************************************************************************************/ protected interface AssignmentStorage { /** * Open. */ public void open(); /** * Close. */ public void close(); /** * Check if an Assignment by this id exists. * * @param id * The assignment id. * @return true if an Assignment by this id exists, false if not. */ public boolean check(String id); /** * Get the Assignment with this id, or null if not found. * * @param id * The Assignment id. * @return The Assignment with this id, or null if not found. */ public Assignment get(String id); /** * Get all Assignments. * * @return The list of all Assignments. */ public List getAll(String context); /** * Add a new Assignment with this id. * * @param id * The Assignment id. * @param context * The context. * @return The locked Assignment object with this id, or null if the id is in use. */ public AssignmentEdit put(String id, String context); /** * Get a lock on the Assignment with this id, or null if a lock cannot be gotten. * * @param id * The Assignment id. * @return The locked Assignment with this id, or null if this records cannot be locked. */ public AssignmentEdit edit(String id); /** * Commit the changes and release the lock. * * @param Assignment * The Assignment to commit. */ public void commit(AssignmentEdit assignment); /** * Cancel the changes and release the lock. * * @param Assignment * The Assignment to commit. */ public void cancel(AssignmentEdit assignment); /** * Remove this Assignment. * * @param Assignment * The Assignment to remove. */ public void remove(AssignmentEdit assignment); } // AssignmentStorage /********************************************************************************************************************************************************************************************************************************************************** * AssignmentContent Storage *********************************************************************************************************************************************************************************************************************************************************/ protected interface AssignmentContentStorage { /** * Open. */ public void open(); /** * Close. */ public void close(); /** * Check if a AssignmentContent by this id exists. * * @param id * The AssignmentContent id. * @return true if a AssignmentContent by this id exists, false if not. */ public boolean check(String id); /** * Get the AssignmentContent with this id, or null if not found. * * @param id * The AssignmentContent id. * @return The AssignmentContent with this id, or null if not found. */ public AssignmentContent get(String id); /** * Get all AssignmentContents. * * @return The list of all AssignmentContents. */ public List getAll(String context); /** * Add a new AssignmentContent with this id. * * @param id * The AssignmentContent id. * @param context * The context. * @return The locked AssignmentContent object with this id, or null if the id is in use. */ public AssignmentContentEdit put(String id, String context); /** * Get a lock on the AssignmentContent with this id, or null if a lock cannot be gotten. * * @param id * The AssignmentContent id. * @return The locked AssignmentContent with this id, or null if this records cannot be locked. */ public AssignmentContentEdit edit(String id); /** * Commit the changes and release the lock. * * @param AssignmentContent * The AssignmentContent to commit. */ public void commit(AssignmentContentEdit content); /** * Cancel the changes and release the lock. * * @param AssignmentContent * The AssignmentContent to commit. */ public void cancel(AssignmentContentEdit content); /** * Remove this AssignmentContent. * * @param AssignmentContent * The AssignmentContent to remove. */ public void remove(AssignmentContentEdit content); } // AssignmentContentStorage /********************************************************************************************************************************************************************************************************************************************************** * AssignmentSubmission Storage *********************************************************************************************************************************************************************************************************************************************************/ protected interface AssignmentSubmissionStorage { /** * Open. */ public void open(); /** * Close. */ public void close(); /** * Check if a AssignmentSubmission by this id exists. * * @param id * The AssignmentSubmission id. * @return true if a AssignmentSubmission by this id exists, false if not. */ public boolean check(String id); /** * Get the AssignmentSubmission with this id, or null if not found. * * @param id * The AssignmentSubmission id. * @return The AssignmentSubmission with this id, or null if not found. */ public AssignmentSubmission get(String id); /** * Get the AssignmentSubmission with this assignment id and user id. * * @param assignmentId * The Assignment id. * @param userId * The user id * @return The AssignmentSubmission with this id, or null if not found. */ public AssignmentSubmission get(String assignmentId, String userId); /** * Get the number of submissions which has been submitted. * * @param assignmentId - * the id of Assignment who's submissions you would like. * @return List over all the submissions for an Assignment. */ public int getSubmittedSubmissionsCount(String assignmentId); /** * Get the number of submissions which has not been submitted and graded. * * @param assignment - * the Assignment who's submissions you would like. * @return List over all the submissions for an Assignment. */ public int getUngradedSubmissionsCount(String assignmentId); /** * Get all AssignmentSubmissions. * * @return The list of all AssignmentSubmissions. */ public List getAll(String context); /** * Add a new AssignmentSubmission with this id. * * @param id * The AssignmentSubmission id. * @param context * The context. * @return The locked AssignmentSubmission object with this id, or null if the id is in use. */ public AssignmentSubmissionEdit put(String id, String assignmentId, String submitterId, String submitTime, String submitted, String graded); /** * Get a lock on the AssignmentSubmission with this id, or null if a lock cannot be gotten. * * @param id * The AssignmentSubmission id. * @return The locked AssignmentSubmission with this id, or null if this records cannot be locked. */ public AssignmentSubmissionEdit edit(String id); /** * Commit the changes and release the lock. * * @param AssignmentSubmission * The AssignmentSubmission to commit. */ public void commit(AssignmentSubmissionEdit submission); /** * Cancel the changes and release the lock. * * @param AssignmentSubmission * The AssignmentSubmission to commit. */ public void cancel(AssignmentSubmissionEdit submission); /** * Remove this AssignmentSubmission. * * @param AssignmentSubmission * The AssignmentSubmission to remove. */ public void remove(AssignmentSubmissionEdit submission); } // AssignmentSubmissionStorage /** * Utility function which returns the string representation of the long value of the time object. * * @param t - * the Time object. * @return A String representation of the long value of the time object. */ protected String getTimeString(Time t) { String retVal = ""; if (t != null) retVal = t.toString(); return retVal; } /** * Utility function which returns a string from a boolean value. * * @param b - * the boolean value. * @return - "True" if the input value is true, "false" otherwise. */ protected String getBoolString(boolean b) { if (b) return "true"; else return "false"; } /** * Utility function which returns a boolean value from a string. * * @param s - * The input string. * @return the boolean true if the input string is "true", false otherwise. */ protected boolean getBool(String s) { boolean retVal = false; if (s != null) { if (s.equalsIgnoreCase("true")) retVal = true; } return retVal; } /** * Utility function which converts a string into a chef time object. * * @param timeString - * String version of a time in long format, representing the standard ms since the epoch, Jan 1, 1970 00:00:00. * @return A chef Time object. */ protected Time getTimeObject(String timeString) { Time aTime = null; timeString = StringUtils.trimToNull(timeString); if (timeString != null) { try { aTime = TimeService.newTimeGmt(timeString); } catch (Exception e) { M_log.warn(":geTimeObject " + e.getMessage()); try { long longTime = Long.parseLong(timeString); aTime = TimeService.newTime(longTime); } catch (Exception ee) { M_log.warn(" getTimeObject Base Exception creating time object from xml file : " + ee.getMessage() + " timeString=" + timeString); } } } return aTime; } protected String getGroupNameFromContext(String context) { String retVal = ""; if (context != null) { int index = context.indexOf("group-"); if (index != -1) { String[] parts = StringUtil.splitFirst(context, "-"); if (parts.length > 1) { retVal = parts[1]; } } else { retVal = context; } } return retVal; } /********************************************************************************************************************************************************************************************************************************************************** * StorageUser implementations (no container) *********************************************************************************************************************************************************************************************************************************************************/ /********************************************************************************************************************************************************************************************************************************************************** * AssignmentStorageUser implementation *********************************************************************************************************************************************************************************************************************************************************/ protected class AssignmentStorageUser implements SingleStorageUser, SAXEntityReader { private Map<String,Object> m_services; /** * Construct a new resource given just an id. * * @param container * The Resource that is the container for the new resource (may be null). * @param id * The id for the new object. * @param others * (options) array of objects to load into the Resource's fields. * @return The new resource. */ public Entity newResource(Entity container, String id, Object[] others) { return new BaseAssignment(id, (String) others[0]); } /** * Construct a new resource, from an XML element. * * @param container * The Resource that is the container for the new resource (may be null). * @param element * The XML. * @return The new resource from the XML. */ public Entity newResource(Entity container, Element element) { return new BaseAssignment(element); } /** * Construct a new resource from another resource of the same type. * * @param container * The Resource that is the container for the new resource (may be null). * @param other * The other resource. * @return The new resource as a copy of the other. */ public Entity newResource(Entity container, Entity other) { return new BaseAssignment((Assignment) other); } /** * Construct a new resource given just an id. * * @param container * The Resource that is the container for the new resource (may be null). * @param id * The id for the new object. * @param others * (options) array of objects to load into the Resource's fields. * @return The new resource. */ public Edit newResourceEdit(Entity container, String id, Object[] others) { BaseAssignmentEdit e = new BaseAssignmentEdit(id, (String) others[0]); e.activate(); return e; } /** * Construct a new resource, from an XML element. * * @param container * The Resource that is the container for the new resource (may be null). * @param element * The XML. * @return The new resource from the XML. */ public Edit newResourceEdit(Entity container, Element element) { BaseAssignmentEdit e = new BaseAssignmentEdit(element); e.activate(); return e; } /** * Construct a new resource from another resource of the same type. * * @param container * The Resource that is the container for the new resource (may be null). * @param other * The other resource. * @return The new resource as a copy of the other. */ public Edit newResourceEdit(Entity container, Entity other) { BaseAssignmentEdit e = new BaseAssignmentEdit((Assignment) other); e.activate(); return e; } /** * Collect the fields that need to be stored outside the XML (for the resource). * * @return An array of field values to store in the record outside the XML (for the resource). */ public Object[] storageFields(Entity r) { Object rv[] = new Object[1]; rv[0] = ((Assignment) r).getContext(); return rv; } /*********************************************************************** * SAXEntityReader */ /* * (non-Javadoc) * * @see org.sakaiproject.util.SAXEntityReader#getDefaultHandler(java.util.Map) */ public DefaultEntityHandler getDefaultHandler(final Map<String, Object> services) { return new DefaultEntityHandler() { /* * (non-Javadoc) * * @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String, * java.lang.String, java.lang.String, * org.xml.sax.Attributes) */ @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { if (doStartElement(uri, localName, qName, attributes)) { if (entity == null) { if ("assignment".equals(qName)) { BaseAssignment ba = new BaseAssignment(); entity = ba; setContentHandler(ba.getContentHandler(services), uri, localName, qName, attributes); } else { M_log.warn(" AssignmentStorageUser getDefaultHandler startElement Unexpected Element in XML [" + qName + "]"); } } } } }; } /* * (non-Javadoc) * * @see org.sakaiproject.util.SAXEntityReader#getServices() */ public Map<String, Object> getServices() { if (m_services == null) { m_services = new HashMap<String, Object>(); } return m_services; } }// AssignmentStorageUser /********************************************************************************************************************************************************************************************************************************************************** * AssignmentContentStorageUser implementation *********************************************************************************************************************************************************************************************************************************************************/ protected class AssignmentContentStorageUser implements SingleStorageUser, SAXEntityReader { private Map<String,Object> m_services; /** * Construct a new resource given just an id. * * @param container * The Resource that is the container for the new resource (may be null). * @param id * The id for the new object. * @param others * (options) array of objects to load into the Resource's fields. * @return The new resource. */ public Entity newResource(Entity container, String id, Object[] others) { return new BaseAssignmentContent(id, (String) others[0]); } /** * Construct a new resource, from an XML element. * * @param container * The Resource that is the container for the new resource (may be null). * @param element * The XML. * @return The new resource from the XML. */ public Entity newResource(Entity container, Element element) { return new BaseAssignmentContent(element); } /** * Construct a new resource from another resource of the same type. * * @param container * The Resource that is the container for the new resource (may be null). * @param other * The other resource. * @return The new resource as a copy of the other. */ public Entity newResource(Entity container, Entity other) { return new BaseAssignmentContent((AssignmentContent) other); } /** * Construct a new rsource given just an id. * * @param container * The Resource that is the container for the new resource (may be null). * @param id * The id for the new object. * @param others * (options) array of objects to load into the Resource's fields. * @return The new resource. */ public Edit newResourceEdit(Entity container, String id, Object[] others) { BaseAssignmentContentEdit e = new BaseAssignmentContentEdit(id, (String) others[0]); e.activate(); return e; } /** * Construct a new resource, from an XML element. * * @param container * The Resource that is the container for the new resource (may be null). * @param element * The XML. * @return The new resource from the XML. */ public Edit newResourceEdit(Entity container, Element element) { BaseAssignmentContentEdit e = new BaseAssignmentContentEdit(element); e.activate(); return e; } /** * Construct a new resource from another resource of the same type. * * @param container * The Resource that is the container for the new resource (may be null). * @param other * The other resource. * @return The new resource as a copy of the other. */ public Edit newResourceEdit(Entity container, Entity other) { BaseAssignmentContentEdit e = new BaseAssignmentContentEdit((AssignmentContent) other); e.activate(); return e; } /** * Collect the fields that need to be stored outside the XML (for the resource). * * @return An array of field values to store in the record outside the XML (for the resource). */ public Object[] storageFields(Entity r) { Object rv[] = new Object[1]; rv[0] = ((AssignmentContent) r).getCreator(); return rv; } /*********************************************************************** * SAXEntityReader */ /* * (non-Javadoc) * * @see org.sakaiproject.util.SAXEntityReader#getDefaultHandler(java.util.Map) */ public DefaultEntityHandler getDefaultHandler(final Map<String, Object> services) { return new DefaultEntityHandler() { /* * (non-Javadoc) * * @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String, * java.lang.String, java.lang.String, * org.xml.sax.Attributes) */ @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { if (doStartElement(uri, localName, qName, attributes)) { if (entity == null) { if ("content".equals(qName)) { BaseAssignmentContent bac = new BaseAssignmentContent(); entity = bac; setContentHandler(bac.getContentHandler(services), uri, localName, qName, attributes); } else { M_log.warn(" AssignmentContentStorageUser getDefaultEntityHandler startElement Unexpected Element in XML [" + qName + "]"); } } } } }; } /* * (non-Javadoc) * * @see org.sakaiproject.util.SAXEntityReader#getServices() */ public Map<String, Object> getServices() { if (m_services == null) { m_services = new HashMap<String, Object>(); } return m_services; } }// ContentStorageUser /********************************************************************************************************************************************************************************************************************************************************** * SubmissionStorageUser implementation *********************************************************************************************************************************************************************************************************************************************************/ protected class AssignmentSubmissionStorageUser implements SingleStorageUser, SAXEntityReader { private Map<String,Object> m_services; /** * Construct a new resource given just an id. * * @param container * The Resource that is the container for the new resource (may be null). * @param id * The id for the new object. * @param others * (options) array of objects to load into the Resource's fields. * @return The new resource. */ public Entity newResource(Entity container, String id, Object[] others) { return new BaseAssignmentSubmission(id, (String) others[0], (String) others[1], (String) others[2], (String) others[3], (String) others[4]); } /** * Construct a new resource, from an XML element. * * @param container * The Resource that is the container for the new resource (may be null). * @param element * The XML. * @return The new resource from the XML. */ public Entity newResource(Entity container, Element element) { return new BaseAssignmentSubmission(element); } /** * Construct a new resource from another resource of the same type. * * @param container * The Resource that is the container for the new resource (may be null). * @param other * The other resource. * @return The new resource as a copy of the other. */ public Entity newResource(Entity container, Entity other) { return new BaseAssignmentSubmission((AssignmentSubmission) other); } /** * Construct a new rsource given just an id. * * @param container * The Resource that is the container for the new resource (may be null). * @param id * The id for the new object. * @param others * (options) array of objects to load into the Resource's fields. * @return The new resource. */ public Edit newResourceEdit(Entity container, String id, Object[] others) { BaseAssignmentSubmissionEdit e = new BaseAssignmentSubmissionEdit(id, (String) others[0], (String) others[1], (String) others[2], (String) others[3], (String) others[4]); e.activate(); return e; } /** * Construct a new resource, from an XML element. * * @param container * The Resource that is the container for the new resource (may be null). * @param element * The XML. * @return The new resource from the XML. */ public Edit newResourceEdit(Entity container, Element element) { BaseAssignmentSubmissionEdit e = new BaseAssignmentSubmissionEdit(element); e.activate(); return e; } /** * Construct a new resource from another resource of the same type. * * @param container * The Resource that is the container for the new resource (may be null). * @param other * The other resource. * @return The new resource as a copy of the other. */ public Edit newResourceEdit(Entity container, Entity other) { BaseAssignmentSubmissionEdit e = new BaseAssignmentSubmissionEdit((AssignmentSubmission) other); e.activate(); return e; } /** * Collect the fields that need to be stored outside the XML (for the resource). * * @return An array of field values to store in the record outside the XML (for the resource). */ public Object[] storageFields(Entity r) { /*"context", "SUBMITTER_ID", "SUBMIT_TIME", "SUBMITTED", "GRADED"*/ Object rv[] = new Object[5]; rv[0] = ((AssignmentSubmission) r).getAssignmentId(); rv[1] = ((AssignmentSubmission) r).getSubmitterId(); Time submitTime = ((AssignmentSubmission) r).getTimeSubmitted(); rv[2] = (submitTime != null)?String.valueOf(submitTime.getTime()):null; rv[3] = Boolean.valueOf(((AssignmentSubmission) r).getSubmitted()).toString(); rv[4] = Boolean.valueOf(((AssignmentSubmission) r).getGraded()).toString(); return rv; } /*********************************************************************** * SAXEntityReader */ /* * (non-Javadoc) * * @see org.sakaiproject.util.SAXEntityReader#getDefaultHandler(java.util.Map) */ public DefaultEntityHandler getDefaultHandler(final Map<String, Object> services) { return new DefaultEntityHandler() { /* * (non-Javadoc) * * @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String, * java.lang.String, java.lang.String, * org.xml.sax.Attributes) */ @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { if (doStartElement(uri, localName, qName, attributes)) { if (entity == null) { if ("submission".equals(qName)) { BaseAssignmentSubmission bas = new BaseAssignmentSubmission(); entity = bas; setContentHandler(bas.getContentHandler(services), uri, localName, qName, attributes); } else { M_log.warn(" AssignmentSubmissionStorageUser getDefaultHandler startElement: Unexpected Element in XML [" + qName + "]"); } } } } }; } /* * (non-Javadoc) * * @see org.sakaiproject.util.SAXEntityReader#getServices() */ public Map<String, Object> getServices() { if (m_services == null) { m_services = new HashMap<String, Object>(); } return m_services; } }// SubmissionStorageUser /********************************************************************************************************************************************************************************************************************************************************** * CacheRefresher implementations (no container) *********************************************************************************************************************************************************************************************************************************************************/ /********************************************************************************************************************************************************************************************************************************************************** * AssignmentCacheRefresher implementation *********************************************************************************************************************************************************************************************************************************************************/ protected class AssignmentCacheRefresher implements CacheRefresher { /** * Get a new value for this key whose value has already expired in the cache. * * @param key * The key whose value has expired and needs to be refreshed. * @param oldValue * The old expired value of the key. * @return a new value for use in the cache for this key; if null, the entry will be removed. */ public Object refresh(Object key, Object oldValue, Event event) { // key is a reference, but our storage wants an id String id = assignmentId((String) key); // get whatever we have from storage for the cache for this vale Assignment assignment = m_assignmentStorage.get(id); M_log.debug(this + " AssignmentCacheRefresher:refresh(): " + key + " : " + id); return assignment; } // refresh }// AssignmentCacheRefresher /********************************************************************************************************************************************************************************************************************************************************** * AssignmentContentCacheRefresher implementation *********************************************************************************************************************************************************************************************************************************************************/ protected class AssignmentContentCacheRefresher implements CacheRefresher { /** * Get a new value for this key whose value has already expired in the cache. * * @param key * The key whose value has expired and needs to be refreshed. * @param oldValue * The old expired value of the key. * @return a new value for use in the cache for this key; if null, the entry will be removed. */ public Object refresh(Object key, Object oldValue, Event event) { // key is a reference, but our storage wants an id String id = contentId((String) key); // get whatever we have from storage for the cache for this vale AssignmentContent content = m_contentStorage.get(id); M_log.debug(this + " AssignmentContentCacheRefresher: refresh(): " + key + " : " + id); return content; } // refresh }// AssignmentContentCacheRefresher /********************************************************************************************************************************************************************************************************************************************************** * AssignmentSubmissionCacheRefresher implementation *********************************************************************************************************************************************************************************************************************************************************/ protected class AssignmentSubmissionCacheRefresher implements CacheRefresher { /** * Get a new value for this key whose value has already expired in the cache. * * @param key * The key whose value has expired and needs to be refreshed. * @param oldValue * The old expired value of the key. * @return a new value for use in the cache for this key; if null, the entry will be removed. */ public Object refresh(Object key, Object oldValue, Event event) { // key is a reference, but our storage wants an id String id = submissionId((String) key); // get whatever we have from storage for the cache for this vale AssignmentSubmission submission = m_submissionStorage.get(id); M_log.debug(this + " AssignmentSubmissionCacheRefresher:refresh(): " + key + " : " + id); return submission; } // refresh }// AssignmentSubmissionCacheRefresher private class UserComparator implements Comparator { public UserComparator() {} public int compare(Object o1, Object o2) { User _u1 = (User)o1; User _u2 = (User)o2; return _u1.compareTo(_u2); } } /** * the AssignmentComparator clas */ static private class AssignmentComparator implements Comparator { /** * the criteria */ String m_criteria = null; /** * the criteria */ String m_asc = null; /** * is group submission */ boolean m_group_submission = false; /** * constructor * @param criteria * The sort criteria string * @param asc * The sort order string. TRUE_STRING if ascending; "false" otherwise. */ public AssignmentComparator(String criteria, String asc) { m_criteria = criteria; m_asc = asc; } // constructor public AssignmentComparator(String criteria, String asc, boolean group) { m_criteria = criteria; m_asc = asc; m_group_submission = group; } /** * implementing the compare function * * @param o1 * The first object * @param o2 * The second object * @return The compare result. 1 is o1 < o2; -1 otherwise */ public int compare(Object o1, Object o2) { int result = -1; /************** for sorting submissions ********************/ if ("submitterName".equals(m_criteria)) { String name1 = getSubmitterSortname(o1); String name2 = getSubmitterSortname(o2); result = name1.compareTo(name2); } /** *********** for sorting assignments ****************** */ else if ("duedate".equals(m_criteria)) { // sorted by the assignment due date Time t1 = ((Assignment) o1).getDueTime(); Time t2 = ((Assignment) o2).getDueTime(); if (t1 == null) { result = -1; } else if (t2 == null) { result = 1; } else if (t1.before(t2)) { result = -1; } else { result = 1; } } else if ("sortname".equals(m_criteria)) { // sorted by the user's display name String s1 = null; String userId1 = (String) o1; if (userId1 != null) { try { User u1 = UserDirectoryService.getUser(userId1); s1 = u1!=null?u1.getSortName():null; } catch (Exception e) { M_log.warn(" AssignmentComparator.compare " + e.getMessage() + " id=" + userId1); } } String s2 = null; String userId2 = (String) o2; if (userId2 != null) { try { User u2 = UserDirectoryService.getUser(userId2); s2 = u2!=null?u2.getSortName():null; } catch (Exception e) { M_log.warn(" AssignmentComparator.compare " + e.getMessage() + " id=" + userId2); } } if (s1 == null) { result = -1; } else if (s2 == null) { result = 1; } else { result = s1.compareTo(s2); } } // sort ascending or descending if (m_asc.equals(Boolean.FALSE.toString())) { result = -result; } return result; } /** * get the submitter sortname String for the AssignmentSubmission object * @param o2 * @return */ private String getSubmitterSortname(Object o2) { String rv = ""; if (o2 instanceof AssignmentSubmission) { // get Assignment AssignmentSubmission _submission =(AssignmentSubmission) o2; if (_submission.getAssignment().isGroup()) { // get the Group try { Site _site = SiteService.getSite( _submission.getAssignment().getContext() ); rv = _site.getGroup(_submission.getSubmitterId()).getTitle(); } catch (Throwable _dfd) { } } else { User[] users2 = ((AssignmentSubmission) o2).getSubmitters(); if (users2 != null) { StringBuffer users2Buffer = new StringBuffer(); for (int i = 0; i < users2.length; i++) { users2Buffer.append(users2[i].getSortName() + " "); } rv = users2Buffer.toString(); } } } return rv; } } /** * {@inheritDoc} */ public void updateEntityReferences(String toContext, Map<String, String> transversalMap){ if(transversalMap != null && transversalMap.size() > 0){ Set<Entry<String, String>> entrySet = (Set<Entry<String, String>>) transversalMap.entrySet(); String toSiteId = toContext; Iterator assignmentsIter = getAssignmentsForContext(toSiteId); while (assignmentsIter.hasNext()) { Assignment assignment = (Assignment) assignmentsIter.next(); String assignmentId = assignment.getId(); try { String msgBody = assignment.getContent().getInstructions(); StringBuffer msgBodyPreMigrate = new StringBuffer(msgBody); msgBody = LinkMigrationHelper.migrateAllLinks(entrySet, msgBody); try { if(!msgBody.equals(msgBodyPreMigrate.toString())){ // add permission to update assignment content SecurityService.pushAdvisor( new MySecurityAdvisor(SessionManager.getCurrentSessionUserId(), new ArrayList<String>(Arrays.asList(SECURE_UPDATE_ASSIGNMENT_CONTENT)), assignment.getContentReference())); AssignmentContentEdit cEdit = editAssignmentContent(assignment.getContentReference()); cEdit.setInstructions(msgBody); commitEdit(cEdit); } } catch (Exception e) { // exception M_log.warn("UpdateEntityReference: cannot get assignment content for " + assignment.getId() + e.getMessage()); } finally { // remove advisor SecurityService.popAdvisor(); } } catch(Exception ee) { M_log.warn("UpdateEntityReference: remove Assignment and all references for " + assignment.getId() + ee.getMessage()); } } } } public void transferCopyEntities(String fromContext, String toContext, List ids, boolean cleanup){ transferCopyEntitiesRefMigrator(fromContext, toContext, ids, cleanup); } public Map<String, String> transferCopyEntitiesRefMigrator(String fromContext, String toContext, List ids, boolean cleanup) { Map<String, String> transversalMap = new HashMap<String, String>(); try { if(cleanup == true) { String toSiteId = toContext; Iterator assignmentsIter = getAssignmentsForContext(toSiteId); while (assignmentsIter.hasNext()) { Assignment assignment = (Assignment) assignmentsIter.next(); String assignmentId = assignment.getId(); try { // advisor to allow edit and remove assignment SecurityService.pushAdvisor( new MySecurityAdvisor(SessionManager.getCurrentSessionUserId(), new ArrayList<String>(Arrays.asList(SECURE_UPDATE_ASSIGNMENT, SECURE_REMOVE_ASSIGNMENT)), assignmentId)); AssignmentEdit aEdit = editAssignment(assignmentId); // remove this assignment with all its associated items removeAssignmentAndAllReferences(aEdit); } catch(Exception ee) { M_log.warn(":transferCopyEntities: remove Assignment and all references for " + assignment.getId() + ee.getMessage()); } finally { // remove SecurityAdvisor SecurityService.popAdvisor(); } } } transversalMap.putAll(transferCopyEntitiesRefMigrator(fromContext, toContext, ids)); } catch (Exception e) { M_log.info(this + "transferCopyEntities: End removing Assignmentt data" + e.getMessage()); } return transversalMap; } /** * This is to mimic the FormattedText.decodeFormattedTextAttribute but use SAX serialization instead * @return */ protected String formattedTextDecodeFormattedTextAttribute(Attributes attributes, String baseAttributeName) { String ret; // first check if an HTML-encoded attribute exists, for example "foo-html", and use it if available ret = StringUtils.trimToNull(xmlDecodeAttribute(attributes, baseAttributeName + "-html")); if (ret != null) return ret; // next try the older kind of formatted text like "foo-formatted", and convert it if found ret = StringUtils.trimToNull(xmlDecodeAttribute(attributes, baseAttributeName + "-formatted")); ret = FormattedText.convertOldFormattedText(ret); if (ret != null) return ret; // next try just a plaintext attribute and convert the plaintext to formatted text if found // convert from old plaintext instructions to new formatted text instruction ret = xmlDecodeAttribute(attributes, baseAttributeName); ret = FormattedText.convertPlaintextToFormattedText(ret); return ret; } /** * this is to mimic the Xml.decodeAttribute * @param el * @param tag * @return */ protected String xmlDecodeAttribute(Attributes attributes, String tag) { String charset = StringUtils.trimToNull(attributes.getValue("charset")); if (charset == null) charset = "UTF-8"; String body = StringUtils.trimToNull(attributes.getValue(tag)); if (body != null) { try { byte[] decoded = Base64.decodeBase64(body); // UTF-8 by default body = org.apache.commons.codec.binary.StringUtils.newString(decoded, charset); } catch (IllegalStateException e) { M_log.warn(" XmlDecodeAttribute: " + e.getMessage() + " tag=" + tag); } } if (body == null) body = ""; return body; } /** * construct the right path for context string, used for permission checkings * @param context * @return */ public static String getContextReference(String context) { String resourceString = getAccessPoint(true) + Entity.SEPARATOR + "a" + Entity.SEPARATOR + context + Entity.SEPARATOR; return resourceString; } /** * the GroupSubmission clas */ public class GroupSubmission { /** * the Group object */ Group m_group = null; /** * the AssignmentSubmission object */ AssignmentSubmission m_submission = null; public GroupSubmission(Group g, AssignmentSubmission s) { m_group = g; m_submission = s; } /** * Returns the AssignmentSubmission object */ public AssignmentSubmission getSubmission() { return m_submission; } public Group getGroup() { return m_group; } } private LRS_Statement getStatementForAssignmentGraded(LRS_Actor instructor, Event event, Assignment a, AssignmentSubmission s, User studentUser) { LRS_Verb verb = new LRS_Verb(SAKAI_VERB.scored); LRS_Object lrsObject = new LRS_Object(m_serverConfigurationService.getPortalUrl() + event.getResource(), "received-grade-assignment"); HashMap<String, String> nameMap = new HashMap<String, String>(); nameMap.put("en-US", "User received a grade"); lrsObject.setActivityName(nameMap); HashMap<String, String> descMap = new HashMap<String, String>(); descMap.put("en-US", "User received a grade for their assginment: " + a.getTitle() + "; Submission #: " + s.getResubmissionNum()); lrsObject.setDescription(descMap); LRS_Actor student = new LRS_Actor(studentUser.getEmail()); student.setName(studentUser.getDisplayName()); LRS_Context context = new LRS_Context(instructor); context.setActivity("other", "assignment"); LRS_Statement statement = new LRS_Statement(student, verb, lrsObject, getLRS_Result(a, s, true), context); return statement; } private LRS_Result getLRS_Result(Assignment a, AssignmentSubmission s, boolean completed) { LRS_Result result = null; AssignmentContent content = a.getContent(); if (3 == content.getTypeOfGrade() && NumberUtils.isNumber(s.getGradeDisplay())) { // Points result = new LRS_Result(new Float(s.getGradeDisplay()), new Float(0.0), new Float(content.getMaxGradePointDisplay()), null); result.setCompletion(completed); } else { result = new LRS_Result(completed); result.setGrade(s.getGradeDisplay()); } return result; } private LRS_Statement getStatementForUnsubmittedAssignmentGraded(LRS_Actor instructor, Event event, Assignment a, AssignmentSubmission s, User studentUser) { LRS_Verb verb = new LRS_Verb(SAKAI_VERB.scored); LRS_Object lrsObject = new LRS_Object(m_serverConfigurationService.getAccessUrl() + event.getResource(), "received-grade-unsubmitted-assignment"); HashMap<String, String> nameMap = new HashMap<String, String>(); nameMap.put("en-US", "User received a grade"); lrsObject.setActivityName(nameMap); HashMap<String, String> descMap = new HashMap<String, String>(); descMap.put("en-US", "User received a grade for an unsubmitted assginment: " + a.getTitle()); lrsObject.setDescription(descMap); LRS_Actor student = new LRS_Actor(studentUser.getEmail()); student.setName(studentUser.getDisplayName()); LRS_Context context = new LRS_Context(instructor); context.setActivity("other", "assignment"); LRS_Statement statement = new LRS_Statement(student, verb, lrsObject, getLRS_Result(a, s, false), context); return statement; } } // BaseAssignmentService