Merge branch 'MDL-37804' of github.com:recreate/moodle
authorDan Poltawski <dan@moodle.com>
Mon, 9 Sep 2013 09:13:22 +0000 (17:13 +0800)
committerDan Poltawski <dan@moodle.com>
Mon, 9 Sep 2013 09:13:22 +0000 (17:13 +0800)
1  2 
mod/assign/locallib.php

diff --combined mod/assign/locallib.php
@@@ -44,14 -44,6 +44,14 @@@ define('ASSIGN_ATTEMPT_REOPEN_METHOD_UN
  // Special value means allow unlimited attempts.
  define('ASSIGN_UNLIMITED_ATTEMPTS', -1);
  
 +// Marking workflow states.
 +define('ASSIGN_MARKING_WORKFLOW_STATE_NOTMARKED', 'notmarked');
 +define('ASSIGN_MARKING_WORKFLOW_STATE_INMARKING', 'inmarking');
 +define('ASSIGN_MARKING_WORKFLOW_STATE_READYFORREVIEW', 'readyforreview');
 +define('ASSIGN_MARKING_WORKFLOW_STATE_INREVIEW', 'inreview');
 +define('ASSIGN_MARKING_WORKFLOW_STATE_READYFORRELEASE', 'readyforrelease');
 +define('ASSIGN_MARKING_WORKFLOW_STATE_RELEASED', 'released');
 +
  require_once($CFG->libdir . '/accesslib.php');
  require_once($CFG->libdir . '/formslib.php');
  require_once($CFG->dirroot . '/repository/lib.php');
@@@ -119,15 -111,6 +119,15 @@@ class assign 
      /** @var string modulenameplural prevents excessive calls to get_string */
      private static $modulenameplural = null;
  
 +    /** @var array of marking workflow states for the current user */
 +    private $markingworkflowstates = null;
 +
 +    /** @var bool whether to exclude users with inactive enrolment */
 +    private $showonlyactiveenrol = null;
 +
 +    /** @var array list of suspended user IDs in form of ([id1] => id1) */
 +    public $susers = null;
 +
      /**
       * Constructor for the base assign class.
       *
       *                      otherwise this class will load one from the context as required.
       */
      public function __construct($coursemodulecontext, $coursemodule, $course) {
 -        global $PAGE;
 -
          $this->context = $coursemodulecontext;
          $this->coursemodule = $coursemodule;
          $this->course = $course;
          global $CFG;
          $result = array();
  
 -        $names = get_plugin_list($subtype);
 +        $names = core_component::get_plugin_list($subtype);
  
          foreach ($names as $name => $path) {
              if (file_exists($path . '/locallib.php')) {
              $this->process_unlock();
              $action = 'redirect';
              $nextpageparams['action'] = 'grading';
 +        } else if ($action == 'setbatchmarkingworkflowstate') {
 +            $this->process_set_batch_marking_workflow_state();
 +            $action = 'redirect';
 +            $nextpageparams['action'] = 'grading';
 +        } else if ($action == 'setbatchmarkingallocation') {
 +            $this->process_set_batch_marking_allocation();
 +            $action = 'redirect';
 +            $nextpageparams['action'] = 'grading';
          } else if ($action == 'confirmsubmit') {
              $action = 'submit';
              if ($this->process_submit_for_grading($mform)) {
               $o .= $this->view_plugin_page();
          } else if ($action == 'viewcourseindex') {
               $o .= $this->view_course_index();
 +        } else if ($action == 'viewbatchsetmarkingworkflowstate') {
 +             $o .= $this->view_batch_set_workflow_state($mform);
 +        } else if ($action == 'viewbatchmarkingallocation') {
 +            $o .= $this->view_batch_markingallocation($mform);
          } else {
              $o .= $this->view_submission_page();
          }
          $update->completionsubmit = !empty($formdata->completionsubmit);
          $update->teamsubmission = $formdata->teamsubmission;
          $update->requireallteammemberssubmit = $formdata->requireallteammemberssubmit;
 -        $update->teamsubmissiongroupingid = $formdata->teamsubmissiongroupingid;
 +        if (isset($formdata->teamsubmissiongroupingid)) {
 +            $update->teamsubmissiongroupingid = $formdata->teamsubmissiongroupingid;
 +        }
          $update->blindmarking = $formdata->blindmarking;
          $update->attemptreopenmethod = ASSIGN_ATTEMPT_REOPEN_METHOD_NONE;
          if (!empty($formdata->attemptreopenmethod)) {
          if (!empty($formdata->maxattempts)) {
              $update->maxattempts = $formdata->maxattempts;
          }
 +        $update->markingworkflow = $formdata->markingworkflow;
 +        $update->markingallocation = $formdata->markingallocation;
  
          $returnid = $DB->insert_record('assign', $update);
          $this->instance = $DB->get_record('assign', array('id'=>$returnid), '*', MUST_EXIST);
          }
          $update->teamsubmission = $formdata->teamsubmission;
          $update->requireallteammemberssubmit = $formdata->requireallteammemberssubmit;
 -        $update->teamsubmissiongroupingid = $formdata->teamsubmissiongroupingid;
 +        if (isset($formdata->teamsubmissiongroupingid)) {
 +            $update->teamsubmissiongroupingid = $formdata->teamsubmissiongroupingid;
 +        }
          $update->blindmarking = $formdata->blindmarking;
          $update->attemptreopenmethod = ASSIGN_ATTEMPT_REOPEN_METHOD_NONE;
          if (!empty($formdata->attemptreopenmethod)) {
          if (!empty($formdata->maxattempts)) {
              $update->maxattempts = $formdata->maxattempts;
          }
 +        $update->markingworkflow = $formdata->markingworkflow;
 +        $update->markingallocation = $formdata->markingallocation;
  
          $result = $DB->update_record('assign', $update);
          $this->instance = $DB->get_record('assign', array('id'=>$update->id), '*', MUST_EXIST);
                  if ($grade < 0) {
                      $displaygrade = '';
                  } else {
 -                    $displaygrade = format_float($grade);
 +                    $displaygrade = format_float($grade, 2);
                  }
                  $o .= '<label class="accesshide" for="quickgrade_' . $userid . '">' .
                         get_string('usergrade', 'assign') .
       */
      public function list_participants($currentgroup, $idsonly) {
          if ($idsonly) {
 -            return get_enrolled_users($this->context, 'mod/assign:submit', $currentgroup, 'u.id');
 +            return get_enrolled_users($this->context, 'mod/assign:submit', $currentgroup, 'u.id', null, null, null,
 +                    $this->show_only_active_users());
          } else {
 -            return get_enrolled_users($this->context, 'mod/assign:submit', $currentgroup);
 +            return get_enrolled_users($this->context, 'mod/assign:submit', $currentgroup, 'u.*', null, null, null,
 +                    $this->show_only_active_users());
          }
      }
  
      }
  
      /**
 -     * Load a count of users enrolled in the current course with the specified permission and group.
 +     * Load a count of active users enrolled in the current course with the specified permission and group.
       * 0 for no group.
       *
       * @param int $currentgroup
       * @return int number of matching users
       */
      public function count_participants($currentgroup) {
 -        return count_enrolled_users($this->context, 'mod/assign:submit', $currentgroup);
 +        return count_enrolled_users($this->context, 'mod/assign:submit', $currentgroup, true);
      }
  
      /**
 -     * Load a count of users submissions in the current module that require grading
 +     * Load a count of active users submissions in the current module that require grading
       * This means the submission modification time is more recent than the
       * grading modification time and the status is SUBMITTED.
       *
          }
  
          $currentgroup = groups_get_activity_group($this->get_course_module(), true);
 -        list($esql, $params) = get_enrolled_sql($this->get_context(), 'mod/assign:submit', $currentgroup, false);
 +        list($esql, $params) = get_enrolled_sql($this->get_context(), 'mod/assign:submit', $currentgroup, true);
  
          $submissionmaxattempt = 'SELECT mxs.userid, MAX(mxs.attemptnumber) AS maxattempt
                                   FROM {assign_submission} mxs
          }
  
          $currentgroup = groups_get_activity_group($this->get_course_module(), true);
 -        list($esql, $params) = get_enrolled_sql($this->get_context(), 'mod/assign:submit', $currentgroup, false);
 +        list($esql, $params) = get_enrolled_sql($this->get_context(), 'mod/assign:submit', $currentgroup, true);
  
          $params['assignid'] = $this->get_instance()->id;
  
              $params['groupuserid'] = 0;
          } else {
              $currentgroup = groups_get_activity_group($this->get_course_module(), true);
 -            list($esql, $params) = get_enrolled_sql($this->get_context(), 'mod/assign:submit', $currentgroup, false);
 +            list($esql, $params) = get_enrolled_sql($this->get_context(), 'mod/assign:submit', $currentgroup, true);
  
              $params['assignid'] = $this->get_instance()->id;
  
                         WHERE
                              s.assignment = :assignid AND
                              s.timemodified IS NOT NULL';
 +
          }
  
          return $DB->count_records_sql($sql, $params);
          global $DB;
  
          $currentgroup = groups_get_activity_group($this->get_course_module(), true);
 -        list($esql, $params) = get_enrolled_sql($this->get_context(), 'mod/assign:submit', $currentgroup, false);
 +        list($esql, $params) = get_enrolled_sql($this->get_context(), 'mod/assign:submit', $currentgroup, true);
  
          $params['assignid'] = $this->get_instance()->id;
          $params['assignid2'] = $this->get_instance()->id;
                              s.assignment = :assignid AND
                              s.timemodified IS NOT NULL AND
                              s.status = :submissionstatus';
 +
          }
  
          return $DB->count_records_sql($sql, $params);
  
          $grade->timemodified = time();
  
 +        if (!empty($grade->workflowstate)) {
 +            $validstates = $this->get_marking_workflow_states_for_current_user();
 +            if (!array_key_exists($grade->workflowstate, $validstates)) {
 +                return false;
 +            }
 +        }
 +
          if ($grade->grade && $grade->grade != -1) {
              if ($this->get_instance()->grade > 0) {
                  if (!is_numeric($grade->grade)) {
                  }
              }
          }
 +        // Exclude suspended users, if user can't see them.
 +        if (!has_capability('moodle/course:viewsuspendedusers', $this->context)) {
 +            foreach ($members as $key => $member) {
 +                if (!$this->is_active_user($member->id)) {
 +                    unset($members[$key]);
 +                }
 +            }
 +        }
          return $members;
      }
  
          require_capability('mod/assign:grade', $this->context);
  
          // Load all users with submit.
 -        $students = get_enrolled_users($this->context, "mod/assign:submit");
 +        $students = get_enrolled_users($this->context, "mod/assign:submit", null, 'u.*', null, null, null,
 +                        $this->show_only_active_users());
  
          // Build a list of files to zip.
          $filesforzipping = array();
              $result .= $this->get_renderer()->continue_button($url);
              $result .= $this->view_footer();
          } else if ($zipfile = $this->pack_files($filesforzipping)) {
 -            $this->add_to_log('download all submissions', get_string('downloadall', 'assign'));
 +            $addtolog = $this->add_to_log('download all submissions', get_string('downloadall', 'assign'), '', true);
 +            $params = array(
 +                'context' => $this->context,
 +                'objectid' => $this->get_instance()->id
 +            );
 +            $event = \mod_assign\event\all_submissions_downloaded::create($params);
 +            $event->set_legacy_logdata($addtolog);
 +            $event->trigger();
              // Send file and delete after sending.
              send_temp_file($zipfile, $filename);
              // We will not get here - send_temp_file calls exit.
       * @param string $action The current action
       * @param string $info A detailed description of the change. But no more than 255 characters.
       * @param string $url The url to the assign module instance.
 -     * @return void
 +     * @param bool $return If true, returns the arguments, else adds to log. The purpose of this is to
 +     *                     retrieve the arguments to use them with the new event system (Event 2).
 +     * @return void|array
       */
 -    public function add_to_log($action = '', $info = '', $url='') {
 +    public function add_to_log($action = '', $info = '', $url='', $return = false) {
          global $USER;
  
          $fullurl = 'view.php?id=' . $this->get_course_module()->id;
              $fullurl .= '&' . $url;
          }
  
 -        add_to_log($this->get_course()->id,
 -                   'assign',
 -                   $action,
 -                   $fullurl,
 -                   $info,
 -                   $this->get_course_module()->id,
 -                   $USER->id);
 +        $args = array(
 +            $this->get_course()->id,
 +            'assign',
 +            $action,
 +            $fullurl,
 +            $info,
 +            $this->get_course_module()->id,
 +            $USER->id
 +        );
 +
 +        if ($return) {
 +            return $args;
 +        }
 +        call_user_func_array('add_to_log', $args);
      }
  
      /**
              $flags->userid = $userid;
              $flags->locked = 0;
              $flags->extensionduedate = 0;
 +            $flags->workflowstate = '';
 +            $flags->allocatedmarker = 0;
  
              // The mailed flag can be one of 3 values: 0 is unsent, 1 is sent and 2 is do not send yet.
              // This is because students only want to be notified about certain types of update (grades and feedback).
                                                     $viewfullnames,
                                                     $this->is_blind_marking(),
                                                     $this->get_uniqueid_for_user($user->id),
 -                                                   get_extra_user_fields($this->get_context()));
 +                                                   get_extra_user_fields($this->get_context()),
 +                                                   !$this->is_active_user($userid));
              $o .= $this->get_renderer()->render($usersummary);
          }
          $submission = $this->get_user_submission($userid, false, $attemptnumber);
          $submissiongroup = null;
 -        $submissiongroupmemberswhohavenotsubmitted = array();
          $teamsubmission = null;
          $notsubmitted = array();
          if ($instance->teamsubmission) {
              if ($grade->grade !== null && $grade->grade >= 0) {
                  $data->grade = format_float($grade->grade, 2);
              }
 +            if (!empty($flags->workflowstate)) {
 +                $data->workflowstate = $flags->workflowstate;
 +            }
 +            if (!empty($flags->allocatedmarker)) {
 +                $data->allocatedmarker = $flags->allocatedmarker;
 +            }
          } else {
              $data = new stdClass();
              $data->grade = '';
  
          $perpage = get_user_preferences('assign_perpage', 10);
          $filter = get_user_preferences('assign_filter', '');
 +        $markerfilter = get_user_preferences('assign_markerfilter', '');
 +        $workflowfilter = get_user_preferences('assign_workflowfilter', '');
          $controller = $gradingmanager->get_active_controller();
          $showquickgrading = empty($controller);
          $quickgrading = get_user_preferences('assign_quickgrading', false);
 +        $showonlyactiveenrolopt = has_capability('moodle/course:viewsuspendedusers', $this->context);
 +
 +        $markingallocation = $this->get_instance()->markingallocation &&
 +            has_capability('mod/assign:manageallocations', $this->context);
 +        // Get markers to use in drop lists.
 +        $markingallocationoptions = array();
 +        if ($markingallocation) {
 +            $markers = get_users_by_capability($this->context, 'mod/assign:grade');
 +            $markingallocationoptions[''] = get_string('filternone', 'assign');
 +            foreach ($markers as $marker) {
 +                $markingallocationoptions[$marker->id] = fullname($marker);
 +            }
 +        }
 +
 +        $markingworkflow = $this->get_instance()->markingworkflow;
 +        // Get marking states to show in form.
 +        $markingworkflowoptions = array();
 +        if ($markingworkflow) {
 +            $notmarked = get_string('markingworkflowstatenotmarked', 'assign');
 +            $markingworkflowoptions[''] = get_string('filternone', 'assign');
 +            $markingworkflowoptions[ASSIGN_MARKING_WORKFLOW_STATE_NOTMARKED] = $notmarked;
 +            $markingworkflowoptions = array_merge($markingworkflowoptions, $this->get_marking_workflow_states_for_current_user());
 +        }
  
          // Print options for changing the filter and changing the number of results per page.
          $gradingoptionsformparams = array('cm'=>$cmid,
                                            'userid'=>$USER->id,
                                            'submissionsenabled'=>$this->is_any_submission_plugin_enabled(),
                                            'showquickgrading'=>$showquickgrading,
 -                                          'quickgrading'=>$quickgrading);
 +                                          'quickgrading'=>$quickgrading,
 +                                          'markingworkflowopt'=>$markingworkflowoptions,
 +                                          'markingallocationopt'=>$markingallocationoptions,
 +                                          'showonlyactiveenrolopt'=>$showonlyactiveenrolopt,
 +                                          'showonlyactiveenrol'=>$this->show_only_active_users());
  
          $classoptions = array('class'=>'gradingoptionsform');
          $gradingoptionsform = new mod_assign_grading_options_form(null,
                                   'submissiondrafts'=>$this->get_instance()->submissiondrafts,
                                   'duedate'=>$this->get_instance()->duedate,
                                   'attemptreopenmethod'=>$this->get_instance()->attemptreopenmethod,
 -                                 'feedbackplugins'=>$this->get_feedback_plugins());
 +                                 'feedbackplugins'=>$this->get_feedback_plugins(),
 +                                 'context'=>$this->get_context(),
 +                                 'markingworkflow'=>$markingworkflow,
 +                                 'markingallocation'=>$markingallocation);
          $classoptions = array('class'=>'gradingbatchoperationsform');
  
          $gradingbatchoperationsform = new mod_assign_grading_batch_operations_form(null,
          $gradingoptionsdata = new stdClass();
          $gradingoptionsdata->perpage = $perpage;
          $gradingoptionsdata->filter = $filter;
 +        $gradingoptionsdata->markerfilter = $markerfilter;
 +        $gradingoptionsdata->workflowfilter = $workflowfilter;
          $gradingoptionsform->set_data($gradingoptionsdata);
  
          $actionformtext = $this->get_renderer()->render($gradingactions);
          if ($userid == $USER->id && has_capability('mod/assign:submit', $this->context)) {
              return true;
          }
 +        if (!$this->is_active_user($userid) && !has_capability('moodle/course:viewsuspendedusers', $this->context)) {
 +            return false;
 +        }
          if (has_capability('mod/assign:grade', $this->context)) {
              return true;
          }
          require_once($CFG->dirroot . '/mod/assign/gradingbatchoperationsform.php');
          require_sesskey();
  
 +        $markingallocation = $this->get_instance()->markingallocation &&
 +            has_capability('mod/assign:manageallocations', $this->context);
 +
          $batchformparams = array('cm'=>$this->get_course_module()->id,
                                   'submissiondrafts'=>$this->get_instance()->submissiondrafts,
                                   'duedate'=>$this->get_instance()->duedate,
                                   'attemptreopenmethod'=>$this->get_instance()->attemptreopenmethod,
 -                                 'feedbackplugins'=>$this->get_feedback_plugins());
 +                                 'feedbackplugins'=>$this->get_feedback_plugins(),
 +                                 'context'=>$this->get_context(),
 +                                 'markingworkflow'=>$this->get_instance()->markingworkflow,
 +                                 'markingallocation'=>$markingallocation);
          $formclasses = array('class'=>'gradingbatchoperationsform');
          $mform = new mod_assign_grading_batch_operations_form(null,
                                                                $batchformparams,
                  // Reset the form so the grant extension page will create the extension form.
                  $mform = null;
                  return 'grantextension';
 +            } else if ($data->operation == 'setmarkingworkflowstate') {
 +                return 'viewbatchsetmarkingworkflowstate';
 +            } else if ($data->operation == 'setmarkingallocation') {
 +                return 'viewbatchmarkingallocation';
              } else if (strpos($data->operation, $prefix) === 0) {
                  $tail = substr($data->operation, strlen($prefix));
                  list($plugintype, $action) = explode('_', $tail, 2);
          return 'grading';
      }
  
 +    /**
 +     * Shows a form that allows the workflow state for selected submissions to be changed.
 +     *
 +     * @param moodleform $mform Set to a grading batch operations form
 +     * @return string - the page to view after processing these actions
 +     */
 +    private function view_batch_set_workflow_state($mform) {
 +        global $CFG, $DB;
 +
 +        require_once($CFG->dirroot . '/mod/assign/batchsetmarkingworkflowstateform.php');
 +
 +        $o = '';
 +
 +        $submitteddata = $mform->get_data();
 +        $users = $submitteddata->selectedusers;
 +        $userlist = explode(',', $users);
 +
 +        $formparams = array('cm'=>$this->get_course_module()->id,
 +                            'users'=>$userlist,
 +                            'context'=>$this->get_context());
 +
 +        $usershtml = '';
 +
 +        $usercount = 0;
 +        $extrauserfields = get_extra_user_fields($this->get_context());
 +        foreach ($userlist as $userid) {
 +            if ($usercount >= 5) {
 +                $usershtml .= get_string('moreusers', 'assign', count($userlist) - 5);
 +                break;
 +            }
 +            $user = $DB->get_record('user', array('id'=>$userid), '*', MUST_EXIST);
 +
 +            $usershtml .= $this->get_renderer()->render(new assign_user_summary($user,
 +                                                                $this->get_course()->id,
 +                                                                has_capability('moodle/site:viewfullnames',
 +                                                                $this->get_course_context()),
 +                                                                $this->is_blind_marking(),
 +                                                                $this->get_uniqueid_for_user($user->id),
 +                                                                $extrauserfields,
 +                                                                !$this->is_active_user($userid)));
 +            $usercount += 1;
 +        }
 +
 +        $formparams['usershtml'] = $usershtml;
 +        $formparams['markingworkflowstates'] = $this->get_marking_workflow_states_for_current_user();
 +
 +        $mform = new mod_assign_batch_set_marking_workflow_state_form(null, $formparams);
 +        $o .= $this->get_renderer()->header();
 +        $o .= $this->get_renderer()->render(new assign_form('setworkflowstate', $mform));
 +        $o .= $this->view_footer();
 +
 +        $this->add_to_log('view batch set marking workflow state', get_string('viewbatchsetmarkingworkflowstate', 'assign'));
 +        return $o;
 +    }
 +
 +    /**
 +     * Shows a form that allows the allocated marker for selected submissions to be changed.
 +     *
 +     * @param moodleform $mform Set to a grading batch operations form
 +     * @return string - the page to view after processing these actions
 +     */
 +    private function view_batch_markingallocation($mform) {
 +        global $CFG, $DB;
 +
 +        require_once($CFG->dirroot . '/mod/assign/batchsetallocatedmarkerform.php');
 +
 +        $o = '';
 +
 +        $submitteddata = $mform->get_data();
 +        $users = $submitteddata->selectedusers;
 +        $userlist = explode(',', $users);
 +
 +        $formparams = array('cm'=>$this->get_course_module()->id,
 +            'users'=>$userlist,
 +            'context'=>$this->get_context());
 +
 +        $usershtml = '';
 +
 +        $usercount = 0;
 +        $extrauserfields = get_extra_user_fields($this->get_context());
 +        foreach ($userlist as $userid) {
 +            if ($usercount >= 5) {
 +                $usershtml .= get_string('moreusers', 'assign', count($userlist) - 5);
 +                break;
 +            }
 +            $user = $DB->get_record('user', array('id'=>$userid), '*', MUST_EXIST);
 +
 +            $usershtml .= $this->get_renderer()->render(new assign_user_summary($user,
 +                $this->get_course()->id,
 +                has_capability('moodle/site:viewfullnames',
 +                $this->get_course_context()),
 +                $this->is_blind_marking(),
 +                $this->get_uniqueid_for_user($user->id),
 +                $extrauserfields,
 +                !$this->is_active_user($userid)));
 +            $usercount += 1;
 +        }
 +
 +        $formparams['usershtml'] = $usershtml;
 +        $markers = get_users_by_capability($this->get_context(), 'mod/assign:grade');
 +        $markerlist = array();
 +        foreach ($markers as $marker) {
 +            $markerlist[$marker->id] = fullname($marker);
 +        }
 +
 +        $formparams['markers'] = $markerlist;
 +
 +        $mform = new mod_assign_batch_set_allocatedmarker_form(null, $formparams);
 +        $o .= $this->get_renderer()->header();
 +        $o .= $this->get_renderer()->render(new assign_form('setworkflowstate', $mform));
 +        $o .= $this->view_footer();
 +
 +        $this->add_to_log('view batch set marker allocation', get_string('viewbatchmarkingallocation', 'assign'));
 +        return $o;
 +    }
 +
      /**
       * Ask the user to confirm they want to submit their work for grading.
       *
  
          $data = new stdClass();
          $adminconfig = $this->get_admin_config();
 -        $requiresubmissionstatement = (!empty($adminconfig->requiresubmissionstatement) ||
 -                                       $this->get_instance()->requiresubmissionstatement) &&
 +        $requiresubmissionstatement = $this->get_instance()->requiresubmissionstatement &&
                                         !empty($adminconfig->submissionstatement);
  
          $submissionstatement = '';
                          ($this->is_any_submission_plugin_enabled()) &&
                          $showlinks;
  
 -            $gradelocked = ($flags && $flags->locked) || $this->grading_disabled($user->id);
 +            $gradelocked = ($flags && $flags->locked) || $this->grading_disabled($user->id, false);
  
              // Grading criteria preview.
              $gradingmanager = get_grading_manager($this->context, 'mod_assign', 'submissions');
                  }
              }
  
 +            $gradereleased = true;
 +            if ($this->get_instance()->markingworkflow &&
 +                (empty($grade) || $flags->workflowstate != ASSIGN_MARKING_WORKFLOW_STATE_RELEASED)) {
 +                $gradereleased = false;
 +                $emptyplugins = true; // Don't show feedback plugins until released either.
 +            }
 +
              $cangrade = has_capability('mod/assign:grade', $this->get_context());
 -            // If there is feedback or a visible grade, show the summary.
 -            if ((!empty($gradebookgrade->grade) && ($cangrade || !$gradebookgrade->hidden)) ||
 -                    !$emptyplugins) {
 +            // If there is a visible grade, show the summary.
 +            if ((!empty($gradebookgrade->grade) || !$emptyplugins)
 +                    && ($cangrade || !$gradebookgrade->hidden)) {
  
                  $gradefordisplay = null;
                  $gradeddate = null;
                  // Only show the grade if it is not hidden in gradebook.
                  if (!empty($gradebookgrade->grade) && ($cangrade || !$gradebookgrade->hidden)) {
                      if ($controller = $gradingmanager->get_active_controller()) {
 -                        $controller->set_grade_range(make_grades_menu($this->get_instance()->grade));
 +                        $controller->set_grade_range(make_grades_menu($this->get_instance()->grade), $this->get_instance()->grade > 0);
                          $gradefordisplay = $controller->render_grade($PAGE,
                                                                       $grade->id,
                                                                       $gradingitem,
  
              // Now get the gradefordisplay.
              if ($controller) {
 -                $controller->set_grade_range(make_grades_menu($this->get_instance()->grade));
 +                $controller->set_grade_range(make_grades_menu($this->get_instance()->grade), $this->get_instance()->grade > 0);
                  $grade->gradefordisplay = $controller->render_grade($PAGE,
                                                                       $grade->id,
                                                                       $gradingitem,
          if ($this->is_blind_marking()) {
              return false;
          }
 +        // If marking workflow is enabled and grade is not released then don't send to gradebook yet.
 +        if ($this->get_instance()->markingworkflow && !empty($grade)) {
 +            $flags = $this->get_user_flags($grade->userid, false);
 +            if (empty($flags->workflowstate) || $flags->workflowstate != ASSIGN_MARKING_WORKFLOW_STATE_RELEASED) {
 +                return false;
 +            }
 +        }
 +
          if ($submission != null) {
              if ($submission->userid == 0) {
                  // This is a group submission update.
                  $team = groups_get_members($submission->groupid, 'u.id');
  
                  foreach ($team as $member) {
 -                    $submission->groupid = 0;
 -                    $submission->userid = $member->id;
 -                    $this->gradebook_item_update($submission, null);
 +                    $membersubmission = clone $submission;
 +                    $membersubmission->groupid = 0;
 +                    $membersubmission->userid = $member->id;
 +                    $this->gradebook_item_update($membersubmission, null);
                  }
                  return;
              }
              return false;
          }
          $assign = clone $this->get_instance();
 -        $assign->cmidnumber = $this->get_course_module()->id;
 +        $assign->cmidnumber = $this->get_course_module()->idnumber;
  
          return assign_grade_item_update($assign, $gradebookgrade);
      }
              foreach ($team as $member) {
                  $membersubmission = $this->get_user_submission($member->id, false, $submission->attemptnumber);
  
 -                if (!$membersubmission || $membersubmission->status != ASSIGN_SUBMISSION_STATUS_SUBMITTED) {
 +                // If no submission found for team member and member is active then everyone has not submitted.
 +                if (!$membersubmission || $membersubmission->status != ASSIGN_SUBMISSION_STATUS_SUBMITTED
 +                        && ($this->is_active_user($member->id))) {
                      $allsubmitted = false;
                      if ($anysubmitted) {
                          break;
              }
          }
  
 -        if ($this->grading_disabled($userid)) {
 +        // See if this user grade is locked in the gradebook.
 +        $gradinginfo = grade_get_grades($this->get_course()->id,
 +                                        'mod',
 +                                        'assign',
 +                                        $this->get_instance()->id,
 +                                        array($userid));
 +        if ($gradinginfo &&
 +                isset($gradinginfo->items[0]->grades[$userid]) &&
 +                $gradinginfo->items[0]->grades[$userid]->locked) {
              return false;
          }
  
       * @return array
       */
      protected function get_graders($userid) {
 -        $potentialgraders = get_enrolled_users($this->context, 'mod/assign:grade');
 +        // Potential graders should be active users only.
 +        $potentialgraders = get_enrolled_users($this->context, "mod/assign:grade", null, 'u.*', null, null, null, true);
  
          $graders = array();
          if (groups_get_activity_groupmode($this->get_course_module()) == SEPARATEGROUPS) {
          $instance = $this->get_instance();
          $data = new stdClass();
          $adminconfig = $this->get_admin_config();
 -        $requiresubmissionstatement = (!empty($adminconfig->requiresubmissionstatement) ||
 -                                       $instance->requiresubmissionstatement) &&
 +        $requiresubmissionstatement = $instance->requiresubmissionstatement &&
                                         !empty($adminconfig->submissionstatement);
  
          $submissionstatement = '';
                  // Give each submission plugin a chance to process the submission.
                  $plugins = $this->get_submission_plugins();
                  foreach ($plugins as $plugin) {
 -                    $plugin->submit_for_grading($submission);
 +                    if ($plugin->is_enabled() && $plugin->is_visible()) {
 +                        $plugin->submit_for_grading($submission);
 +                    }
                  }
  
                  $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
                      $logmessage = get_string('submissionstatementacceptedlog',
                                               'mod_assign',
                                               fullname($USER));
 -                    $this->add_to_log('submission statement accepted', $logmessage);
 +                    $addtolog = $this->add_to_log('submission statement accepted', $logmessage, '', true);
 +                    $params = array(
 +                        'context' => $this->context,
 +                        'objectid' => $submission->id
 +                    );
 +                    $event = \mod_assign\event\statement_accepted::create($params);
 +                    $event->set_legacy_logdata($addtolog);
 +                    $event->trigger();
                  }
 -                $this->add_to_log('submit for grading', $this->format_submission_for_log($submission));
 +                $logdata = $this->add_to_log('submit for grading', $this->format_submission_for_log($submission), '', true);
                  $this->notify_graders($submission);
                  $this->notify_student_submission_receipt($submission);
  
                  // Trigger assessable_submitted event on submission.
 -                $eventdata = new stdClass();
 -                $eventdata->modulename   = 'assign';
 -                $eventdata->cmid         = $this->get_course_module()->id;
 -                $eventdata->itemid       = $submission->id;
 -                $eventdata->courseid     = $this->get_course()->id;
 -                $eventdata->userid       = $USER->id;
 -                $eventdata->params       = array( 'submission_editable' => false);
 -                events_trigger('assessable_submitted', $eventdata);
 +                $params = array(
 +                    'context' => context_module::instance($this->get_course_module()->id),
 +                    'objectid' => $submission->id,
 +                    'other' => array(
 +                        'submission_editable' => false
 +                    )
 +                );
 +                $event = \mod_assign\event\assessable_submitted::create($params);
 +                $event->set_legacy_logdata($logdata);
 +                $event->trigger();
              }
          }
          return true;
          $result = $this->update_user_flags($flags);
  
          if ($result) {
 -            $this->add_to_log('grant extension', $userid);
 +            $addtolog = $this->add_to_log('grant extension', $userid, '', true);
 +            $params = array(
 +                'context' => $this->context,
 +                'objectid' => $flags->assignment,
 +                'relateduserid' => $userid
 +            );
 +            $event = \mod_assign\event\extension_granted::create($params);
 +            $event->set_legacy_logdata($addtolog);
 +            $event->trigger();
          }
          return $result;
      }
              $record->userid = $userid;
              if ($modified >= 0) {
                  $record->grade = unformat_float(optional_param('quickgrade_' . $record->userid, -1, PARAM_TEXT));
 +                $record->workflowstate = optional_param('quickgrade_' . $record->userid.'_workflowstate', '', PARAM_TEXT);
 +                $record->allocatedmarker = optional_param('quickgrade_' . $record->userid.'_allocatedmarker', '', PARAM_INT);
              } else {
                  // This user was not in the grading table.
                  continue;
                              FROM {assign_grades} mxg
                              WHERE mxg.assignment = :assignid1 GROUP BY mxg.userid';
  
 -        $sql = 'SELECT u.id as userid, g.grade as grade, g.timemodified as lastmodified
 +        $sql = 'SELECT u.id as userid, g.grade as grade, g.timemodified as lastmodified, uf.workflowstate, uf.allocatedmarker
                      FROM {user} u
                  LEFT JOIN ( ' . $grademaxattempt . ' ) gmx ON u.id = gmx.userid
                  LEFT JOIN {assign_grades} g ON
                      u.id = g.userid AND
                      g.assignment = :assignid2 AND
                      g.attemptnumber = gmx.maxattempt
 +                LEFT JOIN {assign_user_flags} uf ON uf.assignment = g.assignment AND uf.userid = g.userid
                  WHERE u.id ' . $userids;
          $currentgrades = $DB->get_recordset_sql($sql, $params);
  
              if (($current->grade < 0 || $current->grade === null) &&
                  ($modified->grade < 0 || $modified->grade === null)) {
                  // Different ways to indicate no grade.
 -                continue;
 +                $modified->grade = $current->grade; // Keep existing grade.
              }
              // Treat 0 and null as different values.
              if ($current->grade !== null) {
                  $current->grade = floatval($current->grade);
              }
 -            if ($current->grade !== $modified->grade) {
 +            if ($current->grade !== $modified->grade ||
 +                 ($this->get_instance()->markingallocation && $current->allocatedmarker != $modified->allocatedmarker ) ||
 +                 ($this->get_instance()->markingworkflow && $current->workflowstate !== $modified->workflowstate )) {
 +
                  // Grade changed.
                  if ($this->grading_disabled($modified->userid)) {
                      continue;
          // Ok - ready to process the updates.
          foreach ($modifiedusers as $userid => $modified) {
              $grade = $this->get_user_grade($userid, true);
 +            $flags = $this->get_user_flags($userid, true);
              $grade->grade= grade_floatval(unformat_float($modified->grade));
              $grade->grader= $USER->id;
  
                  }
              }
  
 +            if ($flags->workflowstate != $modified->workflowstate ||
 +                $flags->allocatedmarker != $modified->allocatedmarker) {
 +
 +                $flags->workflowstate = $modified->workflowstate;
 +                $flags->allocatedmarker = $modified->allocatedmarker;
 +                $this->update_user_flags($flags);
 +            }
              $this->update_grade($grade);
              $this->notify_grade_modified($grade);
  
                  }
              }
  
 -            $this->add_to_log('grade submission', $this->format_grade_for_log($grade));
 +            $addtolog = $this->add_to_log('grade submission', $this->format_grade_for_log($grade), '', true);
 +            $params = array(
 +                'context' => $this->context,
 +                'objectid' => $grade->id,
 +                'relateduserid' => $userid
 +            );
 +            $event = \mod_assign\event\submission_graded::create($params);
 +            $event->set_legacy_logdata($addtolog);
 +            $event->trigger();
          }
  
          return get_string('quickgradingchangessaved', 'assign');
              $this->gradebook_item_update(null, $grade);
          }
  
 -        $this->add_to_log('reveal identities', get_string('revealidentities', 'assign'));
 +        $addtolog = $this->add_to_log('reveal identities', get_string('revealidentities', 'assign'), '', true);
 +        $params = array(
 +            'context' => $this->context,
 +            'objectid' => $update->id
 +        );
 +        $event = \mod_assign\event\identities_revealed::create($params);
 +        $event->set_legacy_logdata($addtolog);
 +        $event->trigger();
      }
  
  
          $gradingmanager = get_grading_manager($this->get_context(), 'mod_assign', 'submissions');
          $controller = $gradingmanager->get_active_controller();
          $showquickgrading = empty($controller);
 +        if (!is_null($this->context)) {
 +            $showonlyactiveenrolopt = has_capability('moodle/course:viewsuspendedusers', $this->context);
 +        } else {
 +            $showonlyactiveenrolopt = false;
 +        }
 +
 +        $markingallocation = $this->get_instance()->markingallocation &&
 +            has_capability('mod/assign:manageallocations', $this->context);
 +        // Get markers to use in drop lists.
 +        $markingallocationoptions = array();
 +        if ($markingallocation) {
 +            $markingallocationoptions[''] = get_string('filternone', 'assign');
 +            $markers = get_users_by_capability($this->context, 'mod/assign:grade');
 +            foreach ($markers as $marker) {
 +                $markingallocationoptions[$marker->id] = fullname($marker);
 +            }
 +        }
 +
 +        // Get marking states to show in form.
 +        $markingworkflowoptions = array();
 +        if ($this->get_instance()->markingworkflow) {
 +            $notmarked = get_string('markingworkflowstatenotmarked', 'assign');
 +            $markingworkflowoptions[''] = get_string('filternone', 'assign');
 +            $markingworkflowoptions[ASSIGN_MARKING_WORKFLOW_STATE_NOTMARKED] = $notmarked;
 +            $markingworkflowoptions = array_merge($markingworkflowoptions, $this->get_marking_workflow_states_for_current_user());
 +        }
  
          $gradingoptionsparams = array('cm'=>$this->get_course_module()->id,
                                        'contextid'=>$this->context->id,
                                        'userid'=>$USER->id,
                                        'submissionsenabled'=>$this->is_any_submission_plugin_enabled(),
                                        'showquickgrading'=>$showquickgrading,
 -                                      'quickgrading'=>false);
 +                                      'quickgrading'=>false,
 +                                      'markingworkflowopt' => $markingworkflowoptions,
 +                                      'markingallocationopt' => $markingallocationoptions,
 +                                      'showonlyactiveenrolopt'=>$showonlyactiveenrolopt,
 +                                      'showonlyactiveenrol'=>$this->show_only_active_users());
  
          $mform = new mod_assign_grading_options_form(null, $gradingoptionsparams);
          if ($formdata = $mform->get_data()) {
              if (isset($formdata->filter)) {
                  set_user_preference('assign_filter', $formdata->filter);
              }
 +            if (isset($formdata->markerfilter)) {
 +                set_user_preference('assign_markerfilter', $formdata->markerfilter);
 +            }
 +            if (isset($formdata->workflowfilter)) {
 +                set_user_preference('assign_workflowfilter', $formdata->workflowfilter);
 +            }
              if ($showquickgrading) {
                  set_user_preference('assign_quickgrading', isset($formdata->quickgrading));
              }
 +            if (!empty($showonlyactiveenrolopt)) {
 +                $showonlyactiveenrol = isset($formdata->showonlyactiveenrol);
 +                set_user_preference('grade_report_showonlyactiveenrol', $showonlyactiveenrol);
 +                $this->showonlyactiveenrol = $showonlyactiveenrol;
 +            }
          }
      }
  
              return false;
          }
  
 -        $this->add_to_log('submissioncopied', $this->format_submission_for_log($submission));
 +        $addtolog = $this->add_to_log('submissioncopied', $this->format_submission_for_log($submission), '', true);
 +        $params = array(
 +            'context' => $this->context,
 +            'objectid' => $submission->id
 +        );
 +        $event = \mod_assign\event\submission_duplicated::create($params);
 +        $event->set_legacy_logdata($addtolog);
 +        $event->trigger();
  
          $complete = COMPLETION_INCOMPLETE;
          if ($submission->status == ASSIGN_SUBMISSION_STATUS_SUBMITTED) {
              // The same logic applies here - we could not notify teachers,
              // but then they would wonder why there are submitted assignments
              // and they haven't been notified.
 -            $eventdata = new stdClass();
 -            $eventdata->modulename   = 'assign';
 -            $eventdata->cmid         = $this->get_course_module()->id;
 -            $eventdata->itemid       = $submission->id;
 -            $eventdata->courseid     = $this->get_course()->id;
 -            $eventdata->userid       = $USER->id;
 -            $eventdata->params       = array(
 -                'submission_editable' => true,
 +            $params = array(
 +                'context' => context_module::instance($this->get_course_module()->id),
 +                'objectid' => $submission->id,
 +                'other' => array(
 +                    'submission_editable' => true
 +                )
              );
 -            events_trigger('assessable_submitted', $eventdata);
 +            $event = \mod_assign\event\assessable_submitted::create($params);
 +            $event->trigger();
          }
          return true;
      }
              return true;
          }
          if ($data = $mform->get_data()) {
+             if (!$this->submissions_open()) {
+                 $notices[] = get_string('duedatereached', 'assign');
+                 return false;
+             }
              if ($instance->teamsubmission) {
                  $submission = $this->get_group_submission($USER->id, 0, true);
              } else {
              $allempty = true;
              $pluginerror = false;
              foreach ($this->submissionplugins as $plugin) {
 -                if ($plugin->is_enabled()) {
 +                if ($plugin->is_enabled() && $plugin->is_visible()) {
                      if (!$plugin->save($submission, $data)) {
                          $notices[] = $plugin->get_error();
                          $pluginerror = true;
                  $logmessage = get_string('submissionstatementacceptedlog',
                                           'mod_assign',
                                           fullname($USER));
 -                $this->add_to_log('submission statement accepted', $logmessage);
 +                $addtolog = $this->add_to_log('submission statement accepted', $logmessage, '', true);
 +                $params = array(
 +                    'context' => $this->context,
 +                    'objectid' => $submission->id
 +                );
 +                $event = \mod_assign\event\statement_accepted::create($params);
 +                $event->set_legacy_logdata($addtolog);
 +                $event->trigger();
              }
 -            $this->add_to_log('submit', $this->format_submission_for_log($submission));
 +
 +            $addtolog = $this->add_to_log('submit', $this->format_submission_for_log($submission), '', true);
 +            $params = array(
 +                'context' => $this->context,
 +                'objectid' => $submission->id
 +            );
 +            $event = \mod_assign\event\submission_updated::create($params);
 +            $event->set_legacy_logdata($addtolog);
 +            $event->trigger();
  
              $complete = COMPLETION_INCOMPLETE;
              if ($submission->status == ASSIGN_SUBMISSION_STATUS_SUBMITTED) {
                  $this->notify_student_submission_receipt($submission);
                  $this->notify_graders($submission);
                  // Trigger assessable_submitted event on submission.
 -                $eventdata = new stdClass();
 -                $eventdata->modulename   = 'assign';
 -                $eventdata->cmid         = $this->get_course_module()->id;
 -                $eventdata->itemid       = $submission->id;
 -                $eventdata->courseid     = $this->get_course()->id;
 -                $eventdata->userid       = $USER->id;
 -                $eventdata->params       = array(
 -                    'submission_editable' => true,
 +                $params = array(
 +                    'context' => context_module::instance($this->get_course_module()->id),
 +                    'objectid' => $submission->id,
 +                    'other' => array(
 +                        'submission_editable' => true
 +                    )
                  );
 -                events_trigger('assessable_submitted', $eventdata);
 +                $event = \mod_assign\event\assessable_submitted::create($params);
 +                $event->trigger();
              }
              return true;
          }
  
  
      /**
 -     * Determine if this users grade is locked or overridden.
 +     * Determine if this users grade can be edited.
       *
       * @param int $userid - The student userid
 +     * @param bool $checkworkflow - whether to include a check for the workflow state.
       * @return bool $gradingdisabled
       */
 -    public function grading_disabled($userid) {
 +    public function grading_disabled($userid, $checkworkflow=true) {
          global $CFG;
 -
 +        if ($checkworkflow && $this->get_instance()->markingworkflow) {
 +            $grade = $this->get_user_grade($userid, false);
 +            $validstates = $this->get_marking_workflow_states_for_current_user();
 +            if (!empty($grade) && !empty($grade->workflowstate) && !array_key_exists($grade->workflowstate, $validstates)) {
 +                return true;
 +            }
 +        }
          $gradinginfo = grade_get_grades($this->get_course()->id,
                                          'mod',
                                          'assign',
          global $CFG, $USER;
  
          $grademenu = make_grades_menu($this->get_instance()->grade);
 +        $allowgradedecimals = $this->get_instance()->grade > 0;
  
          $advancedgradingwarning = false;
          $gradingmanager = get_grading_manager($this->context, 'mod_assign', 'submissions');
              }
          }
          if ($gradinginstance) {
 -            $gradinginstance->get_controller()->set_grade_range($grademenu);
 +            $gradinginstance->get_controller()->set_grade_range($grademenu, $allowgradedecimals);
          }
          return $gradinginstance;
      }
                  $gradingelement->freeze();
              } else {
                  $mform->addElement('hidden', 'advancedgradinginstanceid', $gradinginstance->get_id());
 +                $mform->setType('advancedgradinginstanceid', PARAM_INT);
              }
          } else {
              // Use simple direct grading.
              if ($this->get_instance()->grade > 0) {
                  $name = get_string('gradeoutof', 'assign', $this->get_instance()->grade);
 -                $gradingelement = $mform->addElement('text', 'grade', $name);
 -                $mform->addHelpButton('grade', 'gradeoutofhelp', 'assign');
 -                $mform->setType('grade', PARAM_TEXT);
 -                if ($gradingdisabled) {
 -                    $gradingelement->freeze();
 +                if (!$gradingdisabled) {
 +                    $gradingelement = $mform->addElement('text', 'grade', $name);
 +                    $mform->addHelpButton('grade', 'gradeoutofhelp', 'assign');
 +                    $mform->setType('grade', PARAM_RAW);
 +                } else {
 +                    $mform->addElement('hidden', 'grade', $name);
 +                    $mform->hardFreeze('grade');
 +                    $mform->setType('grade', PARAM_RAW);
 +                    $strgradelocked = get_string('gradelocked', 'assign');
 +                    $mform->addElement('static', 'gradedisabled', $name, $strgradelocked);
 +                    $mform->addHelpButton('gradedisabled', 'gradeoutofhelp', 'assign');
                  }
              } else {
                  $grademenu = make_grades_menu($this->get_instance()->grade);
              }
              $gradestring = $usergrade;
          }
 +
 +        if ($this->get_instance()->markingworkflow) {
 +            $states = $this->get_marking_workflow_states_for_current_user();
 +            $options = array('' => get_string('markingworkflowstatenotmarked', 'assign')) + $states;
 +            $mform->addElement('select', 'workflowstate', get_string('markingworkflowstate', 'assign'), $options);
 +            $mform->addHelpButton('workflowstate', 'markingworkflowstate', 'assign');
 +        }
 +
 +        if ($this->get_instance()->markingallocation && has_capability('mod/assign:manageallocations', $this->context)) {
 +            $markers = get_users_by_capability($this->context, 'mod/assign:grade');
 +            $markerlist = array('' =>  get_string('choosemarker', 'assign'));
 +            foreach ($markers as $marker) {
 +                $markerlist[$marker->id] = fullname($marker);
 +            }
 +            $mform->addElement('select', 'allocatedmarker', get_string('allocatedmarker', 'assign'), $markerlist);
 +            $mform->addHelpButton('allocatedmarker', 'allocatedmarker', 'assign');
 +            $mform->disabledIf('allocatedmarker', 'workflowstate', 'eq', ASSIGN_MARKING_WORKFLOW_STATE_READYFORREVIEW);
 +            $mform->disabledIf('allocatedmarker', 'workflowstate', 'eq', ASSIGN_MARKING_WORKFLOW_STATE_INREVIEW);
 +            $mform->disabledIf('allocatedmarker', 'workflowstate', 'eq', ASSIGN_MARKING_WORKFLOW_STATE_READYFORRELEASE);
 +            $mform->disabledIf('allocatedmarker', 'workflowstate', 'eq', ASSIGN_MARKING_WORKFLOW_STATE_RELEASED);
 +        }
 +
          $mform->addElement('static', 'currentgrade', get_string('currentgrade', 'assign'), $gradestring);
  
          if (count($useridlist) > 1) {
          // Submission statement.
          $adminconfig = $this->get_admin_config();
  
 -        $requiresubmissionstatement = (!empty($adminconfig->requiresubmissionstatement) ||
 -                                       $this->get_instance()->requiresubmissionstatement) &&
 +        $requiresubmissionstatement = $this->get_instance()->requiresubmissionstatement &&
                                         !empty($adminconfig->submissionstatement);
  
          $draftsenabled = $this->get_instance()->submissiondrafts;
          $submission->status = ASSIGN_SUBMISSION_STATUS_DRAFT;
          $this->update_submission($submission, $userid, true, $this->get_instance()->teamsubmission);
  
 +        // Give each submission plugin a chance to process the reverting to draft.
 +        $plugins = $this->get_submission_plugins();
 +        foreach ($plugins as $plugin) {
 +            if ($plugin->is_enabled() && $plugin->is_visible()) {
 +                $plugin->revert_to_draft($submission);
 +            }
 +        }
          // Update the modified time on the grade (grader modified).
          $grade = $this->get_user_grade($userid, true);
          $grade->grader = $USER->id;
          $logmessage = get_string('reverttodraftforstudent',
                                   'assign',
                                   array('id'=>$user->id, 'fullname'=>fullname($user)));
 -        $this->add_to_log('revert submission to draft', $logmessage);
 +        $addtolog = $this->add_to_log('revert submission to draft', $logmessage, '', true);
 +        $params = array(
 +            'context' => $this->context,
 +            'objectid' => $submission->id,
 +            'relateduserid' => ($this->get_instance()->teamsubmission) ? null : $userid,
 +            'other' => array(
 +                'newstatus' => $submission->status
 +            )
 +        );
 +        $event = \mod_assign\event\submission_status_updated::create($params);
 +        $event->set_legacy_logdata($addtolog);
 +        $event->trigger();
      }
  
      /**
              $userid = required_param('userid', PARAM_INT);
          }
  
 +        // Give each submission plugin a chance to process the locking.
 +        $plugins = $this->get_submission_plugins();
 +        $submission = $this->get_user_submission($userid, false);
 +        foreach ($plugins as $plugin) {
 +            if ($plugin->is_enabled() && $plugin->is_visible()) {
 +                $plugin->lock($submission);
 +            }
 +        }
 +
          $flags = $this->get_user_flags($userid, true);
          $flags->locked = 1;
          $this->update_user_flags($flags);
          $logmessage = get_string('locksubmissionforstudent',
                                   'assign',
                                   array('id'=>$user->id, 'fullname'=>fullname($user)));
 -        $this->add_to_log('lock submission', $logmessage);
 +        $addtolog = $this->add_to_log('lock submission', $logmessage, '', true);
 +        $params = array(
 +            'context' => $this->context,
 +            'objectid' => $flags->assignment,
 +            'relateduserid' => $user->id
 +        );
 +        $event = \mod_assign\event\submission_locked::create($params);
 +        $event->set_legacy_logdata($addtolog);
 +        $event->trigger();
 +    }
 +
 +
 +    /**
 +     * Set the workflow state for multiple users
 +     *
 +     * @return void
 +     */
 +    protected function process_set_batch_marking_workflow_state() {
 +        global $DB;
 +
 +        require_sesskey();
 +
 +        $batchusers = required_param('selectedusers', PARAM_TEXT);
 +        $state = required_param('markingworkflowstate', PARAM_ALPHA);
 +        $useridlist = explode(',', $batchusers);
 +
 +        foreach ($useridlist as $userid) {
 +            $flags = $this->get_user_flags($userid, true);
 +
 +            $flags->workflowstate = $state;
 +
 +            $gradingdisabled = $this->grading_disabled($userid);
 +
 +            // Will not apply update if user does not have permission to assign this workflow state.
 +            if (!$gradingdisabled && $this->update_user_flags($flags)) {
 +                if ($state == ASSIGN_MARKING_WORKFLOW_STATE_RELEASED) {
 +                    // Update Gradebook.
 +                    $assign = clone $this->get_instance();
 +                    $assign->cmidnumber = $this->get_course_module()->idnumber;
 +                    assign_update_grades($assign, $userid);
 +                }
 +
 +                $user = $DB->get_record('user', array('id' => $userid), '*', MUST_EXIST);
 +
 +                $params = array('id'=>$user->id,
 +                                'fullname'=>fullname($user),
 +                                'state'=>$state);
 +                $message = get_string('setmarkingworkflowstateforlog', 'assign', $params);
 +                $addtolog = $this->add_to_log('set marking workflow state', $message, '', true);
 +                $params = array(
 +                    'context' => $this->context,
 +                    'objectid' => $this->get_instance()->id,
 +                    'relateduserid' => $userid,
 +                    'other' => array(
 +                        'newstate' => $state
 +                    )
 +                );
 +                $event = \mod_assign\event\workflow_state_updated::create($params);
 +                $event->set_legacy_logdata($addtolog);
 +                $event->trigger();
 +            }
 +        }
 +    }
 +
 +    /**
 +     * Set the marking allocation for multiple users
 +     *
 +     * @return void
 +     */
 +    protected function process_set_batch_marking_allocation() {
 +        global $DB;
 +
 +        require_sesskey();
 +        require_capability('mod/assign:manageallocations', $this->context);
 +
 +        $batchusers = required_param('selectedusers', PARAM_TEXT);
 +        $markerid = required_param('allocatedmarker', PARAM_INT);
 +        $marker = $DB->get_record('user', array('id' => $markerid), '*', MUST_EXIST);
 +
 +        $useridlist = explode(',', $batchusers);
 +
 +        foreach ($useridlist as $userid) {
 +            $flags = $this->get_user_flags($userid, true);
 +            if ($flags->workflowstate == ASSIGN_MARKING_WORKFLOW_STATE_READYFORREVIEW ||
 +                $flags->workflowstate == ASSIGN_MARKING_WORKFLOW_STATE_INREVIEW ||
 +                $flags->workflowstate == ASSIGN_MARKING_WORKFLOW_STATE_READYFORRELEASE ||
 +                $flags->workflowstate == ASSIGN_MARKING_WORKFLOW_STATE_RELEASED) {
 +
 +                continue; // Allocated marker can only be changed in certain workflow states.
 +            }
 +
 +            $flags->allocatedmarker = $marker->id;
 +
 +            if ($this->update_user_flags($flags)) {
 +
 +                $user = $DB->get_record('user', array('id' => $userid), '*', MUST_EXIST);
 +
 +                $params = array('id'=>$user->id,
 +                    'fullname'=>fullname($user),
 +                    'marker'=>fullname($marker));
 +                $message = get_string('setmarkerallocationforlog', 'assign', $params);
 +                $addtolog = $this->add_to_log('set marking allocation', $message, '', true);
 +                $params = array(
 +                    'context' => $this->context,
 +                    'objectid' => $this->get_instance()->id,
 +                    'relateduserid' => $userid,
 +                    'other' => array(
 +                        'markerid' => $marker->id
 +                    )
 +                );
 +                $event = \mod_assign\event\marker_updated::create($params);
 +                $event->set_legacy_logdata($addtolog);
 +                $event->trigger();
 +            }
 +        }
      }
  
 +
      /**
       * Unlock the process.
       *
          if (!$userid) {
              $userid = required_param('userid', PARAM_INT);
          }
 +        // Give each submission plugin a chance to process the unlocking.
 +        $plugins = $this->get_submission_plugins();
 +        $submission = $this->get_user_submission($userid, false);
 +        foreach ($plugins as $plugin) {
 +            if ($plugin->is_enabled() && $plugin->is_visible()) {
 +                $plugin->unlock($submission);
 +            }
 +        }
  
          $flags = $this->get_user_flags($userid, true);
          $flags->locked = 0;
          $logmessage = get_string('unlocksubmissionforstudent',
                                   'assign',
                                   array('id'=>$user->id, 'fullname'=>fullname($user)));
 -        $this->add_to_log('unlock submission', $logmessage);
 +        $addtolog = $this->add_to_log('unlock submission', $logmessage, '', true);
 +        $params = array(
 +            'context' => $this->context,
 +            'objectid' => $flags->assignment,
 +            'relateduserid' => $user->id
 +        );
 +        $event = \mod_assign\event\submission_unlocked::create($params);
 +        $event->set_legacy_logdata($addtolog);
 +        $event->trigger();
      }
  
      /**
                      $grade->grade = grade_floatval(unformat_float($formdata->grade));
                  }
              }
 +            if (isset($formdata->workflowstate) || isset($formdata->allocatedmarker)) {
 +                $flags = $this->get_user_flags($userid, true);
 +                $flags->workflowstate = isset($formdata->workflowstate) ? $formdata->workflowstate : $flags->workflowstate;
 +                $flags->allocatedmarker = isset($formdata->allocatedmarker) ? $formdata->allocatedmarker : $flags->allocatedmarker;
 +                $this->update_user_flags($flags);
 +            }
          }
          $grade->grader= $USER->id;
  
          }
          $this->update_grade($grade);
          $this->notify_grade_modified($grade);
 -        $user = $DB->get_record('user', array('id' => $userid), '*', MUST_EXIST);
  
 -        $this->add_to_log('grade submission', $this->format_grade_for_log($grade));
 +        $addtolog = $this->add_to_log('grade submission', $this->format_grade_for_log($grade), '', true);
 +        $params = array(
 +            'context' => $this->context,
 +            'objectid' => $grade->id,
 +            'relateduserid' => $userid
 +        );
 +        $event = \mod_assign\event\submission_graded::create($params);
 +        $event->set_legacy_logdata($addtolog);
 +        $event->trigger();
      }
  
  
          return false;
      }
  
 +    /**
 +     * Get the list of marking_workflow states the current user has permission to transition a grade to.
 +     *
 +     * @return array of state => description
 +     */
 +    public function get_marking_workflow_states_for_current_user() {
 +        if (!empty($this->markingworkflowstates)) {
 +            return $this->markingworkflowstates;
 +        }
 +        $states = array();
 +        if (has_capability('mod/assign:grade', $this->context)) {
 +            $states[ASSIGN_MARKING_WORKFLOW_STATE_INMARKING] = get_string('markingworkflowstateinmarking', 'assign');
 +            $states[ASSIGN_MARKING_WORKFLOW_STATE_READYFORREVIEW] = get_string('markingworkflowstatereadyforreview', 'assign');
 +        }
 +        if (has_any_capability(array('mod/assign:reviewgrades',
 +                                     'mod/assign:managegrades'), $this->context)) {
 +            $states[ASSIGN_MARKING_WORKFLOW_STATE_INREVIEW] = get_string('markingworkflowstateinreview', 'assign');
 +            $states[ASSIGN_MARKING_WORKFLOW_STATE_READYFORRELEASE] = get_string('markingworkflowstatereadyforrelease', 'assign');
 +        }
 +        if (has_any_capability(array('mod/assign:releasegrades',
 +                                     'mod/assign:managegrades'), $this->context)) {
 +            $states[ASSIGN_MARKING_WORKFLOW_STATE_RELEASED] = get_string('markingworkflowstatereleased', 'assign');
 +        }
 +        $this->markingworkflowstates = $states;
 +        return $this->markingworkflowstates;
 +    }
 +
 +    /**
 +     * Check is only active users in course should be shown.
 +     *
 +     * @return bool true if only active users should be shown.
 +     */
 +    public function show_only_active_users() {
 +        global $CFG;
 +
 +        if (is_null($this->showonlyactiveenrol)) {
 +            $defaultgradeshowactiveenrol = !empty($CFG->grade_report_showonlyactiveenrol);
 +            $this->showonlyactiveenrol = get_user_preferences('grade_report_showonlyactiveenrol', $defaultgradeshowactiveenrol);
 +
 +            if (!is_null($this->context)) {
 +                $this->showonlyactiveenrol = $this->showonlyactiveenrol ||
 +                            !has_capability('moodle/course:viewsuspendedusers', $this->context);
 +            }
 +        }
 +        return $this->showonlyactiveenrol;
 +    }
 +
 +    /**
 +     * Return true is user is active user in course else false
 +     *
 +     * @param int $userid
 +     * @return bool true is user is active in course.
 +     */
 +    public function is_active_user($userid) {
 +        if (is_null($this->susers) && !is_null($this->context)) {
 +            $this->susers = get_suspended_userids($this->context);
 +        }
 +        return !in_array($userid, $this->susers);
 +    }
  }
  
  /**
@@@ -6671,7 -6033,7 +6675,7 @@@ class assign_portfolio_caller extends p
              } else if ($this->exporter->get('formatclass') == PORTFOLIO_FORMAT_LEAP2A) {
                  $leapwriter = $this->exporter->get('format')->leap2a_writer();
                  $entry = new portfolio_format_leap2a_entry($this->area . $this->cmid,
 -                                                           print_context_name($context),
 +                                                           $context->get_context_name(),
                                                             'resource',
                                                             $html);
  
  
                  // If we have multiple files, they should be grouped together into a folder.
                  $entry = new portfolio_format_leap2a_entry($baseid . 'group',
 -                                                           print_context_name($context),
 +                                                           $context->get_context_name(),
                                                             'selection');
                  $leapwriter->add_entry($entry);
                  $leapwriter->make_selection($entry, $entryids, 'Folder');