private void makeReleaseFeedbackLink(
      UIContainer tofill,
      ViewSubmissionsViewParams viewparams,
      List<AssignmentSubmission> submissionsWithHistory) {
    // check to see if there is anything to release yet
    boolean feedbackExists = false;
    // determine if we are releasing or retracting
    boolean release = false;

    if (submissionsWithHistory != null) {
      for (AssignmentSubmission submission : submissionsWithHistory) {
        if (submission.getSubmissionHistorySet() != null) {
          for (AssignmentSubmissionVersion version : submission.getSubmissionHistorySet()) {
            // only look at versions that have had feedback activity
            if (version.getLastFeedbackDate() != null) {
              feedbackExists = true;
              // if there is at least one version with unreleased feedback,
              // we will show the "Release" link
              if (!version.isFeedbackReleased()) {
                release = true;
              }
            }
          }
        }
      }
    }

    if (feedbackExists) {
      String releaseLinkText;
      if (release) {
        releaseLinkText =
            messageLocator.getMessage("assignment2.assignment_grade-assignment.feedback.release");
      } else {
        releaseLinkText =
            messageLocator.getMessage("assignment2.assignment_grade-assignment.feedback.retract");
      }
      UIForm releaseFeedbackForm = UIForm.make(tofill, "release-feedback-form");
      releaseFeedbackForm.parameters.add(
          new UIELBinding("#{ReleaseFeedbackAction.assignmentId}", assignmentId));
      releaseFeedbackForm.parameters.add(
          new UIELBinding("#{ReleaseFeedbackAction.releaseFeedback}", release));
      UICommand submitAllFeedbackButton =
          UICommand.make(
              releaseFeedbackForm,
              "release_feedback",
              releaseLinkText,
              "#{ReleaseFeedbackAction.execute}");

      UIInternalLink releaseFeedbackLink =
          UIInternalLink.make(tofill, "release-feedback-link", releaseLinkText, viewparams);
      Map<String, String> idmap = new HashMap<String, String>();
      //  idmap.put("onclick",
      // "document.getElementById('"+submitAllFeedbackButton.getFullID()+"').click(); return
      // false;");
      idmap.put(
          "onclick",
          "asnn2.releaseFeedbackDialog('"
              + submitAllFeedbackButton.getFullID()
              + "', "
              + release
              + "); return false;");
      releaseFeedbackLink.decorate(new UIFreeAttributeDecorator(idmap));
    } else {
      // show a disabled link if no feedback to release or retract
      UIOutput.make(
          tofill,
          "release_feedback_disabled",
          messageLocator.getMessage("assignment2.assignment_grade-assignment.feedback.release"));
    }
  }
  public void fillComponents(
      UIContainer tofill, ViewParameters viewparams, ComponentChecker checker) {
    ViewSubmissionsViewParams params = (ViewSubmissionsViewParams) viewparams;
    // make sure that we have an AssignmentID to work with
    if (params.assignmentId == null) {
      // ERROR SHOULD BE SET, OTHERWISE TAKE BACK TO ASSIGNMENT_LIST
      messages.addMessage(new TargettedMessage("GeneralActionError"));
      return;
    }
    assignmentId = params.assignmentId;
    Assignment2 assignment = assignmentLogic.getAssignmentByIdWithAssociatedData(assignmentId);

    String currUserId = externalLogic.getCurrentUserId();

    boolean contentReviewEnabled =
        assignment.isContentReviewEnabled()
            && contentReviewLogic.isContentReviewAvailable(assignment.getContextId());

    // let's double check that none of the associated groups were deleted from the site
    boolean displayGroupDeletionWarning = false;
    if (assignment.getAssignmentGroupSet() != null
        && !assignment.getAssignmentGroupSet().isEmpty()) {
      Collection<Group> siteGroups = externalLogic.getSiteGroups(assignment.getContextId());
      List<String> groupIds = new ArrayList<String>();
      if (siteGroups != null) {
        for (Group group : siteGroups) {
          groupIds.add(group.getId());
        }
      }

      for (AssignmentGroup assignGroup : assignment.getAssignmentGroupSet()) {
        if (!groupIds.contains(assignGroup.getGroupId())) {
          displayGroupDeletionWarning = true;
          break;
        }
      }
    }

    if (displayGroupDeletionWarning) {
      UIOutput.make(
          tofill,
          "deleted_group",
          messageLocator.getMessage("assignment2.assignment_grade-assignment.group_deleted"));
    }

    // Edit Permission
    boolean userMayEditAssign =
        permissionLogic.isUserAllowedToEditAssignment(currUserId, assignment);
    boolean userMayManageSubmissions =
        permissionLogic.isUserAllowedToManageSubmissionsForAssignment(currUserId, assignment);

    // get parameters
    if (params.sort_by == null) params.sort_by = DEFAULT_SORT_BY;
    if (params.sort_dir == null) params.sort_dir = DEFAULT_SORT_DIR;

    UIVerbatim.make(
        tofill, "defaultSortBy", HTMLUtil.emitJavascriptVar("defaultSortBy", DEFAULT_SORT_BY));

    // we need to retrieve the history for the release/retract feedback logic
    List<AssignmentSubmission> submissions =
        submissionLogic.getViewableSubmissionsWithHistoryForAssignmentId(
            assignmentId, params.groupId);
    List<String> studentIdList = new ArrayList<String>();
    if (submissions != null) {
      for (AssignmentSubmission submission : submissions) {
        studentIdList.add(submission.getUserId());
      }
    }

    // The following is some code to populate the sort order/page size, if
    // it's already been put in session state by the entity provider.
    Long pagesize = null;
    String orderBy = null;
    Boolean ascending = null;
    ToolSession toolSession = sessionManager.getCurrentToolSession();
    if (toolSession.getAttribute(Assignment2SubmissionEntityProvider.SUBMISSIONVIEW_SESSION_ATTR)
        != null) {
      Map attr =
          (Map)
              toolSession.getAttribute(
                  Assignment2SubmissionEntityProvider.SUBMISSIONVIEW_SESSION_ATTR);
      if (attr.containsKey(
          Assignment2SubmissionEntityProvider.SUBMISSIONVIEW_SESSION_ATTR_PAGE_SIZE)) {
        pagesize =
            (Long)
                attr.get(Assignment2SubmissionEntityProvider.SUBMISSIONVIEW_SESSION_ATTR_PAGE_SIZE);
      }
      if (attr.containsKey(
          Assignment2SubmissionEntityProvider.SUBMISSIONVIEW_SESSION_ATTR_ORDER_BY)) {
        orderBy =
            (String)
                attr.get(Assignment2SubmissionEntityProvider.SUBMISSIONVIEW_SESSION_ATTR_ORDER_BY);
      }
      if (attr.containsKey(
          Assignment2SubmissionEntityProvider.SUBMISSIONVIEW_SESSION_ATTR_ASCENDING)) {
        ascending =
            (Boolean)
                attr.get(Assignment2SubmissionEntityProvider.SUBMISSIONVIEW_SESSION_ATTR_ASCENDING);
      }
    }

    // if assign is graded, retrieve the gb details, if appropriate
    GradebookItem gbItem = null;
    boolean gbItemExists = false;
    boolean gradesReleased = false;
    // user may view the associated gradebook item
    boolean userMayViewGbItem = false;
    // user has grading privileges for this gb item
    boolean userMayGrade = false;
    boolean userMayReleaseGrades = false;

    if (assignment.isGraded() && assignment.getGradebookItemId() != null) {
      userMayViewGbItem =
          gradebookLogic.isCurrentUserAbleToViewGradebookItem(
              assignment.getContextId(), assignment.getGradebookItemId());

      if (userMayViewGbItem) {
        // user may grade if there is at least one gradable student among the submissions
        List<String> gradableStudents =
            gradebookLogic.getFilteredStudentsForGradebookItem(
                currUserId,
                assignment.getContextId(),
                assignment.getGradebookItemId(),
                AssignmentConstants.GRADE,
                studentIdList);
        userMayGrade = gradableStudents != null && !gradableStudents.isEmpty();
        userMayReleaseGrades = gradebookLogic.isCurrentUserAbleToEdit(assignment.getContextId());

        try {
          gbItem =
              gradebookLogic.getGradebookItemById(
                  assignment.getContextId(), assignment.getGradebookItemId());
          gbItemExists = true;
          gradesReleased = gbItem.isReleased();
        } catch (GradebookItemNotFoundException ginfe) {
          if (log.isDebugEnabled())
            log.debug("Gb item with id: " + assignment.getGradebookItemId() + " no longer exists!");
          gbItem = null;
        }
      }
    }

    // if user has grading privileges but item no longer exists, display warning
    // to user
    if (assignment.isGraded() && userMayViewGbItem && !gbItemExists) {
      UIOutput.make(
          tofill,
          "no_gb_item",
          messageLocator.getMessage("assignment2.assignment_grade-assignment.gb_item_deleted"));
    }

    // We need to check if it's a non electronic submission.  If it is, we don't want to have
    // the submitted columns appear (Submitted and Submission Status).
    // We pass in the boolean parameter nonElectronicSubmission to viewSubmission.js (specifically
    // snn2subview.init()),
    // where logic is there to use this parameter.
    boolean nonElectronicSubmission = false;

    if (assignment.getSubmissionType() == AssignmentConstants.SUBMIT_NON_ELECTRONIC) {
      nonElectronicSubmission = true;
    }

    UIInitBlock.make(
        tofill,
        "asnn2subview-init",
        "asnn2subview.init",
        new Object[] {
          assignmentId,
          externalLogic.getCurrentContextId(),
          placement.getId(),
          submissions.size(),
          assignment.isGraded(),
          contentReviewEnabled,
          nonElectronicSubmission,
          pagesize,
          orderBy,
          ascending,
          gradesReleased,
          params.pageIndex
        });

    // Breadcrumbs
    UIInternalLink.make(
        tofill,
        "breadcrumb",
        messageLocator.getMessage("assignment2.assignment_list-sortview.heading"),
        new SimpleViewParameters(ListProducer.VIEW_ID));
    UIMessage.make(
        tofill,
        "last_breadcrumb",
        "assignment2.assignment_grade-assignment.heading",
        new Object[] {assignment.getTitle()});

    // ACTION BAR
    boolean displayReleaseGrades = false;
    boolean displayReleaseFB = false;
    boolean displayDownloadAll = false;
    boolean displayUploadAll = false;

    if (userMayEditAssign || userMayManageSubmissions) {
      UIOutput.make(tofill, "navIntraTool");
    }

    // RELEASE GRADES
    // don't display this option if the gb item doesn't exist anymore
    if (userMayReleaseGrades && assignment.isGraded() && gbItemExists) {
      displayReleaseGrades = true;

      // determine if grades have been released yet
      String releaseLinkText =
          messageLocator.getMessage("assignment2.assignment_grade-assignment.grades.release");
      if (gradesReleased) {
        releaseLinkText =
            messageLocator.getMessage("assignment2.assignment_grade-assignment.grades.retract");
      }

      UIForm releaseGradesForm = UIForm.make(tofill, "release_grades_form");
      UICommand releaseGradesButton = UICommand.make(releaseGradesForm, "release_grades");

      UIOutput.make(tofill, "release_grades_li");
      UIInternalLink releaseGradesLink =
          UIInternalLink.make(tofill, "release_grades_link", releaseLinkText, viewparams);
      Map<String, String> idmap = new HashMap<String, String>();
      idmap.put(
          "onclick",
          "asnn2.releaseGradesDialog('"
              + releaseGradesButton.getFullID()
              + "', '"
              + assignment.getContextId()
              + "', '"
              + assignment.getGradebookItemId()
              + "', '"
              + !gradesReleased
              + "'); return false;");
      releaseGradesLink.decorate(new UIFreeAttributeDecorator(idmap));

      makeReleaseGradesDialog(gradesReleased, assignment, tofill);
    }

    // RELEASE FEEDBACK
    if (userMayManageSubmissions) {
      displayReleaseFB = true;
      makeReleaseFeedbackLink(tofill, params, submissions);
    }

    // DOWNLOAD ALL
    if (userMayManageSubmissions) {
      displayDownloadAll = true;

      ZipViewParams zvp = new ZipViewParams("zipSubmissions", assignmentId);
      UIInternalLink.make(
          tofill,
          "downloadall",
          UIMessage.make("assignment2.assignment_grade-assignment.downloadall.button"),
          zvp);
    }

    // UPLOAD GRADES & FEEDBACK
    if (userMayManageSubmissions) {
      displayUploadAll = true;

      AssignmentViewParams avp = new AssignmentViewParams("uploadall", assignmentId);
      if (assignment.isGraded() && gbItemExists && userMayGrade) {
        UIInternalLink.make(
            tofill,
            "uploadall",
            UIMessage.make("assignment2.uploadall.breadcrumb.upload.graded"),
            avp);
      } else {
        UIInternalLink.make(
            tofill,
            "uploadall",
            UIMessage.make("assignment2.uploadall.breadcrumb.upload.ungraded"),
            avp);
      }
    }

    // handle those pesky separators
    if (displayReleaseGrades && (displayReleaseFB || displayUploadAll || displayDownloadAll)) {
      UIOutput.make(tofill, "release_grades_sep");
    }

    if (displayReleaseFB && (displayUploadAll || displayDownloadAll)) {
      UIOutput.make(tofill, "release_feedback_sep");
    }

    if (displayDownloadAll && displayUploadAll) {
      UIOutput.make(tofill, "downloadall_sep");
    }

    UIMessage.make(tofill, "page-title", "assignment2.assignment_grade-assignment.title");

    // now make the "View By Sections/Groups" filter
    makeViewByGroupFilter(tofill, params, assignment);

    /*
     * Form for assigning a grade to all submissions without a grade.
     * Do not allow grading if gbItem is null - it must have been deleted
     */
    if (submissions != null
        && !submissions.isEmpty()
        && userMayGrade
        && assignment.isGraded()
        && gbItemExists) {
      createApplyToUngradedWidget(assignment, tofill, params, "unassigned-apply-form0:");
      createApplyToUngradedWidget(assignment, tofill, params, "unassigned-apply-form1:");
    }

    // Confirmation Dialogs
    // These are only added here for internationalization. They are not part
    // of a real form.
    UICommand.make(
        tofill,
        "release-feedback-confirm",
        UIMessage.make("assignment2.dialogs.release_all_feedback.confirm"));
    UICommand.make(
        tofill,
        "release-feedback-cancel",
        UIMessage.make("assignment2.dialogs.release_all_feedback.cancel"));

    UICommand.make(
        tofill,
        "retract-feedback-confirm",
        UIMessage.make("assignment2.dialogs.retract_all_feedback.confirm"));
    UICommand.make(
        tofill,
        "retract-feedback-cancel",
        UIMessage.make("assignment2.dialogs.retract_all_feedback.cancel"));
  }