/*==========================================================================*\ | $Id: GraderNavigator.java,v 1.7 2012/05/09 16:31:03 stedwar2 Exp $ |*-------------------------------------------------------------------------*| | Copyright (C) 2006-2012 Virginia Tech | | This file is part of Web-CAT. | | Web-CAT is free software; you can redistribute it and/or modify | it under the terms of the GNU Affero General Public License as published | by the Free Software Foundation; either version 3 of the License, or | (at your option) any later version. | | Web-CAT is distributed in the hope that it will be useful, | but WITHOUT ANY WARRANTY; without even the implied warranty of | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | GNU General Public License for more details. | | You should have received a copy of the GNU Affero General Public License | along with Web-CAT; if not, see <http://www.gnu.org/licenses/>. \*==========================================================================*/ package org.webcat.grader; import java.util.HashMap; import java.util.Map; import org.apache.log4j.Logger; import org.webcat.core.CoreNavigator; import org.webcat.core.Course; import org.webcat.core.CourseOffering; import org.webcat.core.EntityUtils; import org.webcat.core.INavigatorObject; import org.webcat.ui.generators.JavascriptGenerator; import com.webobjects.appserver.WOContext; import com.webobjects.eocontrol.EOFetchSpecification; import com.webobjects.eocontrol.EOQualifier; import com.webobjects.eocontrol.EOSortOrdering; import com.webobjects.foundation.NSArray; import com.webobjects.foundation.NSMutableArray; import com.webobjects.foundation.NSTimestamp; import er.extensions.eof.ERXQ; import er.extensions.foundation.ERXArrayUtilities; //-------------------------------------------------------------------------- /** * The popup assignment selector that serves as the basis for the Web-CAT * grader subsystem navigation scheme. * * <h2>Bindings</h2> * <dl> * <dt>allowsAllSemesters</dt> * <dd>A boolean value that adds an option to the semester drop-down that * allows the user to select "All" semesters. If false, the user can only * select a single semester. Defaults to true.</dd> * <dt>allowsAllOfferingsForCourse</dt> * <dd>A boolean value that adds an option for each course in the course * drop-down that allows the user to select "All" offerings for that course. If * false, the user may only select a single offering for a single course. * Defaults to true.</dd> * <dt>includeAdminAccess</dt> * <dd>A boolean value indicating whether the course drop-down should include * courses that the user is not teaching or enrolled in but does have admin * access for. If the user is an administrator, he can change this in the user * interface. Defaults to false.</dd> * <dt>includeWhatImTeaching</dt> * <dd>A boolean value indicating whether the course drop-down should include * courses that the user is teaching. If the user has TA privileges or higher, * he can change this in the user interface. Defaults to true.</dd> * <dt>showClosedAssignments</dt> * <dd>A boolean value indicating whether the assignment drop-down should * include assignments that have been closed for submissions. This option is * only available if the user has TA privileges or higher. Defaults to * false.</dd> * <dt>showUnpublishedAssignments</dt> * <dd>A boolean value indicating whether the assignment drop-down should * include assignments that are unpublished. This option is only available if * the user has TA privileges or higher. Defaults to true.</dd> * <dt>hideClosedAssignmentsFromStudents</dt> * <dd>A boolean value indicating whether the student should be allowed to * choose a closed assignment and be given the option to toggle the visibility * of closed assignments in the navigator. Defaults to false.</dd> * </dl> * * @author Tony Allevato * @author latest changes by: $Author: stedwar2 $ * @version $Revision: 1.7 $ $Date: 2012/05/09 16:31:03 $ */ public class GraderNavigator extends CoreNavigator { //~ Constructors .......................................................... // ---------------------------------------------------------- /** * Create a new object. * * @param context */ public GraderNavigator(WOContext context) { super(context); } //~ KVC attributes (must be public) ....................................... public NSMutableArray<INavigatorObject> assignments; public INavigatorObject assignmentInRepetition; public INavigatorObject selectedAssignment; public boolean hideClosedAssignmentsFromStudents = false; //~ Methods ............................................................... // ---------------------------------------------------------- public void awake() { log.debug("entering awake()"); super.awake(); if (!(selectionsParent instanceof GraderAssignmentComponent)) { throw new IllegalStateException("GraderNavigator can only be " + "embedded inside a GraderAssignmentComponent page"); } graderParent = (GraderAssignmentComponent)selectionsParent; log.debug("selected assignment = " + selectedAssignment); log.debug("graderParent = " + graderParent.getClass().getName()); log.debug("leaving awake()"); } // ---------------------------------------------------------- /** * Gets an array containing the identifiers of the course and assignment * panes, for refresh purposes. * * @return an array containing the course and assignment pane identifiers */ public NSArray<String> idsForCourseAndAssignmentPanes() { return new NSArray<String>(new String[] { idFor.get("coursePane"), idFor.get("assignmentPane") }); } // ---------------------------------------------------------- /** * Updates the list of course offerings, followed by assignments. * * @return the result is ignored */ public JavascriptGenerator updateCourseOfferings() { log.debug("updateCourseOfferings()"); super.updateCourseOfferings(); return updateAssignments().refresh(idFor.get("coursePane")); } // ---------------------------------------------------------- public JavascriptGenerator updateCourseOfferingsAndClearSelection() { log.debug("updateCourseOfferingsAndClearSelection()"); super.updateCourseOfferingsAndClearSelection(); selectedAssignment = null; JavascriptGenerator result = updateAssignments(); if (selectedAssignment == null) { result = result.refresh(idFor.get("coursePane")); } else { saveAssignmentSelection(); result = new JavascriptGenerator().redirectTo(""); } return result; } // ---------------------------------------------------------- public boolean isAssignmentFilterPlaceholder() { return assignmentInRepetition == GraderNavigatorObjects.FILTER_PLACEHOLDER; } // ---------------------------------------------------------- public boolean isAssignmentNoCoursePlaceholder() { return assignmentInRepetition == GraderNavigatorObjects.NO_COURSE_PLACEHOLDER; } // ---------------------------------------------------------- private void gatherAssignments() { log.debug("gatherAssignments()"); assignments = new NSMutableArray<INavigatorObject>(); if (selectedCourseOffering == null) { assignments.addObject( GraderNavigatorObjects.NO_COURSE_PLACEHOLDER); return; } if (userIsStaffForSelectedCourse() || !hideClosedAssignmentsFromStudents) { assignments.addObject( GraderNavigatorObjects.FILTER_PLACEHOLDER); } @SuppressWarnings("unchecked") NSArray<CourseOffering> offerings = (NSArray<CourseOffering>) selectedCourseOffering.representedObjects(); if (offerings == null) { return; } EOQualifier unpublishedQual = null; if (!showUnpublishedAssignments()) { unpublishedQual = ERXQ.isTrue("publish"); } EOFetchSpecification fspec = new EOFetchSpecification( AssignmentOffering.ENTITY_NAME, ERXQ.and( ERXQ.in("courseOffering", offerings), unpublishedQual), null); NSArray<AssignmentOffering> assnOffs = AssignmentOffering .objectsWithFetchSpecification(localContext(), fspec); if (log.isDebugEnabled()) { log.debug("scanning assignment offerings: " + assnOffs); } // Filter out closed assignments (lateDeadline is not a database // property so we have to do it here, in memory. NSTimestamp now = new NSTimestamp(); boolean hideClosed = (hideClosedAssignmentsFromStudents && !userIsStaffForSelectedCourse()) || !showClosedAssignments(); if (hideClosed) { assnOffs = ERXQ.filtered(assnOffs, ERXQ.greaterThan("lateDeadline", now)); } Map<Assignment, Assignment> closedAssigns = new HashMap<Assignment, Assignment>(); Map<Assignment, Assignment> unpublishedAssigns = new HashMap<Assignment, Assignment>(); for (AssignmentOffering ao : assnOffs) { if (!ao.publish()) { Assignment a = ao.assignment(); unpublishedAssigns.put(a, a); } if (ao.lateDeadline() != null && now.after(ao.lateDeadline())) { Assignment a = ao.assignment(); closedAssigns.put(a, a); } } @SuppressWarnings("unchecked") NSArray<Assignment> assigns = ERXArrayUtilities.arrayWithoutDuplicates( (NSArray<Assignment>)assnOffs.valueForKey("assignment")); assigns = EOSortOrdering.sortedArrayUsingKeyOrderArray(assigns, EntityUtils.sortOrderingsForEntityNamed(Assignment.ENTITY_NAME)); Assignment targetAssignment = graderParent.prefs().assignment(); if (targetAssignment == null) { AssignmentOffering ao = graderParent.prefs().assignmentOffering(); if (ao != null) { targetAssignment = ao.assignment(); } } String targetName = null; if (targetAssignment != null) { targetName = targetAssignment.name(); if (targetName == null) { targetName = ""; } targetName = targetName.toLowerCase(); } for (Assignment assignment : assigns) { INavigatorObject thisAssignment = new GraderNavigatorObjects.SingleAssignment( assignment, unpublishedAssigns.containsKey(assignment), closedAssigns.containsKey(assignment)); if (assignment == targetAssignment) { selectedAssignment = thisAssignment; } else if (selectedAssignment == null && targetName != null) { // default to any assignment with the same name, ignoring case String thisName = assignment.name(); if (thisName == null) { thisName = ""; } thisName = thisName.toLowerCase(); if (targetName.equals(thisName)) { selectedAssignment = thisAssignment; } } assignments.addObject(thisAssignment); } if (assignments.count() == 0 && userIsStaffForSelectedCourse()) { // If none were found ... if (!showUnpublishedAssignments()) { // First, try enabling unpublished assignments setShowUnpublishedAssignments(true); gatherAssignments(); } else if (!showClosedAssignments()) { // Then try enabling closed assignments setShowClosedAssignments(true); gatherAssignments(); } } /*if (assignments.count() > firstAssignmentIndex && selectedAssignment == null) { selectedAssignment = assignments.objectAtIndex(firstAssignmentIndex); }*/ if (log.isDebugEnabled()) { log.debug("assignments = " + assignments); log.debug("selected assignment = " + selectedAssignment); } } // ---------------------------------------------------------- /** * Updates the list of available assignments. * * @return the result is ignored */ public JavascriptGenerator updateAssignments() { log.debug("updateAssignments()"); gatherAssignments(); return new JavascriptGenerator().refresh(idFor.get("assignmentPane")) .append("window.navUnderlay.hide(); window.navUnderlay.destroyRecursive()"); } // ---------------------------------------------------------- public JavascriptGenerator updateAssignmentMenu() { log.debug("updateAssignmentsMenu()"); gatherAssignments(); return new JavascriptGenerator().refresh(idFor.get("assignmentMenu")) .append("window.navUnderlay.hide(); window.navUnderlay.destroyRecursive()"); } // ---------------------------------------------------------- public JavascriptGenerator updateAssignmentsAndClearSelection() { log.debug("updateAssignmentsAndClearSelection()"); saveCourseSelection(); selectedAssignment = null; JavascriptGenerator result = updateAssignments(); if (selectedAssignment != null) { saveAssignmentSelection(); result = new JavascriptGenerator().redirectTo(""); } return result; } // ---------------------------------------------------------- protected void saveAssignmentSelection() { if (selectedAssignment != null) { NSArray<?> assignArray = selectedAssignment.representedObjects(); if (assignArray.count() > 0) { Assignment targetAssignment = (Assignment)assignArray.objectAtIndex(0); if (log.isDebugEnabled()) { log.debug("saving assignment selection = " + targetAssignment); } graderParent.prefs().setAssignmentRelationship( targetAssignment); graderParent.prefs().setAssignmentOfferingRelationship(null); CourseOffering co = selectionsParent.coreSelections().courseOffering(); Course course = selectionsParent.coreSelections().course(); for (AssignmentOffering ao : targetAssignment.offerings()) { if ((co != null && co == ao.courseOffering()) || (co == null && course == ao.courseOffering().course())) { if (log.isDebugEnabled()) { log.debug("saving assignment offering selection = " + ao); } graderParent.prefs().setAssignmentOfferingRelationship( ao); break; } } } else { log.debug("saving assignment selection = null"); graderParent.prefs().setAssignmentRelationship(null); } } else { log.debug("saving assignment selection = null"); graderParent.prefs().setAssignmentRelationship(null); } } // ---------------------------------------------------------- @Override protected void saveSelections() { super.saveSelections(); saveAssignmentSelection(); } // ---------------------------------------------------------- public void setShowUnpublishedAssignments( boolean showUnpublishedAssignments) { graderParent.prefs().setShowUnpublishedAssignments( showUnpublishedAssignments); } // ---------------------------------------------------------- public boolean showUnpublishedAssignments() { return graderParent.prefs().showUnpublishedAssignments(); } // ---------------------------------------------------------- public JavascriptGenerator toggleUnpublishedAssignments() { setShowUnpublishedAssignments(!showUnpublishedAssignments()); return updateAssignmentMenu(); } // ---------------------------------------------------------- public void setShowClosedAssignments(boolean showClosedAssignments) { graderParent.prefs().setShowClosedAssignments( showClosedAssignments); } // ---------------------------------------------------------- public boolean showClosedAssignments() { return graderParent.prefs().showClosedAssignments(); } // ---------------------------------------------------------- public JavascriptGenerator toggleClosedAssignments() { setShowClosedAssignments(!showClosedAssignments()); return updateAssignmentMenu(); } // ---------------------------------------------------------- public boolean userIsStaffForSelectedCourse() { if (user().hasAdminPrivileges()) { return true; } if (selectedCourseOffering == null) { return false; } @SuppressWarnings("unchecked") NSArray<CourseOffering> offerings = (NSArray<CourseOffering>) selectedCourseOffering.representedObjects(); for (CourseOffering co : offerings) { if (!co.isGrader(user()) && !co.isInstructor(user())) { return false; } } return true; } //~ Static/instance variables ............................................. protected GraderAssignmentComponent graderParent; static Logger log = Logger.getLogger(GraderNavigator.class); }