Index: samigo-app/src/java/org/sakaiproject/tool/assessment/bundle/AssessmentSettingsMessages.properties =================================================================== --- samigo-app/src/java/org/sakaiproject/tool/assessment/bundle/AssessmentSettingsMessages.properties (revision 132363) +++ samigo-app/src/java/org/sakaiproject/tool/assessment/bundle/AssessmentSettingsMessages.properties (working copy) @@ -288,3 +288,6 @@ less_than_two_columns=columns field need to have at least two inputs, separated by "Return" key selectFromFavorites=Select from Favorites +# Extended time +extendedTimeHeading=Exceptions to Time Limit and Delivery Date +extendedTimeAdd=Add a Time Limit/Delivery Date Exception. Index: samigo-app/src/java/org/sakaiproject/tool/assessment/ui/bean/author/AssessmentSettingsBean.java =================================================================== --- samigo-app/src/java/org/sakaiproject/tool/assessment/ui/bean/author/AssessmentSettingsBean.java (revision 132363) +++ samigo-app/src/java/org/sakaiproject/tool/assessment/ui/bean/author/AssessmentSettingsBean.java (working copy) @@ -52,6 +52,12 @@ import org.sakaiproject.exception.IdUnusedException; import org.sakaiproject.exception.PermissionException; import org.sakaiproject.exception.TypeException; +import org.sakaiproject.section.api.coursemanagement.EnrollmentRecord; +import org.sakaiproject.section.api.coursemanagement.User; +import org.sakaiproject.section.api.facade.Role; +import org.sakaiproject.section.api.SectionAwareness; +import org.sakaiproject.service.gradebook.shared.CategoryDefinition; +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; @@ -209,6 +215,10 @@ private String releaseToGroupsAsString; private String blockDivs; + + private String extendedTimes; + private SelectItem[] extendedTimeTargets; + /** * we use the calendar widget which uses 'MM/dd/yyyy hh:mm:ss a' * used to take the internal format from calendar picker and move it @@ -282,7 +292,18 @@ this.bgImageSelect=null; this.bgColorSelect="1"; } - + + // Get the extended time information for this assessment + short extendedTimeCount = 1; + String extendedTimeLabel = "extendedTime"+extendedTimeCount; + this.extendedTimes = ""; + while((assessment.getAssessmentMetaDataByLabel(extendedTimeLabel) != null) && + (!assessment.getAssessmentMetaDataByLabel(extendedTimeLabel).equals(""))) { + String extendedTimeValue = assessment.getAssessmentMetaDataByLabel(extendedTimeLabel); + this.extendedTimes = this.extendedTimes.concat(extendedTimeValue+"^"); + extendedTimeCount++; + extendedTimeLabel = "extendedTime"+extendedTimeCount; + } // these are properties in AssessmentAccessControl AssessmentAccessControlIfc accessControl = null; @@ -1795,5 +1816,91 @@ return selections; } + + public void setExtendedTimes(String extendedTimes) { + this.extendedTimes = extendedTimes; + } + + public String getExtendedTimes() { + return extendedTimes; + } + + /** + * Popluate the select item list of extended time targets + * @return + */ + public SelectItem[] initExtendedTimeTargets(){ + SelectItem[] extTimeSelectItems = null; + Site site = null; + + try { + site = SiteService.getSite(ToolManager.getCurrentPlacement().getContext()); + Collection groups = site.getGroups(); + SectionAwareness sectionAwareness = PersistenceService.getInstance().getSectionAwareness(); + //List sections = sectionAwareness.getSections(site.getId()); + List enrollments = sectionAwareness.getSiteMembersInRole(site.getId(), Role.STUDENT); + + // Treemaps are used here because they auto-sort + TreeMap SectionTargets = new TreeMap(); + TreeMap groupTargets = new TreeMap(); + TreeMap studentTargets = new TreeMap(); + + // Add groups to target set + if (groups != null && groups.size() > 0) { + Iterator groupIter = groups.iterator(); + while (groupIter.hasNext()) { + Group group = (Group) groupIter.next(); + if(!group.getTitle().startsWith("Access: ")) // do not include Lessons groups + groupTargets.put("Group: "+group.getTitle(), group.getId()); + } + } + + // Add students to target set + if (enrollments != null && enrollments.size() > 0) { + for(Iterator iter = enrollments.iterator(); iter.hasNext();) { + EnrollmentRecord enrollmentRecord = (EnrollmentRecord)iter.next(); + String userId = enrollmentRecord.getUser().getUserUid(); + String userDisplayName = enrollmentRecord.getUser().getSortName(); + studentTargets.put(userDisplayName, userId); + } + } + + // Add targets to selectItem array. We put the alpha name in as the key so it would + // be alphabetized. Now we pull it out and build the select item list. + int listSize = 1 + groupTargets.size() + studentTargets.size(); + extTimeSelectItems = new SelectItem[listSize]; + extTimeSelectItems[0] = new SelectItem("1", "Select User/Group"); + int selectCount = 1; + + // Add in groups to select item list + Set keySet = groupTargets.keySet(); + Iterator iter = keySet.iterator(); + while (iter.hasNext()) { + String alphaName = (String) iter.next(); + String sakaiId = (String) groupTargets.get(alphaName); + extTimeSelectItems[selectCount++] = new SelectItem(sakaiId, alphaName); + } + + // Add in students to select item list + keySet = studentTargets.keySet(); + iter = keySet.iterator(); + while (iter.hasNext()) { + String alphaName = (String) iter.next(); + String sakaiId = (String) studentTargets.get(alphaName); + extTimeSelectItems[selectCount++] = new SelectItem(sakaiId, alphaName); + } + + } catch (IdUnusedException ex) { + // No site available + } + return extTimeSelectItems; + } + + public SelectItem[] getExtendedTimeTargets(){ + return extendedTimeTargets; + } + public void setExtendedTimeTargets(SelectItem[] targets){ + this.extendedTimeTargets = targets; + } } Index: samigo-app/src/java/org/sakaiproject/tool/assessment/ui/bean/author/PublishedAssessmentSettingsBean.java =================================================================== --- samigo-app/src/java/org/sakaiproject/tool/assessment/ui/bean/author/PublishedAssessmentSettingsBean.java (revision 132363) +++ samigo-app/src/java/org/sakaiproject/tool/assessment/ui/bean/author/PublishedAssessmentSettingsBean.java (working copy) @@ -60,6 +60,10 @@ import org.sakaiproject.tool.api.ToolSession; import org.sakaiproject.entity.api.Reference; import org.sakaiproject.entity.cover.EntityManager; +import org.sakaiproject.section.api.coursemanagement.EnrollmentRecord; +import org.sakaiproject.section.api.coursemanagement.CourseSection; +import org.sakaiproject.section.api.facade.Role; +import org.sakaiproject.section.api.SectionAwareness; import org.sakaiproject.tool.assessment.data.dao.assessment.AssessmentAccessControl; import org.sakaiproject.tool.assessment.data.ifc.assessment.AssessmentAccessControlIfc; import org.sakaiproject.tool.assessment.data.ifc.assessment.AssessmentFeedbackIfc; @@ -212,6 +216,9 @@ private String bgColorSelect; private String bgImageSelect; + private String extendedTimes; + private SelectItem[] extendedTimeTargets; + /* * Creates a new AssessmentBean object. */ @@ -255,6 +262,20 @@ this.bgColorSelect="1"; } + // Get the extended time information for this assessment + short extendedTimeCount = 1; + String extendedTimeLabel = "extendedTime"+extendedTimeCount; + this.extendedTimes = ""; + while((assessment.getAssessmentMetaDataByLabel(extendedTimeLabel) != null) && + (!assessment.getAssessmentMetaDataByLabel(extendedTimeLabel).equals(""))) { + String extendedTimeValue = assessment.getAssessmentMetaDataByLabel(extendedTimeLabel); + //this.extendedTimes.add(extendedTimeValue); + //TODO: switch this back to being a list or hashmap + this.extendedTimes = this.extendedTimes.concat(extendedTimeValue+"^"); + extendedTimeCount++; + extendedTimeLabel = "extendedTime"+extendedTimeCount; + } + setDisplayFormat(ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.GeneralMessages","output_data_picker_w_sec")); resetIsValidDate(); resetOriginalDateString(); @@ -392,6 +413,9 @@ String currentSiteId = AgentFacade.getCurrentSiteId(); this.gradebookExists = gbsHelper.isGradebookExist(currentSiteId); + + this.extendedTimeTargets = initExtendedTimeTargets(); + /* GradebookService g = null; if (integrated) @@ -1682,6 +1706,93 @@ return selections; } + + public void setExtendedTimes(String extendedTimes) { + this.extendedTimes = extendedTimes; + } + + public String getExtendedTimes() { + return extendedTimes; + } + + /** + * Popluate the select item list of extended time targets + * @return + */ + public SelectItem[] initExtendedTimeTargets(){ + SelectItem[] extTimeSelectItems = null; + Site site = null; + + try { + site = SiteService.getSite(ToolManager.getCurrentPlacement().getContext()); + Collection groups = site.getGroups(); + SectionAwareness sectionAwareness = PersistenceService.getInstance().getSectionAwareness(); + //List sections = sectionAwareness.getSections(site.getId()); + List enrollments = sectionAwareness.getSiteMembersInRole(site.getId(), Role.STUDENT); + + // Treemaps are used here because they auto-sort + TreeMap SectionTargets = new TreeMap(); + TreeMap groupTargets = new TreeMap(); + TreeMap studentTargets = new TreeMap(); + + // Add groups to target set + if (groups != null && groups.size() > 0) { + Iterator groupIter = groups.iterator(); + while (groupIter.hasNext()) { + Group group = (Group) groupIter.next(); + if(!group.getTitle().startsWith("Access: ")) // do not include Lessons groups + groupTargets.put("Group: "+group.getTitle(), group.getId()); + } + } + + // Add students to target set + if (enrollments != null && enrollments.size() > 0) { + for(Iterator iter = enrollments.iterator(); iter.hasNext();) { + EnrollmentRecord enrollmentRecord = (EnrollmentRecord)iter.next(); + String userId = enrollmentRecord.getUser().getUserUid(); + String userDisplayName = enrollmentRecord.getUser().getSortName(); + studentTargets.put(userDisplayName, userId); + } + } + + // Add targets to selectItem array. We put the alpha name in as the key so it would + // be alphabetized. Now we pull it out and build the select item list. + int listSize = 1 + groupTargets.size() + studentTargets.size(); + extTimeSelectItems = new SelectItem[listSize]; + extTimeSelectItems[0] = new SelectItem("1", "Select User/Group"); + int selectCount = 1; + + // Add in groups to select item list + Set keySet = groupTargets.keySet(); + Iterator iter = keySet.iterator(); + while (iter.hasNext()) { + String alphaName = (String) iter.next(); + String sakaiId = (String) groupTargets.get(alphaName); + extTimeSelectItems[selectCount++] = new SelectItem(sakaiId, alphaName); + } + + // Add in students to select item list + keySet = studentTargets.keySet(); + iter = keySet.iterator(); + while (iter.hasNext()) { + String alphaName = (String) iter.next(); + String sakaiId = (String) studentTargets.get(alphaName); + extTimeSelectItems[selectCount++] = new SelectItem(sakaiId, alphaName); + } + + } catch (IdUnusedException ex) { + // No site available + } + return extTimeSelectItems; + } + + public SelectItem[] getExtendedTimeTargets(){ + return extendedTimeTargets; + } + public void setExtendedTimeTargets(SelectItem[] targets){ + this.extendedTimeTargets = targets; + } + } Index: samigo-app/src/java/org/sakaiproject/tool/assessment/ui/bean/delivery/DeliveryBean.java =================================================================== --- samigo-app/src/java/org/sakaiproject/tool/assessment/ui/bean/delivery/DeliveryBean.java (revision 132363) +++ samigo-app/src/java/org/sakaiproject/tool/assessment/ui/bean/delivery/DeliveryBean.java (working copy) @@ -29,6 +29,7 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.Serializable; +import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; @@ -35,6 +36,7 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; +import java.util.Locale; import java.util.ResourceBundle; import java.util.Set; @@ -75,6 +77,7 @@ import org.sakaiproject.tool.assessment.facade.PublishedAssessmentFacade; import org.sakaiproject.tool.assessment.services.FinFormatException; import org.sakaiproject.tool.assessment.services.GradingService; +import org.sakaiproject.tool.assessment.services.PersistenceService; import org.sakaiproject.tool.assessment.services.SaLengthException; import org.sakaiproject.tool.assessment.services.assessment.EventLogService; import org.sakaiproject.tool.assessment.services.assessment.PublishedAssessmentService; @@ -93,6 +96,7 @@ import org.sakaiproject.tool.assessment.ui.model.delivery.TimedAssessmentGradingModel; import org.sakaiproject.tool.assessment.ui.queue.delivery.TimedAssessmentQueue; import org.sakaiproject.tool.assessment.ui.web.session.SessionUtil; +import org.sakaiproject.tool.assessment.util.ExtendedTimeService; import org.sakaiproject.tool.assessment.util.MimeTypesLocator; import org.sakaiproject.tool.cover.SessionManager; import org.sakaiproject.tool.cover.ToolManager; @@ -267,6 +271,8 @@ private String secureDeliveryHTMLFragment; private boolean isFromPrint; + + private ExtendedTimeService extendedTimeService = null; private static String ACCESSBASE = ServerConfigurationService.getAccessUrl(); private static String RECPATH = ServerConfigurationService.getString("samigo.recommendations.path"); @@ -273,6 +279,8 @@ private static ResourceBundle eventLogMessages = ResourceBundle.getBundle("org.sakaiproject.tool.assessment.bundle.EventLogMessages"); + private static String EXTENDED_TIME_KEY = "extendedTime"; + /** * Creates a new DeliveryBean object. */ @@ -416,6 +424,7 @@ * * @param action */ + public void setAction(String action) { this.action = action; @@ -3102,6 +3111,9 @@ if (adata!=null){ assessmentGrading = service.load(adata.getAssessmentGradingId().toString(), false); } + + extendedTimeService = new ExtendedTimeService(publishedAssessment); + PublishedAssessmentService pubService = new PublishedAssessmentService(); int totalSubmitted = (pubService.getTotalSubmission(AgentFacade.getAgentString(), getPublishedAssessment().getPublishedAssessmentId().toString())).intValue(); @@ -3241,7 +3253,12 @@ private boolean isAvailable(){ boolean isAvailable = true; Date currentDate = new Date(); - Date startDate = publishedAssessment.getAssessmentAccessControl().getStartDate(); + Date startDate = new Date(); + if (extendedTimeService.hasExtendedTime()) { + startDate = extendedTimeService.getStartDate(); + } else { + startDate = publishedAssessment.getAssessmentAccessControl().getStartDate(); + } if (startDate != null && startDate.after(currentDate)){ isAvailable = false; } @@ -3251,7 +3268,12 @@ private boolean pastDueDate(){ boolean pastDue = true; Date currentDate = new Date(); - Date dueDate = publishedAssessment.getAssessmentAccessControl().getDueDate(); + Date dueDate = new Date(); + if(extendedTimeService.hasExtendedTime()) { + dueDate = extendedTimeService.getDueDate(); + } else { + dueDate = publishedAssessment.getAssessmentAccessControl().getDueDate(); + } if (dueDate == null || dueDate.after(currentDate)){ pastDue = false; } @@ -3262,14 +3284,12 @@ boolean isRetracted = true; Date currentDate = new Date(); Date retractDate = null; - if (isSubmitForGrade) { - PublishedAssessmentService pubService = new PublishedAssessmentService(); - PublishedAssessmentData publishedAssessmentData = pubService.getBasicInfoOfPublishedAssessment(getPublishedAssessment().getPublishedAssessmentId().toString()); - retractDate = publishedAssessmentData.getRetractDate(); - } - else { + if (extendedTimeService.hasExtendedTime()) { + retractDate = extendedTimeService.getRetractDate(); + } else { retractDate = publishedAssessment.getAssessmentAccessControl().getRetractDate(); } + if (retractDate == null || retractDate.after(currentDate)){ isRetracted = false; } @@ -3463,7 +3483,9 @@ return String.valueOf(timeBeforeDue); } } - + + retractDate = publishedAssessment.getRetractDate(); + if (retractDate != null) { int timeBeforeRetract = Math.round((retractDate.getTime() - beginTime.getTime())/1000.0f); if (timeBeforeRetract < Integer.parseInt(timeLimit)) { @@ -3686,5 +3708,36 @@ return recURL; } + /** + * Return the time limit as a String + * @param delivery + * @param publishedAssessment + * @param fromBeginAssessment + * @return + */ + public int evaluateTimeLimit(PublishedAssessmentFacade pubAssessment, Boolean fromBeginAssessment, int extTimeVal){ + publishedAssessment = pubAssessment; // synchronize the passed in values + Integer timeLimit = 0; + Integer originalTimeLimit = publishedAssessment.getAssessmentAccessControl().getTimeLimit(); + int extTimeAdjust = 0; + + // Calcuate the adjustment due to extended time if necessary + if (extTimeVal > 0) { + extTimeAdjust = extTimeVal - originalTimeLimit; // adjustment to add to time remaining + } + + if (fromBeginAssessment) { + timeLimit = Integer.parseInt(this.updateTimeLimit(originalTimeLimit.toString())) + + extTimeAdjust; + } else { + if (this.getTimeLimit() != null) { + timeLimit = Integer.parseInt(this.getTimeLimit()); + } + } + this.setTimeLimit(timeLimit.toString()); + + return timeLimit; + } + } Index: samigo-app/src/java/org/sakaiproject/tool/assessment/ui/listener/author/SaveAssessmentSettings.java =================================================================== --- samigo-app/src/java/org/sakaiproject/tool/assessment/ui/listener/author/SaveAssessmentSettings.java (revision 132363) +++ samigo-app/src/java/org/sakaiproject/tool/assessment/ui/listener/author/SaveAssessmentSettings.java (working copy) @@ -79,6 +79,7 @@ public class SaveAssessmentSettings { private static Log log = LogFactory.getLog(SaveAssessmentSettings.class); + private static String EXTENDED_TIME_KEY = "extendedTime"; public AssessmentFacade save(AssessmentSettingsBean assessmentSettings, boolean isFromConfirmPublishAssessmentListener) { @@ -261,6 +262,7 @@ // hasUsernamePassword, // hasTimeAssessment,hasAutoSubmit, hasPartMetaData, hasQuestionMetaData HashMap h = assessmentSettings.getValueMap(); + h = addExtendedTimeValuesToMetaData(assessment, assessmentSettings); updateMetaWithValueMap(assessment, h); // i. set Graphics @@ -498,4 +500,38 @@ return releaseToGroupsAsString.toString(); } + + /** + * This will clear out the old extended time values and update them with new ones. + * @param assessment + * @param assessmentSettings + * @return + */ + private HashMap addExtendedTimeValuesToMetaData(AssessmentFacade assessment, + AssessmentSettingsBean assessmentSettings) { + + String[] allExtendedTimeEntries = assessmentSettings.getExtendedTimes().split("\\^");; + HashMap metaDataMap = assessment.getAssessmentMetaDataMap(); + String metaKey = ""; + + // clear out the old extended Time values + int itemNum = 1; + String extendedTimeData = assessment.getAssessmentMetaDataByLabel(EXTENDED_TIME_KEY+itemNum); + while((extendedTimeData != null) && (!extendedTimeData.equals(""))) { + metaKey = EXTENDED_TIME_KEY+itemNum; + metaDataMap.put(metaKey, ""); // set to empty string TODO: actually delete it. + extendedTimeData = assessment.getAssessmentMetaDataByLabel(EXTENDED_TIME_KEY+itemNum); + itemNum++; + } + + for(itemNum = 0; itemNum < allExtendedTimeEntries.length; itemNum++) { + String extendedTimeEntry = allExtendedTimeEntries[itemNum]; + metaKey = "extendedTime"+(itemNum+1); + + // Add in the new extended time values + metaDataMap.put(metaKey, extendedTimeEntry); + } + + return metaDataMap; + } } Index: samigo-app/src/java/org/sakaiproject/tool/assessment/ui/listener/author/SavePublishedSettingsListener.java =================================================================== --- samigo-app/src/java/org/sakaiproject/tool/assessment/ui/listener/author/SavePublishedSettingsListener.java (revision 132363) +++ samigo-app/src/java/org/sakaiproject/tool/assessment/ui/listener/author/SavePublishedSettingsListener.java (working copy) @@ -72,6 +72,7 @@ import org.sakaiproject.tool.assessment.services.assessment.PublishedAssessmentService; import org.sakaiproject.tool.assessment.shared.api.assessment.SecureDeliveryServiceAPI; import org.sakaiproject.tool.assessment.ui.bean.author.AssessmentBean; +import org.sakaiproject.tool.assessment.ui.bean.author.AssessmentSettingsBean; import org.sakaiproject.tool.assessment.ui.bean.author.AuthorBean; import org.sakaiproject.tool.assessment.ui.bean.author.PublishedAssessmentSettingsBean; import org.sakaiproject.tool.assessment.ui.listener.util.ContextUtil; @@ -97,6 +98,7 @@ IntegrationContextFactory.getInstance().isIntegrated(); private CalendarServiceHelper calendarService = IntegrationContextFactory.getInstance().getCalendarServiceHelper(); private ResourceLoader rb= new ResourceLoader("org.sakaiproject.tool.assessment.bundle.AssessmentSettingsMessages"); + private static String EXTENDED_TIME_KEY = "extendedTime"; public SavePublishedSettingsListener() { @@ -566,6 +568,7 @@ // hasUsernamePassword, hasTimeAssessment,hasAutoSubmit, hasPartMetaData, // hasQuestionMetaData HashMap h = assessmentSettings.getValueMap(); + h = addExtendedTimeValuesToMetaData(assessment, assessmentSettings); saveAssessmentSettings.updateMetaWithValueMap(assessment, h); // i. set Graphics @@ -731,6 +734,40 @@ PublishedAssessmentFacadeQueries.TITLE, true, AgentFacade.getCurrentSiteId()); authorActionListener.prepareAllPublishedAssessmentsList(author, gradingService, publishedAssessmentList); } + + /** + * This will clear out the old extended time values and update them with new ones. + * @param assessment + * @param assessmentSettings + * @return + */ + private HashMap addExtendedTimeValuesToMetaData(PublishedAssessmentFacade assessment, + PublishedAssessmentSettingsBean assessmentSettings) { + + String[] allExtendedTimeEntries = assessmentSettings.getExtendedTimes().split("\\^");; + HashMap metaDataMap = assessment.getAssessmentMetaDataMap(); + String metaKey = ""; + + // clear out the old extended Time values + int itemNum = 1; + String extendedTimeData = assessment.getAssessmentMetaDataByLabel(EXTENDED_TIME_KEY+itemNum); + while((extendedTimeData != null) && (!extendedTimeData.equals(""))) { + metaKey = EXTENDED_TIME_KEY+itemNum; + metaDataMap.put(metaKey, ""); // set to empty string TODO: actually delete it. + extendedTimeData = assessment.getAssessmentMetaDataByLabel(EXTENDED_TIME_KEY+itemNum); + itemNum++; + } + + for(itemNum = 0; itemNum < allExtendedTimeEntries.length; itemNum++) { + String extendedTimeEntry = allExtendedTimeEntries[itemNum]; + metaKey = "extendedTime"+(itemNum+1); + + // Add in the new extended time values + metaDataMap.put(metaKey, extendedTimeEntry); + } + + return metaDataMap; + } } Index: samigo-app/src/java/org/sakaiproject/tool/assessment/ui/listener/delivery/BeginDeliveryActionListener.java =================================================================== --- samigo-app/src/java/org/sakaiproject/tool/assessment/ui/listener/delivery/BeginDeliveryActionListener.java (revision 132363) +++ samigo-app/src/java/org/sakaiproject/tool/assessment/ui/listener/delivery/BeginDeliveryActionListener.java (working copy) @@ -64,6 +64,7 @@ import org.sakaiproject.tool.assessment.ui.bean.shared.PersonBean; import org.sakaiproject.tool.assessment.ui.listener.util.ContextUtil; import org.sakaiproject.tool.assessment.ui.listener.author.RemovePublishedAssessmentThread; +import org.sakaiproject.tool.assessment.util.ExtendedTimeService; /** *

Title: Samigo

@@ -278,9 +279,20 @@ delivery.setQuestionIndex(0); delivery.setBeginTime(null); delivery.setFeedbackOnDate(false); - delivery.setDueDate(control.getDueDate()); - delivery.setRetractDate(control.getRetractDate()); + ExtendedTimeService extTimeService = new ExtendedTimeService(delivery.getPublishedAssessment()); + PublishedAssessmentFacade paFacade = delivery.getPublishedAssessment(); + + if (extTimeService.hasExtendedTime()) { + delivery.setDueDate(extTimeService.getDueDate()); + delivery.setRetractDate(extTimeService.getRetractDate()); + if(extTimeService.getTimeLimit() > 0) + paFacade.setTimeLimit(extTimeService.getTimeLimit()); + } else { + delivery.setDueDate(control.getDueDate()); + delivery.setRetractDate(control.getRetractDate()); + } + if (control.getMarkForReview() != null && (Integer.valueOf(1)).equals(control.getMarkForReview())) { delivery.setDisplayMardForReview(true); } @@ -308,23 +320,28 @@ delivery.setNavigation(control.getItemNavigation().toString()); // #3 - if this is a timed assessment, set the time limit in hr, min & sec. - setTimedAssessment(delivery, pubAssessment); + setTimedAssessment(delivery, pubAssessment, extTimeService); } - private void setTimedAssessment(DeliveryBean delivery, PublishedAssessmentIfc pubAssessment){ + private void setTimedAssessment(DeliveryBean delivery, PublishedAssessmentIfc pubAssessment, ExtendedTimeService extTimeService){ AssessmentAccessControlIfc control = pubAssessment.getAssessmentAccessControl(); // check if we need to time the assessment, i.e.hasTimeassessment="true" String hasTimeLimit = pubAssessment.getAssessmentMetaDataByLabel("hasTimeAssessment"); + hasTimeLimit="true"; //TODO: figure out why this isn't giving me the right value if (hasTimeLimit!=null && hasTimeLimit.equals("true")){ delivery.setHasTimeLimit(true); delivery.setTimerId((new Date()).getTime()+""); try { + if (extTimeService.hasExtendedTime() && extTimeService.getTimeLimit() > 0) + control.setTimeLimit(extTimeService.getTimeLimit()); if (control.getTimeLimit() != null) { - delivery.setTimeLimit(delivery.updateTimeLimit(control.getTimeLimit().toString())); - int seconds = control.getTimeLimit().intValue(); + Integer timeLimit = control.getTimeLimit(); + if(timeLimit < 1) delivery.setHasTimeLimit(false); //TODO: figure out why I have to do this + delivery.setTimeLimit(delivery.updateTimeLimit(timeLimit.toString())); + int seconds = timeLimit; int hour = 0; int minute = 0; if (seconds>=3600) { Index: samigo-app/src/java/org/sakaiproject/tool/assessment/ui/listener/delivery/DeliveryActionListener.java =================================================================== --- samigo-app/src/java/org/sakaiproject/tool/assessment/ui/listener/delivery/DeliveryActionListener.java (revision 132363) +++ samigo-app/src/java/org/sakaiproject/tool/assessment/ui/listener/delivery/DeliveryActionListener.java (working copy) @@ -97,6 +97,7 @@ import org.sakaiproject.tool.assessment.ui.model.delivery.TimedAssessmentGradingModel; import org.sakaiproject.tool.assessment.ui.queue.delivery.TimedAssessmentQueue; import org.sakaiproject.tool.assessment.ui.web.session.SessionUtil; +import org.sakaiproject.tool.assessment.util.ExtendedTimeService; import org.sakaiproject.tool.assessment.util.FormatException; import org.sakaiproject.util.FormattedText; import org.sakaiproject.util.ResourceLoader; @@ -2438,15 +2439,17 @@ AssessmentAccessControlIfc control = publishedAssessment.getAssessmentAccessControl(); AssessmentGradingData ag = delivery.getAssessmentGrading(); delivery.setBeginTime(ag.getAttemptDate()); + + // Handle Extended Time Information + ExtendedTimeService extendedTimeService = new ExtendedTimeService(publishedAssessment); + if (extendedTimeService.hasExtendedTime()) { + if (extendedTimeService.getTimeLimit() > 0) + publishedAssessment.setTimeLimit(extendedTimeService.getTimeLimit()); + publishedAssessment.setDueDate(extendedTimeService.getDueDate()); + publishedAssessment.setRetractDate(extendedTimeService.getRetractDate()); + } if (delivery.getHasTimeLimit() && control != null && control.getTimeLimit()!=null) { - if (fromBeginAssessment) { - timeLimit = Integer.parseInt(delivery.updateTimeLimit(publishedAssessment.getAssessmentAccessControl().getTimeLimit().toString())); - } - else { - if (delivery.getTimeLimit() != null) { - timeLimit = Integer.parseInt(delivery.getTimeLimit()); - } - } + timeLimit = delivery.evaluateTimeLimit(publishedAssessment,fromBeginAssessment, extendedTimeService.getTimeLimit()); } if (timeLimit==0) @@ -2744,5 +2747,6 @@ lrsObject.setDescription(descMap); return new LRS_Statement(actor, verb, lrsObject); } + } Index: samigo-app/src/java/org/sakaiproject/tool/assessment/ui/listener/select/SelectActionListener.java =================================================================== --- samigo-app/src/java/org/sakaiproject/tool/assessment/ui/listener/select/SelectActionListener.java (revision 132363) +++ samigo-app/src/java/org/sakaiproject/tool/assessment/ui/listener/select/SelectActionListener.java (working copy) @@ -23,6 +23,8 @@ package org.sakaiproject.tool.assessment.ui.listener.select; +import java.text.ParseException; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Date; @@ -30,6 +32,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Locale; import javax.faces.context.FacesContext; import javax.faces.event.AbortProcessingException; @@ -46,10 +49,12 @@ import org.sakaiproject.tool.assessment.data.ifc.assessment.AssessmentFeedbackIfc; import org.sakaiproject.tool.assessment.data.ifc.assessment.AssessmentIfc; import org.sakaiproject.tool.assessment.data.ifc.assessment.EvaluationModelIfc; +import org.sakaiproject.tool.assessment.data.ifc.assessment.PublishedAssessmentIfc; import org.sakaiproject.tool.assessment.facade.AgentFacade; import org.sakaiproject.tool.assessment.facade.PublishedAssessmentFacade; import org.sakaiproject.tool.assessment.facade.PublishedAssessmentFacadeQueries; import org.sakaiproject.tool.assessment.services.GradingService; +import org.sakaiproject.tool.assessment.services.PersistenceService; import org.sakaiproject.tool.assessment.services.assessment.PublishedAssessmentService; import org.sakaiproject.tool.assessment.shared.api.assessment.SecureDeliveryServiceAPI; import org.sakaiproject.tool.assessment.ui.bean.authz.AuthorizationBean; @@ -59,6 +64,7 @@ import org.sakaiproject.tool.assessment.ui.bean.shared.PersonBean; import org.sakaiproject.tool.assessment.ui.listener.util.ContextUtil; import org.sakaiproject.tool.assessment.util.BeanSort; +import org.sakaiproject.tool.assessment.util.ExtendedTimeService; import org.sakaiproject.util.ResourceLoader; /** @@ -75,6 +81,8 @@ //private static ContextUtil cu; private static BeanSort bs; private static BeanSort bs2; + private static ExtendedTimeService extendedTimeService = null; + private static String EXTENDED_TIME_KEY = "extendedTime"; public SelectActionListener() { } @@ -113,7 +121,7 @@ // look for some sort information passed as parameters processSortInfo(select); - + // ----------------- prepare Takeable assessment list ------------- // 1a. get total no. of submission (for grade) per assessment by the given agent in current site HashMap h = publishedAssessmentService.getTotalSubmissionPerAssessment( @@ -578,6 +586,18 @@ HashMap actualNumberRetake = gradingService.getActualNumberRetakeHash(AgentFacade.getAgentString()); for (int i = 0; i < assessmentList.size(); i++) { PublishedAssessmentFacade f = (PublishedAssessmentFacade)assessmentList.get(i); + + // Handle extended time info + extendedTimeService = new ExtendedTimeService(f); + if(extendedTimeService.hasExtendedTime()) { + f.setStartDate(extendedTimeService.getStartDate()); + f.setDueDate(extendedTimeService.getDueDate()); + f.setRetractDate(extendedTimeService.getRetractDate()); + if(extendedTimeService.getTimeLimit() != 0) { + f.setTimeLimit(extendedTimeService.getTimeLimit()); + } + } + if (f.getReleaseTo()!=null && !("").equals(f.getReleaseTo()) && f.getReleaseTo().indexOf("Anonymous Users") == -1 ) { if (isAvailable(f, h, numberRetakeHash, actualNumberRetake, updatedAssessmentNeedResubmitList, updatedAssessmentList)) { @@ -593,9 +613,10 @@ //1. prepare our significant parameters Integer status = f.getStatus(); Date currentDate = new Date(); + Date startDate = f.getStartDate(); + Date dueDate = f.getDueDate(); Date retractDate = f.getRetractDate(); - Date dueDate = f.getDueDate(); if (!Integer.valueOf(1).equals(status)) { return false; @@ -852,4 +873,9 @@ delivery.setTimeLimit_minute(0); } } + + + + + } Index: samigo-app/src/java/org/sakaiproject/tool/assessment/util/ExtendedTimeService.java =================================================================== --- samigo-app/src/java/org/sakaiproject/tool/assessment/util/ExtendedTimeService.java (revision 0) +++ samigo-app/src/java/org/sakaiproject/tool/assessment/util/ExtendedTimeService.java (working copy) @@ -0,0 +1,199 @@ +package org.sakaiproject.tool.assessment.util; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; + +import org.sakaiproject.authz.api.AuthzGroup; +import org.sakaiproject.authz.cover.AuthzGroupService; +import org.sakaiproject.tool.assessment.facade.AgentFacade; +import org.sakaiproject.tool.assessment.facade.PublishedAssessmentFacade; +import org.sakaiproject.tool.assessment.services.PersistenceService; +import org.sakaiproject.tool.assessment.services.assessment.PublishedAssessmentService; + +/** + * This class will instantiate with all the proper values for the current user's + * extended time values for the given published assessment. + * @author pdagnall1 + * + */ +public class ExtendedTimeService { + + private static String EXTENDED_TIME_KEY = "extendedTime"; + private String siteId; + + private boolean hasExtendedTime; + private Integer timeLimit; + private Date startDate; + private Date dueDate; + private Date retractDate; + private String metaString; //holds the extended time info for the current user + + public ExtendedTimeService(PublishedAssessmentFacade publishedAssessment) { + PublishedAssessmentService assessmentService = new PublishedAssessmentService(); + PublishedAssessmentFacade metaPublishedAssessment = assessmentService. + getPublishedAssessmentQuick(publishedAssessment.getPublishedAssessmentId().toString()); + if(!assessmentInitialized(publishedAssessment)) { + publishedAssessment = metaPublishedAssessment; + } + + // Grab the site id from the publishedAssessment because the user may not be in a site + // if they're taking the test via url. + PublishedAssessmentService publishedAssessmentService = new PublishedAssessmentService(); + String pubId = publishedAssessment.getPublishedAssessmentId().toString(); + siteId = publishedAssessmentService.getPublishedAssessmentSiteId(pubId); + + this.metaString = extractMetaString(metaPublishedAssessment); + this.hasExtendedTime = (metaString != null); + if (this.hasExtendedTime) { + this.timeLimit = extractExtendedTime(); + this.startDate = determineDate(1, publishedAssessment.getStartDate(), publishedAssessment); + this.dueDate = determineDate(2, publishedAssessment.getDueDate(), publishedAssessment); + this.retractDate = determineDate(3, publishedAssessment.getRetractDate(), publishedAssessment); + } else { + this.timeLimit = 0; + this.startDate = publishedAssessment.getStartDate(); + this.dueDate = publishedAssessment.getDueDate(); + this.retractDate = publishedAssessment.getRetractDate(); + } + } + + // Depending on the scope the assessment info sometimes is not initialized. + private boolean assessmentInitialized(PublishedAssessmentFacade publishedAssessment) { + if(publishedAssessment == null) return false; + if(publishedAssessment.getStartDate() != null) return true; + if(publishedAssessment.getDueDate() != null) return true; + if(publishedAssessment.getRetractDate() != null) return true; + if(publishedAssessment.getTimeLimit() != null) return true; + return false; + } + + // This sets the metString that holds the extended time info for the user + private String extractMetaString(PublishedAssessmentFacade publishedAssessment) { + short itemNum = 1; + String meta = null; + String extendedTimeData = publishedAssessment.getAssessmentMetaDataByLabel(EXTENDED_TIME_KEY+itemNum); + int extendedTime = 0; + while((extendedTimeData != null) && (!extendedTimeData.equals(""))) { + + String[] extendedTimeItems = extendedTimeData.split("[|]"); + + // Get target user/group value + String target = extendedTimeItems[0]; + + // If it's a group determine if user is a member + boolean isMember = isUserInGroup(target); + + String userId = AgentFacade.getAgentString(); + if (target.equals(userId) || isMember) { + meta = extendedTimeData; + } + itemNum++; + extendedTimeData = publishedAssessment.getAssessmentMetaDataByLabel(EXTENDED_TIME_KEY+itemNum); + } + return meta; + } + + /** + * If this user has been assigned an extended time then we'll return the time value. Otherwise + * we'll return null. + * @param delivery + * @param publishedAssessment + * @return + */ + private int extractExtendedTime() { + int extendedTime = 0; + String[] extendedTimeItems = metaString.split("[|]"); + extendedTime = Integer.parseInt(extendedTimeItems[1]); + return extendedTime; + } + + + /** + * Return the default date unless there are extended time dates we should + * use instead. + * @param dateType - 1: Start Date, 2. Due Date 3. Retract Date + * @param defaultDate + * @return + */ + private Date determineDate(int dateType, Date defaultDate, PublishedAssessmentFacade publishedAssessment) { + Date xtDate = defaultDate; + + String[] extendedTimeItems = metaString.split("[|]"); + + if(extendedTimeItems.length < dateType+2) { // check for no entry + return defaultDate; + } + + String dateString = extendedTimeItems[dateType+1]; + if(dateString != null && !dateString.equals("")) { // check for blanks + xtDate = parseDate(dateString, xtDate); + } + return xtDate; + } + + + private Date parseDate(String dateString, Date xtDate) { + try { + xtDate = new SimpleDateFormat("MM/dd/yyyy hh:mm:ss aa", Locale.ENGLISH).parse(dateString); + this.hasExtendedTime = true; + } catch(ParseException e) { + e.printStackTrace(); + } + return xtDate; + } + + private boolean isUserInGroup(String groupId) { + String realmId = "/site/"+siteId+"/group/"+groupId; + boolean isMember = false; + try { + AuthzGroup group = AuthzGroupService.getAuthzGroup(realmId); + if (group.getUserRole(AgentFacade.getAgentString()) != null) + isMember = true; + } catch(Exception e) { + return false; // this isn't a group + } + return isMember; + } + + public Integer getTimeLimit() { + return timeLimit; + } + + public void setTimeLimit(Integer timeLimit) { + this.timeLimit = timeLimit; + } + + public Date getStartDate() { + return startDate; + } + + public void setStartDate(Date startDate) { + this.startDate = startDate; + } + + public Date getDueDate() { + return dueDate; + } + + public void setDueDate(Date dueDate) { + this.dueDate = dueDate; + } + + public Date getRetractDate() { + return retractDate; + } + + public void setRetractDate(Date retractDate) { + this.retractDate = retractDate; + } + + public boolean hasExtendedTime() { + return hasExtendedTime; + } + + public void setHasExtendedTime(boolean hasExtendedTime) { + this.hasExtendedTime = hasExtendedTime; + } +} Index: samigo-app/src/webapp/js/authoring.js =================================================================== --- samigo-app/src/webapp/js/authoring.js (revision 132363) +++ samigo-app/src/webapp/js/authoring.js (working copy) @@ -582,3 +582,12 @@ $('#assessmentSettingsAction\\:markForReview1').prop('disabled', ''); } } + +function initTimedCheckBox(){ + var timedHours = document.getElementById("assessmentSettingsAction\:timedHours"); + var timedHoursVal = timedHours.options[timedHours.selectedIndex].value; + var timedMinutes = document.getElementById("assessmentSettingsAction\:timedMinutes"); + var timedMinutesVal = timedMinutes.options[timedMinutes.selectedIndex].value; + + if((timedHoursVal != "0") || (timedMinutesVal != "0")) document.getElementById("assessmentSettingsAction\:selTimeAssess").checked=true;; +} \ No newline at end of file Index: samigo-app/src/webapp/js/extendedTime.js =================================================================== --- samigo-app/src/webapp/js/extendedTime.js (revision 0) +++ samigo-app/src/webapp/js/extendedTime.js (working copy) @@ -0,0 +1,208 @@ +var MAXITEMS = 10; // Max amount of extended time items allowed. We're capping this at 10 for now + +var activeExtTimeEntries = 0; + +// This is the method called from authorSettings.jsp to set up all the extended time stuff +function extendedTimeInitialize() { + addAllExtendedTimeItems(); + allExtendedTimeEntries = document.getElementById("assessmentSettingsAction\:xt1").value.split("^"); + + for (var itemNum = 1; itemNum <= MAXITEMS; itemNum++) { + copyListValuesForExtTime(itemNum); + } + + var itemNum = 1 + do { + activeExtTimeEntries++; + fullExtendedTimeString = allExtendedTimeEntries[itemNum-1].split("|"); + initializeExtTimeValues(fullExtendedTimeString,itemNum); + showActiveExtTimeEntities(fullExtendedTimeString,itemNum); + itemNum++; + + } while(itemNum < allExtendedTimeEntries.length); +} + +// We secretly store the extended time info in a hidden JSF input field. Whenever a user edits an +// extended time value this method gets called on save and updates the hidden field with everything +// that has been entered. When the form is submitted Java sorts it all out. +function extendedTimeCombine() { + document.getElementById("assessmentSettingsAction\:xt1").value = ""; + for (var itemNum = 1; itemNum <= MAXITEMS; itemNum++) { + var target = document.getElementById("xt_id"+itemNum).value; + if(target != "1"){ // don't add empties + var minutes = (parseInt(document.getElementById("xt_hours"+itemNum).value) * 3600) + parseInt(document.getElementById("xt_minutes"+itemNum).value) * 60; + var code = target+"|" + minutes +"|" + + document.getElementById("xt_open"+itemNum).value+"|" + document.getElementById("xt_due"+itemNum).value+"|" + document.getElementById("xt_retract"+itemNum).value + + "^"; + document.getElementById("assessmentSettingsAction\:xt1").value = document.getElementById("assessmentSettingsAction\:xt1").value.concat(code); + } // end if(target != "0") + } //end for + +} + +// Each Extended time item needs to either bring in existing values or +// sync with values already on the page. +function initializeExtTimeValues(fullExtendedTimeString,itemNum) { + //document.getElementById("xt_id"+itemNum).value = fullExtendedTimeString[0]; + var targetId = fullExtendedTimeString[0]; + var seconds = fullExtendedTimeString[1]; + var hours = Math.floor(seconds / (60 * 60)); + var divisor_for_minutes = seconds % (60 * 60); + var minutes = Math.floor(divisor_for_minutes / 60); + + initializeSelectList("xt_id"+itemNum, targetId); + initializeSelectList("xt_hours"+itemNum, hours); + initializeSelectList("xt_minutes"+itemNum, minutes); + + document.getElementById("xt_open"+itemNum).value = evaluateDate(fullExtendedTimeString[2]); + document.getElementById("xt_due"+itemNum).value = evaluateDate(fullExtendedTimeString[3]); + document.getElementById("xt_retract"+itemNum).value = evaluateDate(fullExtendedTimeString[4]); +} + +// Avoid undefined date values +function evaluateDate(dateVal) { + if(dateVal == null) return ""; + else return dateVal; +} + +// If there are already values for extended time, go ahead and show them; otherwise +// hide them until the user activates them. +function showActiveExtTimeEntities(fullExtendedTimeString,itemNum) { + if(document.getElementById("xt_id"+itemNum).value.length > 1) { + document.getElementById("extendedTimeEntries").style.display = 'block'; + document.getElementById("xt"+itemNum).style.display = 'block'; + document.getElementById("xt_show").style.display = 'none'; + + if(document.getElementById("xt_open"+itemNum).value.length > 1) { + document.getElementById("xt_dates"+itemNum).style.display = 'block'; + document.getElementById("xt_datesToggle"+itemNum).checked=true; + } + } else if(itemNum == 1){ // if nothing is set, allow the first one to show so the user can add input + document.getElementById("xt"+itemNum).style.display = 'block'; + } +} + +// Default a select list to a particular value +function initializeSelectList(listName, val) { + var sel = document.getElementById(listName); + for(var i = 0, j = sel.options.length; i < j; ++i) { + if(sel.options[i].value == val) { + sel.selectedIndex = i; + break; + } + } +} + +// Rather than building lists independently. We'll guarantee consistency by copying them from the page. +function copyListValuesForExtTime(itemNum) { + + var srcTargetList = document.getElementById("assessmentSettingsAction\:extendedTimeTarget"); + var options = srcTargetList.innerHTML; + document.getElementById("xt_id"+itemNum).innerHTML = options; + + var srcHoursList = document.getElementById("assessmentSettingsAction\:timedHours"); + options = srcHoursList.innerHTML; + document.getElementById("xt_hours"+itemNum).innerHTML = options; + + var srcMinutesList = document.getElementById("assessmentSettingsAction\:timedMinutes"); + + options = srcMinutesList.innerHTML; + document.getElementById("xt_minutes"+itemNum).innerHTML = options; + +} + +// Control to allow checkboxes to toggle whether a div displays or not. +function toggleExtendedTimeEntity(it, box) { + var vis = (box.checked) ? "block" : "none"; + document.getElementById(it).style.display = vis; + + // They are clearing out the list + if(vis == "none" && it == "extendedTimeEntries") { + deleteAllExtTimeEntries(); + } +} + +// Action whent he first add link is clicked +function showExtendedTime() { + document.getElementById('xt_show').style.display = "none"; + document.getElementById('extendedTimeEntries').style.display = "block"; +} + +// Action when a user clicks to add a new extended time entry +function addExtTimeEntry() { + activeExtTimeEntries++; + document.getElementById("xt"+activeExtTimeEntries).style.display = "block"; + if(activeExtTimeEntries == MAXITEMS) { // prevents them from adding more than max + document.getElementById("addExtTimeControl").style.display = "none"; + } +} + +// Delete Extended Time entry when a user clicks the button +function deleteExtTimeEntry(itemNum) { + document.getElementById("xt_id"+itemNum).value = "1"; + document.getElementById("xt_hours"+itemNum).value = "0"; + document.getElementById("xt_minutes"+itemNum).value = "0"; + deleteExtTimeDates(itemNum); + extendedTimeCombine(); // updates the form input + document.getElementById("xt"+itemNum).style.display = "none"; +} + +function deleteExtTimeDates(itemNum) { + document.getElementById("xt_open"+itemNum).value = ""; + document.getElementById("xt_due"+itemNum).value = ""; + document.getElementById("xt_retract"+itemNum).value = ""; +} + +function deleteAllExtTimeEntries() { + for (var itemNum = 1; itemNum <= MAXITEMS; itemNum++) { + deleteExtTimeEntry(itemNum); + } + + // we need to keep one around for re-adding potientially + activeExtTimeEntries = 1; + document.getElementById("xt1").style.display = "block"; +} + +// Dynamically create a div for each potential extended time item. Most will be hidden. +function addAllExtendedTimeItems() { + var xtItem = document.getElementById("extendedTimeEntries"); + xtItem.innerHTML = ""; + + for (var itemNum = 1; itemNum <= MAXITEMS; itemNum++) { + + var code = "
"+ // none display by default + "
"+ + "  "+ + "hrs. "+ + "min. "+ + "         "+ // delete button + "
"+ + "Change Delivery Dates for this group/student."+ + "
"+ // dates don't display by default + "
"+ + "Available Date"+ + " "+ + "\"Click"+ + "
"+ + "Due Date"+ + " "+ + "\"Click"+ + "
"+ + "Retract Date"+ + " "+ + "\"Click"+ + "
"+ + "
"+ + "
"+ + "
"; + + xtItem.innerHTML = xtItem.innerHTML.concat(code); + } // end for + + // Add the link for allowing the user to add additional extended time entries + var addLinkCode = ""; + xtItem.innerHTML = xtItem.innerHTML.concat(addLinkCode); + +} \ No newline at end of file Index: samigo-app/src/webapp/jsf/author/authorSettings.jsp =================================================================== --- samigo-app/src/webapp/jsf/author/authorSettings.jsp (revision 132363) +++ samigo-app/src/webapp/jsf/author/authorSettings.jsp (working copy) @@ -35,6 +35,7 @@ <h:outputText value="#{assessmentSettingsMessages.sakai_assessment_manager} #{assessmentSettingsMessages.dash} #{assessmentSettingsMessages.settings}" /> + +