Merge branch 'MDL-52924-master' of git://github.com/jleyva/moodle
authorDavid Monllao <davidm@moodle.com>
Mon, 21 Mar 2016 01:25:19 +0000 (09:25 +0800)
committerDavid Monllao <davidm@moodle.com>
Mon, 21 Mar 2016 01:25:19 +0000 (09:25 +0800)
Conflicts:
version.php

mod/assign/db/services.php
mod/assign/externallib.php
mod/assign/feedback/editpdf/locallib.php
mod/assign/locallib.php
mod/assign/tests/externallib_test.php
version.php

index cadf5f8..d7dc5fd 100644 (file)
@@ -180,4 +180,14 @@ $functions = array(
             'capabilities'  => 'mod/assign:view',
             'services'      => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
         ),
+
+        'mod_assign_get_submission_status' => array(
+            'classname'     => 'mod_assign_external',
+            'methodname'    => 'get_submission_status',
+            'classpath'     => 'mod/assign/externallib.php',
+            'description'   => 'Returns information about an assignment submission status for a given user.',
+            'type'          => 'read',
+            'capabilities'  => 'mod/assign:view',
+            'services'      => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
+        ),
 );
index ef7d016..2f769e1 100644 (file)
@@ -26,6 +26,7 @@
 defined('MOODLE_INTERNAL') || die;
 
 require_once("$CFG->libdir/externallib.php");
+require_once("$CFG->dirroot/mod/assign/locallib.php");
 
 /**
  * Assign functions
@@ -194,6 +195,29 @@ class mod_assign_external extends external_api {
         return $result;
     }
 
+    /**
+     * Creates a grade single structure.
+     *
+     * @return external_single_structure a grade single structure.
+     * @since  Moodle 3.1
+     */
+    private static function get_grade_structure($required = VALUE_REQUIRED) {
+        return new external_single_structure(
+            array(
+                'id'                => new external_value(PARAM_INT, 'grade id'),
+                'assignment'        => new external_value(PARAM_INT, 'assignment id', VALUE_OPTIONAL),
+                'userid'            => new external_value(PARAM_INT, 'student id'),
+                'attemptnumber'     => new external_value(PARAM_INT, 'attempt number'),
+                'timecreated'       => new external_value(PARAM_INT, 'grade creation time'),
+                'timemodified'      => new external_value(PARAM_INT, 'grade last modified time'),
+                'grader'            => new external_value(PARAM_INT, 'grader'),
+                'grade'             => new external_value(PARAM_TEXT, 'grade'),
+                'gradefordisplay'   => new external_value(PARAM_RAW, 'grade rendered into a format suitable for display',
+                                                            VALUE_OPTIONAL),
+            ), 'grade information', $required
+        );
+    }
+
     /**
      * Creates an assign_grades external_single_structure
      * @return external_single_structure
@@ -202,19 +226,8 @@ class mod_assign_external extends external_api {
     private static function assign_grades() {
         return new external_single_structure(
             array (
-                'assignmentid'    => new external_value(PARAM_INT, 'assignment id'),
-                'grades'   => new external_multiple_structure(new external_single_structure(
-                        array(
-                            'id'            => new external_value(PARAM_INT, 'grade id'),
-                            'userid'        => new external_value(PARAM_INT, 'student id'),
-                            'attemptnumber' => new external_value(PARAM_INT, 'attempt number'),
-                            'timecreated'   => new external_value(PARAM_INT, 'grade creation time'),
-                            'timemodified'  => new external_value(PARAM_INT, 'grade last modified time'),
-                            'grader'        => new external_value(PARAM_INT, 'grader'),
-                            'grade'         => new external_value(PARAM_TEXT, 'grade')
-                        )
-                    )
-                )
+                'assignmentid'  => new external_value(PARAM_INT, 'assignment id'),
+                'grades'        => new external_multiple_structure(self::get_grade_structure())
             )
         );
     }
@@ -275,7 +288,6 @@ class mod_assign_external extends external_api {
      */
     public static function get_assignments($courseids = array(), $capabilities = array(), $includenotenrolledcourses = false) {
         global $USER, $DB, $CFG;
-        require_once("$CFG->dirroot/mod/assign/locallib.php");
 
         $params = self::validate_parameters(
             self::get_assignments_parameters(),
@@ -578,6 +590,72 @@ class mod_assign_external extends external_api {
         );
     }
 
+    /**
+     * Return information (files and text fields) for the given plugins in the assignment.
+     *
+     * @param  assign $assign the assignment object
+     * @param  array $assignplugins array of assignment plugins (submission or feedback)
+     * @param  stdClass $item the item object (submission or grade)
+     * @return array an array containing the plugins returned information
+     */
+    private static function get_plugins_data($assign, $assignplugins, $item) {
+        global $CFG;
+
+        $plugins = array();
+        $fs = get_file_storage();
+
+        foreach ($assignplugins as $assignplugin) {
+
+            if (!$assignplugin->is_enabled() or !$assignplugin->is_visible()) {
+                continue;
+            }
+
+            $plugin = array(
+                'name' => $assignplugin->get_name(),
+                'type' => $assignplugin->get_type()
+            );
+            // Subtype is 'assignsubmission', type is currently 'file' or 'onlinetext'.
+            $component = $assignplugin->get_subtype().'_'.$assignplugin->get_type();
+
+            $fileareas = $assignplugin->get_file_areas();
+            foreach ($fileareas as $filearea => $name) {
+                $fileareainfo = array('area' => $filearea);
+                $files = $fs->get_area_files(
+                    $assign->get_context()->id,
+                    $component,
+                    $filearea,
+                    $item->id,
+                    "timemodified",
+                    false
+                );
+                foreach ($files as $file) {
+                    $filepath = $file->get_filepath().$file->get_filename();
+                    $fileurl = file_encode_url($CFG->wwwroot . '/webservice/pluginfile.php', '/' . $assign->get_context()->id .
+                        '/' . $component. '/'. $filearea . '/' . $item->id . $filepath);
+                    $fileinfo = array(
+                        'filepath' => $filepath,
+                        'fileurl' => $fileurl
+                        );
+                    $fileareainfo['files'][] = $fileinfo;
+                }
+                $plugin['fileareas'][] = $fileareainfo;
+            }
+
+            $editorfields = $assignplugin->get_editor_fields();
+            foreach ($editorfields as $name => $description) {
+                $editorfieldinfo = array(
+                    'name' => $name,
+                    'description' => $description,
+                    'text' => $assignplugin->get_editor_text($name, $item->id),
+                    'format' => $assignplugin->get_editor_format($name, $item->id)
+                );
+                $plugin['editorfields'][] = $editorfieldinfo;
+            }
+            $plugins[] = $plugin;
+        }
+        return $plugins;
+    }
+
     /**
      * Describes the parameters for get_submissions
      *
@@ -610,7 +688,7 @@ class mod_assign_external extends external_api {
      */
     public static function get_submissions($assignmentids, $status = '', $since = 0, $before = 0) {
         global $DB, $CFG;
-        require_once("$CFG->dirroot/mod/assign/locallib.php");
+
         $params = self::validate_parameters(self::get_submissions_parameters(),
                         array('assignmentids' => $assignmentids,
                               'status' => $status,
@@ -647,7 +725,6 @@ class mod_assign_external extends external_api {
 
         foreach ($assigns as $assign) {
             $submissions = array();
-            $submissionplugins = $assign->get_submission_plugins();
             $placeholders = array('assignid1' => $assign->get_instance()->id,
                                   'assignid2' => $assign->get_instance()->id);
 
@@ -677,7 +754,7 @@ class mod_assign_external extends external_api {
             $submissionrecords = $DB->get_records_sql($sql, $placeholders);
 
             if (!empty($submissionrecords)) {
-                $fs = get_file_storage();
+                $submissionplugins = $assign->get_submission_plugins();
                 foreach ($submissionrecords as $submissionrecord) {
                     $submission = array(
                         'id' => $submissionrecord->id,
@@ -686,53 +763,9 @@ class mod_assign_external extends external_api {
                         'timemodified' => $submissionrecord->timemodified,
                         'status' => $submissionrecord->status,
                         'attemptnumber' => $submissionrecord->attemptnumber,
-                        'groupid' => $submissionrecord->groupid
+                        'groupid' => $submissionrecord->groupid,
+                        'plugins' => self::get_plugins_data($assign, $submissionplugins, $submissionrecord)
                     );
-                    foreach ($submissionplugins as $submissionplugin) {
-                        $plugin = array(
-                            'name' => $submissionplugin->get_name(),
-                            'type' => $submissionplugin->get_type()
-                        );
-                        // Subtype is 'assignsubmission', type is currently 'file' or 'onlinetext'.
-                        $component = $submissionplugin->get_subtype().'_'.$submissionplugin->get_type();
-
-                        $fileareas = $submissionplugin->get_file_areas();
-                        foreach ($fileareas as $filearea => $name) {
-                            $fileareainfo = array('area' => $filearea);
-                            $files = $fs->get_area_files(
-                                $assign->get_context()->id,
-                                $component,
-                                $filearea,
-                                $submissionrecord->id,
-                                "timemodified",
-                                false
-                            );
-                            foreach ($files as $file) {
-                                $filepath = $file->get_filepath().$file->get_filename();
-                                $fileurl = file_encode_url($CFG->wwwroot . '/webservice/pluginfile.php', '/' . $assign->get_context()->id .
-                                    '/' . $component. '/'. $filearea . '/' . $submissionrecord->id . $filepath);
-                                $fileinfo = array(
-                                    'filepath' => $filepath,
-                                    'fileurl' => $fileurl
-                                    );
-                                $fileareainfo['files'][] = $fileinfo;
-                            }
-                            $plugin['fileareas'][] = $fileareainfo;
-                        }
-
-                        $editorfields = $submissionplugin->get_editor_fields();
-                        foreach ($editorfields as $name => $description) {
-                            $editorfieldinfo = array(
-                                'name' => $name,
-                                'description' => $description,
-                                'text' => $submissionplugin->get_editor_text($name, $submissionrecord->id),
-                                'format' => $submissionplugin->get_editor_format($name, $submissionrecord->id)
-                            );
-                            $plugin['editorfields'][] = $editorfieldinfo;
-                        }
-
-                        $submission['plugins'][] = $plugin;
-                    }
                     $submissions[] = $submission;
                 }
             } else {
@@ -759,68 +792,83 @@ class mod_assign_external extends external_api {
     }
 
     /**
-     * Creates an assign_submissions external_single_structure
+     * Creates an assignment plugin structure.
      *
-     * @return external_single_structure
-     * @since Moodle 2.5
+     * @return external_single_structure the plugin structure
      */
-    private static function get_submissions_structure() {
+    private static function get_plugin_structure() {
         return new external_single_structure(
-            array (
-                'assignmentid' => new external_value(PARAM_INT, 'assignment id'),
-                'submissions' => new external_multiple_structure(
+            array(
+                'type' => new external_value(PARAM_TEXT, 'submission plugin type'),
+                'name' => new external_value(PARAM_TEXT, 'submission plugin name'),
+                'fileareas' => new external_multiple_structure(
                     new external_single_structure(
-                        array(
-                            'id' => new external_value(PARAM_INT, 'submission id'),
-                            'userid' => new external_value(PARAM_INT, 'student id'),
-                            'attemptnumber' => new external_value(PARAM_INT, 'attempt number'),
-                            'timecreated' => new external_value(PARAM_INT, 'submission creation time'),
-                            'timemodified' => new external_value(PARAM_INT, 'submission last modified time'),
-                            'status' => new external_value(PARAM_TEXT, 'submission status'),
-                            'groupid' => new external_value(PARAM_INT, 'group id'),
-                            'plugins' => new external_multiple_structure(
+                        array (
+                            'area' => new external_value (PARAM_TEXT, 'file area'),
+                            'files' => new external_multiple_structure(
                                 new external_single_structure(
-                                    array(
-                                        'type' => new external_value(PARAM_TEXT, 'submission plugin type'),
-                                        'name' => new external_value(PARAM_TEXT, 'submission plugin name'),
-                                        'fileareas' => new external_multiple_structure(
-                                            new external_single_structure(
-                                                array (
-                                                    'area' => new external_value (PARAM_TEXT, 'file area'),
-                                                    'files' => new external_multiple_structure(
-                                                        new external_single_structure(
-                                                            array (
-                                                                'filepath' => new external_value (PARAM_TEXT, 'file path'),
-                                                                'fileurl' => new external_value (PARAM_URL, 'file download url',
-                                                                    VALUE_OPTIONAL)
-                                                            )
-                                                        ), 'files', VALUE_OPTIONAL
-                                                    )
-                                                )
-                                            ), 'fileareas', VALUE_OPTIONAL
-                                        ),
-                                        'editorfields' => new external_multiple_structure(
-                                            new external_single_structure(
-                                                array(
-                                                    'name' => new external_value(PARAM_TEXT, 'field name'),
-                                                    'description' => new external_value(PARAM_TEXT, 'field description'),
-                                                    'text' => new external_value (PARAM_RAW, 'field value'),
-                                                    'format' => new external_format_value ('text')
-                                                )
-                                            )
-                                            , 'editorfields', VALUE_OPTIONAL
-                                        )
+                                    array (
+                                        'filepath' => new external_value (PARAM_TEXT, 'file path'),
+                                        'fileurl' => new external_value (PARAM_URL, 'file download url',
+                                            VALUE_OPTIONAL)
                                     )
-                                )
-                                , 'plugins', VALUE_OPTIONAL
+                                ), 'files', VALUE_OPTIONAL
                             )
                         )
+                    ), 'fileareas', VALUE_OPTIONAL
+                ),
+                'editorfields' => new external_multiple_structure(
+                    new external_single_structure(
+                        array(
+                            'name' => new external_value(PARAM_TEXT, 'field name'),
+                            'description' => new external_value(PARAM_TEXT, 'field description'),
+                            'text' => new external_value (PARAM_RAW, 'field value'),
+                            'format' => new external_format_value ('text')
+                        )
                     )
+                    , 'editorfields', VALUE_OPTIONAL
                 )
             )
         );
     }
 
+    /**
+     * Creates a submission structure.
+     *
+     * @return external_single_structure the submission structure
+     */
+    private static function get_submission_structure($required = VALUE_REQUIRED) {
+        return new external_single_structure(
+            array(
+                'id' => new external_value(PARAM_INT, 'submission id'),
+                'userid' => new external_value(PARAM_INT, 'student id'),
+                'attemptnumber' => new external_value(PARAM_INT, 'attempt number'),
+                'timecreated' => new external_value(PARAM_INT, 'submission creation time'),
+                'timemodified' => new external_value(PARAM_INT, 'submission last modified time'),
+                'status' => new external_value(PARAM_TEXT, 'submission status'),
+                'groupid' => new external_value(PARAM_INT, 'group id'),
+                'assignment' => new external_value(PARAM_INT, 'assignment id', VALUE_OPTIONAL),
+                'latest' => new external_value(PARAM_INT, 'latest attempt', VALUE_OPTIONAL),
+                'plugins' => new external_multiple_structure(self::get_plugin_structure(), 'plugins', VALUE_OPTIONAL)
+            ), 'submission info', $required
+        );
+    }
+
+    /**
+     * Creates an assign_submissions external_single_structure
+     *
+     * @return external_single_structure
+     * @since Moodle 2.5
+     */
+    private static function get_submissions_structure() {
+        return new external_single_structure(
+            array (
+                'assignmentid' => new external_value(PARAM_INT, 'assignment id'),
+                'submissions' => new external_multiple_structure(self::get_submission_structure())
+            )
+        );
+    }
+
     /**
      * Describes the get_submissions return value
      *
@@ -871,7 +919,6 @@ class mod_assign_external extends external_api {
      */
     public static function set_user_flags($assignmentid, $userflags = array()) {
         global $CFG, $DB;
-        require_once($CFG->dirroot . "/mod/assign/locallib.php");
 
         $params = self::validate_parameters(self::set_user_flags_parameters(),
                                             array('assignmentid' => $assignmentid,
@@ -1303,7 +1350,6 @@ class mod_assign_external extends external_api {
      */
     public static function lock_submissions($assignmentid, $userids) {
         global $CFG;
-        require_once("$CFG->dirroot/mod/assign/locallib.php");
 
         $params = self::validate_parameters(self::lock_submissions_parameters(),
                         array('assignmentid' => $assignmentid,
@@ -1365,7 +1411,6 @@ class mod_assign_external extends external_api {
      */
     public static function revert_submissions_to_draft($assignmentid, $userids) {
         global $CFG;
-        require_once("$CFG->dirroot/mod/assign/locallib.php");
 
         $params = self::validate_parameters(self::revert_submissions_to_draft_parameters(),
                         array('assignmentid' => $assignmentid,
@@ -1427,7 +1472,6 @@ class mod_assign_external extends external_api {
      */
     public static function unlock_submissions($assignmentid, $userids) {
         global $CFG;
-        require_once("$CFG->dirroot/mod/assign/locallib.php");
 
         $params = self::validate_parameters(self::unlock_submissions_parameters(),
                         array('assignmentid' => $assignmentid,
@@ -1485,7 +1529,6 @@ class mod_assign_external extends external_api {
      */
     public static function submit_for_grading($assignmentid, $acceptsubmissionstatement) {
         global $CFG, $USER;
-        require_once("$CFG->dirroot/mod/assign/locallib.php");
 
         $params = self::validate_parameters(self::submit_for_grading_parameters(),
                                             array('assignmentid' => $assignmentid,
@@ -1554,7 +1597,6 @@ class mod_assign_external extends external_api {
      */
     public static function save_user_extensions($assignmentid, $userids, $dates) {
         global $CFG;
-        require_once("$CFG->dirroot/mod/assign/locallib.php");
 
         $params = self::validate_parameters(self::save_user_extensions_parameters(),
                         array('assignmentid' => $assignmentid,
@@ -1622,7 +1664,6 @@ class mod_assign_external extends external_api {
      */
     public static function reveal_identities($assignmentid) {
         global $CFG, $USER;
-        require_once("$CFG->dirroot/mod/assign/locallib.php");
 
         $params = self::validate_parameters(self::reveal_identities_parameters(),
                                             array('assignmentid' => $assignmentid));
@@ -1661,7 +1702,6 @@ class mod_assign_external extends external_api {
      */
     public static function save_submission_parameters() {
         global $CFG;
-        require_once("$CFG->dirroot/mod/assign/locallib.php");
         $instance = new assign(null, null, null);
         $pluginsubmissionparams = array();
 
@@ -1694,7 +1734,6 @@ class mod_assign_external extends external_api {
      */
     public static function save_submission($assignmentid, $plugindata) {
         global $CFG, $USER;
-        require_once("$CFG->dirroot/mod/assign/locallib.php");
 
         $params = self::validate_parameters(self::save_submission_parameters(),
                                             array('assignmentid' => $assignmentid,
@@ -1742,7 +1781,6 @@ class mod_assign_external extends external_api {
      */
     public static function save_grade_parameters() {
         global $CFG;
-        require_once("$CFG->dirroot/mod/assign/locallib.php");
         require_once("$CFG->dirroot/grade/grading/lib.php");
         $instance = new assign(null, null, null);
         $pluginfeedbackparams = array();
@@ -1820,7 +1858,6 @@ class mod_assign_external extends external_api {
                                       $plugindata = array(),
                                       $advancedgradingdata = array()) {
         global $CFG, $USER;
-        require_once("$CFG->dirroot/mod/assign/locallib.php");
 
         $params = self::validate_parameters(self::save_grade_parameters(),
                                             array('assignmentid' => $assignmentid,
@@ -1884,7 +1921,6 @@ class mod_assign_external extends external_api {
      */
     public static function save_grades_parameters() {
         global $CFG;
-        require_once("$CFG->dirroot/mod/assign/locallib.php");
         require_once("$CFG->dirroot/grade/grading/lib.php");
         $instance = new assign(null, null, null);
         $pluginfeedbackparams = array();
@@ -1966,7 +2002,6 @@ class mod_assign_external extends external_api {
      */
     public static function save_grades($assignmentid, $applytoall = false, $grades) {
         global $CFG, $USER;
-        require_once("$CFG->dirroot/mod/assign/locallib.php");
 
         $params = self::validate_parameters(self::save_grades_parameters(),
                                             array('assignmentid' => $assignmentid,
@@ -2052,7 +2087,6 @@ class mod_assign_external extends external_api {
      */
     public static function copy_previous_attempt($assignmentid) {
         global $CFG, $USER;
-        require_once("$CFG->dirroot/mod/assign/locallib.php");
 
         $params = self::validate_parameters(self::copy_previous_attempt_parameters(),
                                             array('assignmentid' => $assignmentid));
@@ -2111,7 +2145,6 @@ class mod_assign_external extends external_api {
      */
     public static function view_grading_table($assignid) {
         global $DB, $CFG;
-        require_once($CFG->dirroot . "/mod/assign/locallib.php");
 
         $params = self::validate_parameters(self::view_grading_table_parameters(),
                                             array(
@@ -2175,7 +2208,6 @@ class mod_assign_external extends external_api {
      */
     public static function view_submission_status($assignid) {
         global $DB, $CFG;
-        require_once("$CFG->dirroot/mod/assign/locallib.php");
 
         $warnings = array();
         $params = array(
@@ -2215,4 +2247,243 @@ class mod_assign_external extends external_api {
         );
     }
 
+    /**
+     * Describes the parameters for get_submission_status.
+     *
+     * @return external_external_function_parameters
+     * @since Moodle 3.1
+     */
+    public static function get_submission_status_parameters() {
+        return new external_function_parameters (
+            array(
+                'assignid' => new external_value(PARAM_INT, 'assignment instance id'),
+                'userid' => new external_value(PARAM_INT, 'user id (empty for current user)', VALUE_DEFAULT, 0),
+            )
+        );
+    }
+
+    /**
+     * Returns information about an assignment submission status for a given user.
+     *
+     * @param int $assignid assignment instance id
+     * @param int $userid user id (empty for current user)
+     * @return array of warnings and grading, status, feedback and previous attempts information
+     * @since Moodle 3.1
+     * @throws required_capability_exception
+     */
+    public static function get_submission_status($assignid, $userid = 0) {
+        global $USER, $DB;
+
+        $warnings = array();
+
+        $params = array(
+            'assignid' => $assignid,
+            'userid' => $userid,
+        );
+        $params = self::validate_parameters(self::get_submission_status_parameters(), $params);
+
+        // Request and permission validation.
+        $assign = $DB->get_record('assign', array('id' => $params['assignid']), 'id', MUST_EXIST);
+        list($course, $cm) = get_course_and_cm_from_instance($assign, 'assign');
+
+        $context = context_module::instance($cm->id);
+        self::validate_context($context);
+
+        $assign = new assign($context, $cm, $course);
+
+        // Default value for userid.
+        if (empty($params['userid'])) {
+            $params['userid'] = $USER->id;
+        }
+        $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
+        core_user::require_active_user($user);
+
+        if (!$assign->can_view_submission($user->id)) {
+            throw new required_capability_exception($context, 'mod/assign:viewgrades', 'nopermission', '');
+        }
+
+        $gradingsummary = $lastattempt = $feedback = $previousattempts = null;
+
+        // Get the renderable since it contais all the info we need.
+        if ($assign->can_view_grades()) {
+            $gradingsummary = $assign->get_assign_grading_summary_renderable();
+        }
+
+        // Retrieve the rest of the renderable objects.
+        if (has_capability('mod/assign:submit', $assign->get_context(), $user)) {
+            $lastattempt = $assign->get_assign_submission_status_renderable($user, true);
+        }
+
+        $feedback = $assign->get_assign_feedback_status_renderable($user);
+
+        $previousattempts = $assign->get_assign_attempt_history_renderable($user);
+
+        // Now, build the result.
+        $result = array();
+
+        // First of all, grading summary, this is suitable for teachers/managers.
+        if ($gradingsummary) {
+            $result['gradingsummary'] = $gradingsummary;
+        }
+
+        // Did we submit anything?
+        if ($lastattempt) {
+            $submissionplugins = $assign->get_submission_plugins();
+
+            if (empty($lastattempt->submission)) {
+                unset($lastattempt->submission);
+            } else {
+                $lastattempt->submission->plugins = self::get_plugins_data($assign, $submissionplugins, $lastattempt->submission);
+            }
+
+            if (empty($lastattempt->teamsubmission)) {
+                unset($lastattempt->teamsubmission);
+            } else {
+                $lastattempt->teamsubmission->plugins = self::get_plugins_data($assign, $submissionplugins,
+                                                                                $lastattempt->teamsubmission);
+            }
+
+            // We need to change the type of some of the structures retrieved from the renderable.
+            if (!empty($lastattempt->submissiongroup)) {
+                $lastattempt->submissiongroup = $lastattempt->submissiongroup->id;
+            }
+            if (!empty($lastattempt->usergroups)) {
+                $lastattempt->usergroups = array_keys($lastattempt->usergroups);
+            }
+            // We cannot use array_keys here.
+            if (!empty($lastattempt->submissiongroupmemberswhoneedtosubmit)) {
+                $lastattempt->submissiongroupmemberswhoneedtosubmit = array_map(
+                                                                            function($e){
+                                                                                return $e->id;
+                                                                            },
+                                                                            $lastattempt->submissiongroupmemberswhoneedtosubmit);
+            }
+
+            $result['lastattempt'] = $lastattempt;
+        }
+
+        // The feedback for our latest submission.
+        if ($feedback) {
+            if ($feedback->grade) {
+                $feedbackplugins = $assign->get_feedback_plugins();
+                $feedback->plugins = self::get_plugins_data($assign, $feedbackplugins, $feedback->grade);
+            } else {
+                unset($feedback->plugins);
+                unset($feedback->grade);
+            }
+
+            $result['feedback'] = $feedback;
+        }
+
+        // Retrieve only previous attempts.
+        if ($previousattempts and count($previousattempts->submissions) > 1) {
+            // Don't show the last one because it is the current submission.
+            array_pop($previousattempts->submissions);
+
+            // Show newest to oldest.
+            $previousattempts->submissions = array_reverse($previousattempts->submissions);
+
+            foreach ($previousattempts->submissions as $i => $submission) {
+                $attempt = array();
+
+                $grade = null;
+                foreach ($previousattempts->grades as $onegrade) {
+                    if ($onegrade->attemptnumber == $submission->attemptnumber) {
+                        $grade = $onegrade;
+                        break;
+                    }
+                }
+
+                $attempt['attemptnumber'] = $submission->attemptnumber;
+
+                if ($submission) {
+                    $submission->plugins = self::get_plugins_data($assign, $previousattempts->submissionplugins, $submission);
+                    $attempt['submission'] = $submission;
+                }
+
+                if ($grade) {
+                    // From object to id.
+                    $grade->grader = $grade->grader->id;
+                    $feedbackplugins = self::get_plugins_data($assign, $previousattempts->feedbackplugins, $grade);
+
+                    $attempt['grade'] = $grade;
+                    $attempt['feedbackplugins'] = $feedbackplugins;
+                }
+                $result['previousattempts'][] = $attempt;
+            }
+        }
+
+        $result['warnings'] = $warnings;
+        return $result;
+    }
+
+    /**
+     * Describes the get_submission_status return value.
+     *
+     * @return external_single_structure
+     * @since Moodle 3.1
+     */
+    public static function get_submission_status_returns() {
+        return new external_single_structure(
+            array(
+                'gradingsummary' => new external_single_structure(
+                    array(
+                        'participantcount' => new external_value(PARAM_INT, 'Number of users who can submit.'),
+                        'submissiondraftscount' => new external_value(PARAM_INT, 'Number of submissions in draft status.'),
+                        'submissiondraftscount' => new external_value(PARAM_INT, 'Number of submissions in draft status.'),
+                        'submissionsenabled' => new external_value(PARAM_BOOL, 'Whether submissions are enabled or not.'),
+                        'submissionssubmittedcount' => new external_value(PARAM_INT, 'Number of submissions in submitted status.'),
+                        'submissionsneedgradingcount' => new external_value(PARAM_INT, 'Number of submissions that need grading.'),
+                        'warnofungroupedusers' => new external_value(PARAM_BOOL, 'Whether we need to warn people that there
+                                                                        are users without groups.'),
+                    ), 'Grading information.', VALUE_OPTIONAL
+                ),
+                'lastattempt' => new external_single_structure(
+                    array(
+                        'submission' => self::get_submission_structure(VALUE_OPTIONAL),
+                        'teamsubmission' => self::get_submission_structure(VALUE_OPTIONAL),
+                        'submissiongroup' => new external_value(PARAM_INT, 'The submission group id (for group submissions only).',
+                                                                VALUE_OPTIONAL),
+                        'submissiongroupmemberswhoneedtosubmit' => new external_multiple_structure(
+                            new external_value(PARAM_INT, 'USER id.'),
+                            'List of users who still need to submit (for group submissions only).',
+                            VALUE_OPTIONAL
+                        ),
+                        'submissionsenabled' => new external_value(PARAM_BOOL, 'Whether submissions are enabled or not.'),
+                        'locked' => new external_value(PARAM_BOOL, 'Whether new submissions are locked.'),
+                        'graded' => new external_value(PARAM_BOOL, 'Whether the submission is graded.'),
+                        'canedit' => new external_value(PARAM_BOOL, 'Whether the user can edit the current submission.'),
+                        'cansubmit' => new external_value(PARAM_BOOL, 'Whether the user can submit.'),
+                        'extensionduedate' => new external_value(PARAM_INT, 'Extension due date.'),
+                        'blindmarking' => new external_value(PARAM_BOOL, 'Whether blind marking is enabled.'),
+                        'gradingstatus' => new external_value(PARAM_ALPHANUMEXT, 'Grading status.'),
+                        'usergroups' => new external_multiple_structure(
+                            new external_value(PARAM_INT, 'Group id.'), 'User groups in the course.'
+                        ),
+                    ), 'Last attempt information.', VALUE_OPTIONAL
+                ),
+                'feedback' => new external_single_structure(
+                    array(
+                        'grade' => self::get_grade_structure(VALUE_OPTIONAL),
+                        'gradefordisplay' => new external_value(PARAM_RAW, 'Grade rendered into a format suitable for display.'),
+                        'gradeddate' => new external_value(PARAM_INT, 'The date the user was graded.'),
+                        'plugins' => new external_multiple_structure(self::get_plugin_structure(), 'Plugins info.', VALUE_OPTIONAL),
+                    ), 'Feedback for the last attempt.', VALUE_OPTIONAL
+                ),
+                'previousattempts' => new external_multiple_structure(
+                    new external_single_structure(
+                        array(
+                            'attemptnumber' => new external_value(PARAM_INT, 'Attempt number.'),
+                            'submission' => self::get_submission_structure(VALUE_OPTIONAL),
+                            'grade' => self::get_grade_structure(VALUE_OPTIONAL),
+                            'feedbackplugins' => new external_multiple_structure(self::get_plugin_structure(), 'Feedback info.',
+                                                                                    VALUE_OPTIONAL),
+                        )
+                    ), 'List all the previous attempts did by the user.', VALUE_OPTIONAL
+                ),
+                'warnings' => new external_warnings(),
+            )
+        );
+    }
+
 }
index f2c5b8d..7eddad1 100644 (file)
@@ -355,4 +355,13 @@ class assign_feedback_editpdf extends assign_feedback_plugin {
     public function is_configurable() {
         return false;
     }
+
+    /**
+     * Get file areas returns a list of areas this plugin stores files.
+     *
+     * @return array - An array of fileareas (keys) and descriptions (values)
+     */
+    public function get_file_areas() {
+        return array(document_services::FINAL_PDF_FILEAREA => $this->get_name());
+    }
 }
index ad93008..9c462e2 100644 (file)
@@ -217,7 +217,10 @@ class assign {
     public function get_return_action() {
         global $PAGE;
 
-        $params = $PAGE->url->params();
+        // Web services don't set a URL, we should avoid debugging when ussing the url object.
+        if (!WS_SERVER) {
+            $params = $PAGE->url->params();
+        }
 
         if (!empty($params['action'])) {
             return $params['action'];
@@ -3999,21 +4002,18 @@ class assign {
     }
 
     /**
-     * Print 2 tables of information with no action links -
-     * the submission summary and the grading summary.
+     * Creates an assign_submission_status renderable.
      *
-     * @param stdClass $user the user to print the report for
-     * @param bool $showlinks - Return plain text or links to the profile
-     * @return string - the html summary
+     * @param stdClass $user the user to get the report for
+     * @param bool $showlinks return plain text or links to the profile
+     * @return assign_submission_status renderable object
      */
-    public function view_student_summary($user, $showlinks) {
-        global $CFG, $DB, $PAGE;
+    public function get_assign_submission_status_renderable($user, $showlinks) {
+        global $PAGE;
 
         $instance = $this->get_instance();
-        $grade = $this->get_user_grade($user->id, false);
         $flags = $this->get_user_flags($user->id, false);
         $submission = $this->get_user_submission($user->id, false);
-        $o = '';
 
         $teamsubmission = null;
         $submissiongroup = null;
@@ -4028,159 +4028,208 @@ class assign {
             $notsubmitted = $this->get_submission_group_members_who_have_not_submitted($groupid, false);
         }
 
-        if ($this->can_view_submission($user->id)) {
-            $showedit = $showlinks &&
-                        ($this->is_any_submission_plugin_enabled()) &&
-                        $this->can_edit_submission($user->id);
-
-            $gradelocked = ($flags && $flags->locked) || $this->grading_disabled($user->id, false);
-
-            // Grading criteria preview.
-            $gradingmanager = get_grading_manager($this->context, 'mod_assign', 'submissions');
-            $gradingcontrollerpreview = '';
-            if ($gradingmethod = $gradingmanager->get_active_method()) {
-                $controller = $gradingmanager->get_controller($gradingmethod);
-                if ($controller->is_form_defined()) {
-                    $gradingcontrollerpreview = $controller->render_preview($PAGE);
-                }
-            }
+        $showedit = $showlinks &&
+                    ($this->is_any_submission_plugin_enabled()) &&
+                    $this->can_edit_submission($user->id);
 
-            $showsubmit = ($showlinks && $this->submissions_open($user->id));
-            $showsubmit = ($showsubmit && $this->show_submit_button($submission, $teamsubmission, $user->id));
+        $gradelocked = ($flags && $flags->locked) || $this->grading_disabled($user->id, false);
 
-            $extensionduedate = null;
-            if ($flags) {
-                $extensionduedate = $flags->extensionduedate;
+        // Grading criteria preview.
+        $gradingmanager = get_grading_manager($this->context, 'mod_assign', 'submissions');
+        $gradingcontrollerpreview = '';
+        if ($gradingmethod = $gradingmanager->get_active_method()) {
+            $controller = $gradingmanager->get_controller($gradingmethod);
+            if ($controller->is_form_defined()) {
+                $gradingcontrollerpreview = $controller->render_preview($PAGE);
             }
-            $viewfullnames = has_capability('moodle/site:viewfullnames', $this->get_course_context());
+        }
 
-            $gradingstatus = $this->get_grading_status($user->id);
-            $usergroups = $this->get_all_groups($user->id);
-            $submissionstatus = new assign_submission_status($instance->allowsubmissionsfromdate,
-                                                              $instance->alwaysshowdescription,
-                                                              $submission,
-                                                              $instance->teamsubmission,
-                                                              $teamsubmission,
-                                                              $submissiongroup,
-                                                              $notsubmitted,
-                                                              $this->is_any_submission_plugin_enabled(),
-                                                              $gradelocked,
-                                                              $this->is_graded($user->id),
-                                                              $instance->duedate,
-                                                              $instance->cutoffdate,
-                                                              $this->get_submission_plugins(),
-                                                              $this->get_return_action(),
-                                                              $this->get_return_params(),
-                                                              $this->get_course_module()->id,
-                                                              $this->get_course()->id,
-                                                              assign_submission_status::STUDENT_VIEW,
-                                                              $showedit,
-                                                              $showsubmit,
-                                                              $viewfullnames,
-                                                              $extensionduedate,
-                                                              $this->get_context(),
-                                                              $this->is_blind_marking(),
-                                                              $gradingcontrollerpreview,
-                                                              $instance->attemptreopenmethod,
-                                                              $instance->maxattempts,
-                                                              $gradingstatus,
-                                                              $instance->preventsubmissionnotingroup,
-                                                              $usergroups);
-            if (has_capability('mod/assign:submit', $this->get_context(), $user)) {
-                $o .= $this->get_renderer()->render($submissionstatus);
-            }
+        $showsubmit = ($showlinks && $this->submissions_open($user->id));
+        $showsubmit = ($showsubmit && $this->show_submit_button($submission, $teamsubmission, $user->id));
 
-            require_once($CFG->libdir.'/gradelib.php');
-            require_once($CFG->dirroot.'/grade/grading/lib.php');
+        $extensionduedate = null;
+        if ($flags) {
+            $extensionduedate = $flags->extensionduedate;
+        }
+        $viewfullnames = has_capability('moodle/site:viewfullnames', $this->get_course_context());
+
+        $gradingstatus = $this->get_grading_status($user->id);
+        $usergroups = $this->get_all_groups($user->id);
+        $submissionstatus = new assign_submission_status($instance->allowsubmissionsfromdate,
+                                                          $instance->alwaysshowdescription,
+                                                          $submission,
+                                                          $instance->teamsubmission,
+                                                          $teamsubmission,
+                                                          $submissiongroup,
+                                                          $notsubmitted,
+                                                          $this->is_any_submission_plugin_enabled(),
+                                                          $gradelocked,
+                                                          $this->is_graded($user->id),
+                                                          $instance->duedate,
+                                                          $instance->cutoffdate,
+                                                          $this->get_submission_plugins(),
+                                                          $this->get_return_action(),
+                                                          $this->get_return_params(),
+                                                          $this->get_course_module()->id,
+                                                          $this->get_course()->id,
+                                                          assign_submission_status::STUDENT_VIEW,
+                                                          $showedit,
+                                                          $showsubmit,
+                                                          $viewfullnames,
+                                                          $extensionduedate,
+                                                          $this->get_context(),
+                                                          $this->is_blind_marking(),
+                                                          $gradingcontrollerpreview,
+                                                          $instance->attemptreopenmethod,
+                                                          $instance->maxattempts,
+                                                          $gradingstatus,
+                                                          $instance->preventsubmissionnotingroup,
+                                                          $usergroups);
+        return $submissionstatus;
+    }
+
+
+    /**
+     * Creates an assign_feedback_status renderable.
+     *
+     * @param stdClass $user the user to get the report for
+     * @return assign_feedback_status renderable object
+     */
+    public function get_assign_feedback_status_renderable($user) {
+        global $CFG, $DB, $PAGE;
 
-            $gradinginfo = grade_get_grades($this->get_course()->id,
-                                        'mod',
-                                        'assign',
-                                        $instance->id,
-                                        $user->id);
-
-            $gradingitem = null;
-            $gradebookgrade = null;
-            if (isset($gradinginfo->items[0])) {
-                $gradingitem = $gradinginfo->items[0];
-                $gradebookgrade = $gradingitem->grades[$user->id];
-            }
-
-            // Check to see if all feedback plugins are empty.
-            $emptyplugins = true;
-            if ($grade) {
-                foreach ($this->get_feedback_plugins() as $plugin) {
-                    if ($plugin->is_visible() && $plugin->is_enabled()) {
-                        if (!$plugin->is_empty($grade)) {
-                            $emptyplugins = false;
-                        }
+        require_once($CFG->libdir.'/gradelib.php');
+        require_once($CFG->dirroot.'/grade/grading/lib.php');
+
+        $instance = $this->get_instance();
+        $grade = $this->get_user_grade($user->id, false);
+        $gradingstatus = $this->get_grading_status($user->id);
+
+        $gradinginfo = grade_get_grades($this->get_course()->id,
+                                    'mod',
+                                    'assign',
+                                    $instance->id,
+                                    $user->id);
+
+        $gradingitem = null;
+        $gradebookgrade = null;
+        if (isset($gradinginfo->items[0])) {
+            $gradingitem = $gradinginfo->items[0];
+            $gradebookgrade = $gradingitem->grades[$user->id];
+        }
+
+        // Check to see if all feedback plugins are empty.
+        $emptyplugins = true;
+        if ($grade) {
+            foreach ($this->get_feedback_plugins() as $plugin) {
+                if ($plugin->is_visible() && $plugin->is_enabled()) {
+                    if (!$plugin->is_empty($grade)) {
+                        $emptyplugins = false;
                     }
                 }
             }
+        }
+
+        if ($this->get_instance()->markingworkflow && $gradingstatus != ASSIGN_MARKING_WORKFLOW_STATE_RELEASED) {
+            $emptyplugins = true; // Don't show feedback plugins until released either.
+        }
 
-            if ($this->get_instance()->markingworkflow && $gradingstatus != ASSIGN_MARKING_WORKFLOW_STATE_RELEASED) {
-                $emptyplugins = true; // Don't show feedback plugins until released either.
+        $cangrade = has_capability('mod/assign:grade', $this->get_context());
+        // If there is a visible grade, show the summary.
+        if ((!is_null($gradebookgrade->grade) || !$emptyplugins)
+                && ($cangrade || !$gradebookgrade->hidden)) {
+
+            $gradefordisplay = null;
+            $gradeddate = null;
+            $grader = null;
+            $gradingmanager = get_grading_manager($this->get_context(), 'mod_assign', 'submissions');
+
+            // Only show the grade if it is not hidden in gradebook.
+            if (!is_null($gradebookgrade->grade) && ($cangrade || !$gradebookgrade->hidden)) {
+                if ($controller = $gradingmanager->get_active_controller()) {
+                    $menu = make_grades_menu($this->get_instance()->grade);
+                    $controller->set_grade_range($menu, $this->get_instance()->grade > 0);
+                    $gradefordisplay = $controller->render_grade($PAGE,
+                                                                 $grade->id,
+                                                                 $gradingitem,
+                                                                 $gradebookgrade->str_long_grade,
+                                                                 $cangrade);
+                } else {
+                    $gradefordisplay = $this->display_grade($gradebookgrade->grade, false);
+                }
+                $gradeddate = $gradebookgrade->dategraded;
+                if (isset($grade->grader)) {
+                    $grader = $DB->get_record('user', array('id' => $grade->grader));
+                }
             }
 
-            $cangrade = has_capability('mod/assign:grade', $this->get_context());
-            // If there is a visible grade, show the summary.
-            if ((!is_null($gradebookgrade->grade) || !$emptyplugins)
-                    && ($cangrade || !$gradebookgrade->hidden)) {
+            $feedbackstatus = new assign_feedback_status($gradefordisplay,
+                                                  $gradeddate,
+                                                  $grader,
+                                                  $this->get_feedback_plugins(),
+                                                  $grade,
+                                                  $this->get_course_module()->id,
+                                                  $this->get_return_action(),
+                                                  $this->get_return_params());
+            return $feedbackstatus;
+        }
+        return;
+    }
+
+    /**
+     * Creates an assign_attempt_history renderable.
+     *
+     * @param stdClass $user the user to get the report for
+     * @return assign_attempt_history renderable object
+     */
+    public function get_assign_attempt_history_renderable($user) {
 
-                $gradefordisplay = null;
-                $gradeddate = null;
-                $grader = null;
-                $gradingmanager = get_grading_manager($this->get_context(), 'mod_assign', 'submissions');
+        $allsubmissions = $this->get_all_submissions($user->id);
+        $allgrades = $this->get_all_grades($user->id);
 
-                // Only show the grade if it is not hidden in gradebook.
-                if (!is_null($gradebookgrade->grade) && ($cangrade || !$gradebookgrade->hidden)) {
-                    if ($controller = $gradingmanager->get_active_controller()) {
-                        $menu = make_grades_menu($this->get_instance()->grade);
-                        $controller->set_grade_range($menu, $this->get_instance()->grade > 0);
-                        $gradefordisplay = $controller->render_grade($PAGE,
-                                                                     $grade->id,
-                                                                     $gradingitem,
-                                                                     $gradebookgrade->str_long_grade,
-                                                                     $cangrade);
-                    } else {
-                        $gradefordisplay = $this->display_grade($gradebookgrade->grade, false);
-                    }
-                    $gradeddate = $gradebookgrade->dategraded;
-                    if (isset($grade->grader)) {
-                        $grader = $DB->get_record('user', array('id'=>$grade->grader));
-                    }
-                }
+        $history = new assign_attempt_history($allsubmissions,
+                                              $allgrades,
+                                              $this->get_submission_plugins(),
+                                              $this->get_feedback_plugins(),
+                                              $this->get_course_module()->id,
+                                              $this->get_return_action(),
+                                              $this->get_return_params(),
+                                              false,
+                                              0,
+                                              0);
+        return $history;
+    }
 
-                $feedbackstatus = new assign_feedback_status($gradefordisplay,
-                                                      $gradeddate,
-                                                      $grader,
-                                                      $this->get_feedback_plugins(),
-                                                      $grade,
-                                                      $this->get_course_module()->id,
-                                                      $this->get_return_action(),
-                                                      $this->get_return_params());
+    /**
+     * Print 2 tables of information with no action links -
+     * the submission summary and the grading summary.
+     *
+     * @param stdClass $user the user to print the report for
+     * @param bool $showlinks - Return plain text or links to the profile
+     * @return string - the html summary
+     */
+    public function view_student_summary($user, $showlinks) {
 
-                $o .= $this->get_renderer()->render($feedbackstatus);
-            }
+        $o = '';
 
-            $allsubmissions = $this->get_all_submissions($user->id);
+        if ($this->can_view_submission($user->id)) {
 
-            if (count($allsubmissions) > 1) {
-                $allgrades = $this->get_all_grades($user->id);
-                $history = new assign_attempt_history($allsubmissions,
-                                                      $allgrades,
-                                                      $this->get_submission_plugins(),
-                                                      $this->get_feedback_plugins(),
-                                                      $this->get_course_module()->id,
-                                                      $this->get_return_action(),
-                                                      $this->get_return_params(),
-                                                      false,
-                                                      0,
-                                                      0);
+            if (has_capability('mod/assign:submit', $this->get_context(), $user)) {
+                $submissionstatus = $this->get_assign_submission_status_renderable($user, $showlinks);
+                $o .= $this->get_renderer()->render($submissionstatus);
+            }
 
-                $o .= $this->get_renderer()->render($history);
+            // If there is a visible grade, show the feedback.
+            $feedbackstatus = $this->get_assign_feedback_status_renderable($user);
+            if ($feedbackstatus) {
+                $o .= $this->get_renderer()->render($feedbackstatus);
             }
 
+            // If there is more than one submission, show the history.
+            $history = $this->get_assign_attempt_history_renderable($user);
+            if (count($history->submissions) > 1) {
+                $o .= $this->get_renderer()->render($history);
+            }
         }
         return $o;
     }
@@ -4327,6 +4376,55 @@ class assign {
         return $submissions;
     }
 
+    /**
+     * Creates an assign_grading_summary renderable.
+     *
+     * @return assign_grading_summary renderable object
+     */
+    public function get_assign_grading_summary_renderable() {
+
+        $instance = $this->get_instance();
+
+        $draft = ASSIGN_SUBMISSION_STATUS_DRAFT;
+        $submitted = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
+
+        $activitygroup = groups_get_activity_group($this->get_course_module());
+
+        if ($instance->teamsubmission) {
+            $defaultteammembers = $this->get_submission_group_members(0, true);
+            $warnofungroupedusers = (count($defaultteammembers) > 0 && $instance->preventsubmissionnotingroup);
+
+            $summary = new assign_grading_summary($this->count_teams($activitygroup),
+                                                  $instance->submissiondrafts,
+                                                  $this->count_submissions_with_status($draft),
+                                                  $this->is_any_submission_plugin_enabled(),
+                                                  $this->count_submissions_with_status($submitted),
+                                                  $instance->cutoffdate,
+                                                  $instance->duedate,
+                                                  $this->get_course_module()->id,
+                                                  $this->count_submissions_need_grading(),
+                                                  $instance->teamsubmission,
+                                                  $warnofungroupedusers);
+        } else {
+            // The active group has already been updated in groups_print_activity_menu().
+            $countparticipants = $this->count_participants($activitygroup);
+            $summary = new assign_grading_summary($countparticipants,
+                                                  $instance->submissiondrafts,
+                                                  $this->count_submissions_with_status($draft),
+                                                  $this->is_any_submission_plugin_enabled(),
+                                                  $this->count_submissions_with_status($submitted),
+                                                  $instance->cutoffdate,
+                                                  $instance->duedate,
+                                                  $this->get_course_module()->id,
+                                                  $this->count_submissions_need_grading(),
+                                                  $instance->teamsubmission,
+                                                  false);
+
+        }
+
+        return $summary;
+    }
+
     /**
      * View submissions page (contains details of current submission).
      *
@@ -4358,47 +4456,12 @@ class assign {
         }
 
         if ($this->can_view_grades()) {
-            $draft = ASSIGN_SUBMISSION_STATUS_DRAFT;
-            $submitted = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
-
             // Group selector will only be displayed if necessary.
             $currenturl = new moodle_url('/mod/assign/view.php', array('id' => $this->get_course_module()->id));
             $o .= groups_print_activity_menu($this->get_course_module(), $currenturl->out(), true);
 
-            $activitygroup = groups_get_activity_group($this->get_course_module());
-
-            if ($instance->teamsubmission) {
-                $defaultteammembers = $this->get_submission_group_members(0, true);
-                $warnofungroupedusers = (count($defaultteammembers) > 0 && $instance->preventsubmissionnotingroup);
-
-                $summary = new assign_grading_summary($this->count_teams($activitygroup),
-                                                      $instance->submissiondrafts,
-                                                      $this->count_submissions_with_status($draft),
-                                                      $this->is_any_submission_plugin_enabled(),
-                                                      $this->count_submissions_with_status($submitted),
-                                                      $instance->cutoffdate,
-                                                      $instance->duedate,
-                                                      $this->get_course_module()->id,
-                                                      $this->count_submissions_need_grading(),
-                                                      $instance->teamsubmission,
-                                                      $warnofungroupedusers);
-                $o .= $this->get_renderer()->render($summary);
-            } else {
-                // The active group has already been updated in groups_print_activity_menu().
-                $countparticipants = $this->count_participants($activitygroup);
-                $summary = new assign_grading_summary($countparticipants,
-                                                      $instance->submissiondrafts,
-                                                      $this->count_submissions_with_status($draft),
-                                                      $this->is_any_submission_plugin_enabled(),
-                                                      $this->count_submissions_with_status($submitted),
-                                                      $instance->cutoffdate,
-                                                      $instance->duedate,
-                                                      $this->get_course_module()->id,
-                                                      $this->count_submissions_need_grading(),
-                                                      $instance->teamsubmission,
-                                                      false);
-                $o .= $this->get_renderer()->render($summary);
-            }
+            $summary = $this->get_assign_grading_summary_renderable();
+            $o .= $this->get_renderer()->render($summary);
         }
         $grade = $this->get_user_grade($USER->id, false);
         $submission = $this->get_user_submission($USER->id, false);
index a6c2c3c..1b92d58 100644 (file)
@@ -365,16 +365,7 @@ class mod_assign_external_testcase extends externallib_advanced_testcase {
         $this->assertEquals(1, count($assignment['submissions']));
         $submission = $assignment['submissions'][0];
         $this->assertEquals($sid, $submission['id']);
-        $this->assertGreaterThanOrEqual(3, count($submission['plugins']));
-        $plugins = $submission['plugins'];
-        foreach ($plugins as $plugin) {
-            $foundonlinetext = false;
-            if ($plugin['type'] == 'onlinetext') {
-                $foundonlinetext = true;
-                break;
-            }
-        }
-        $this->assertTrue($foundonlinetext);
+        $this->assertCount(1, $submission['plugins']);
     }
 
     /**
@@ -1748,4 +1739,274 @@ class mod_assign_external_testcase extends externallib_advanced_testcase {
         }
     }
 
+    /**
+     * Create a submission for testing the get_submission_status function.
+     * @param  boolean $submitforgrading whether to submit for grading the submission
+     * @return array an array containing all the required data for testing
+     */
+    private function create_submission_for_testing_status($submitforgrading = false) {
+        global $DB, $CFG;
+        require_once($CFG->dirroot . '/mod/assign/tests/base_test.php');
+
+        // Create a course and assignment and users.
+        $course = self::getDataGenerator()->create_course();
+
+        $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
+        $params = array(
+            'course' => $course->id,
+            'assignsubmission_file_maxfiles' => 1,
+            'assignsubmission_file_maxsizebytes' => 1024 * 1024,
+            'assignsubmission_onlinetext_enabled' => 1,
+            'assignsubmission_file_enabled' => 1,
+            'submissiondrafts' => 1,
+            'assignfeedback_file_enabled' => 1,
+            'assignfeedback_comments_enabled' => 1,
+            'attemptreopenmethod' => ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL,
+            'sendnotifications' => 0
+        );
+
+        set_config('submissionreceipts', 0, 'assign');
+
+        $instance = $generator->create_instance($params);
+        $cm = get_coursemodule_from_instance('assign', $instance->id);
+        $context = context_module::instance($cm->id);
+
+        $assign = new testable_assign($context, $cm, $course);
+
+        $student1 = self::getDataGenerator()->create_user();
+        $student2 = self::getDataGenerator()->create_user();
+        $studentrole = $DB->get_record('role', array('shortname' => 'student'));
+        $this->getDataGenerator()->enrol_user($student1->id, $course->id, $studentrole->id);
+        $this->getDataGenerator()->enrol_user($student2->id, $course->id, $studentrole->id);
+        $teacher = self::getDataGenerator()->create_user();
+        $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
+        $this->getDataGenerator()->enrol_user($teacher->id, $course->id, $teacherrole->id);
+
+        $this->setUser($student1);
+
+        // Create a student1 with an online text submission.
+        // Simulate a submission.
+        $submission = $assign->get_user_submission($student1->id, true);
+
+        $data = new stdClass();
+        $data->onlinetext_editor = array('itemid' => file_get_unused_draft_itemid(),
+                                         'text' => 'Submission text',
+                                         'format' => FORMAT_MOODLE);
+
+        $draftidfile = file_get_unused_draft_itemid();
+        $usercontext = context_user::instance($student1->id);
+        $filerecord = array(
+            'contextid' => $usercontext->id,
+            'component' => 'user',
+            'filearea'  => 'draft',
+            'itemid'    => $draftidfile,
+            'filepath'  => '/',
+            'filename'  => 't.txt',
+        );
+        $fs = get_file_storage();
+        $fs->create_file_from_string($filerecord, 'text contents');
+
+        $data->files_filemanager = $draftidfile;
+
+        $notices = array();
+        $assign->save_submission($data, $notices);
+
+        if ($submitforgrading) {
+            // Now, submit the draft for grading.
+            $notices = array();
+
+            $data = new stdClass;
+            $data->userid = $student1->id;
+            $assign->submit_for_grading($data, $notices);
+        }
+
+        return array($assign, $instance, $student1, $student2, $teacher);
+    }
+
+    /**
+     * Test get_submission_status for a draft submission.
+     */
+    public function test_get_submission_status_in_draft_status() {
+        $this->resetAfterTest(true);
+
+        list($assign, $instance, $student1, $student2, $teacher) = $this->create_submission_for_testing_status();
+
+        $result = mod_assign_external::get_submission_status($assign->get_instance()->id);
+        // We expect debugging because of the $PAGE object, this won't happen in a normal WS request.
+        $this->assertDebuggingCalled();
+
+        $result = external_api::clean_returnvalue(mod_assign_external::get_submission_status_returns(), $result);
+
+        // The submission is now in draft mode.
+        $this->assertCount(0, $result['warnings']);
+        $this->assertFalse(isset($result['gradingsummary']));
+        $this->assertFalse(isset($result['feedback']));
+        $this->assertFalse(isset($result['previousattempts']));
+
+        $this->assertTrue($result['lastattempt']['submissionsenabled']);
+        $this->assertTrue($result['lastattempt']['canedit']);
+        $this->assertTrue($result['lastattempt']['cansubmit']);
+        $this->assertFalse($result['lastattempt']['locked']);
+        $this->assertFalse($result['lastattempt']['graded']);
+        $this->assertEmpty($result['lastattempt']['extensionduedate']);
+        $this->assertFalse($result['lastattempt']['blindmarking']);
+        $this->assertCount(0, $result['lastattempt']['submissiongroupmemberswhoneedtosubmit']);
+        $this->assertEquals('notgraded', $result['lastattempt']['gradingstatus']);
+
+        $this->assertEquals($student1->id, $result['lastattempt']['submission']['userid']);
+        $this->assertEquals(0, $result['lastattempt']['submission']['attemptnumber']);
+        $this->assertEquals('draft', $result['lastattempt']['submission']['status']);
+        $this->assertEquals(0, $result['lastattempt']['submission']['groupid']);
+        $this->assertEquals($assign->get_instance()->id, $result['lastattempt']['submission']['assignment']);
+        $this->assertEquals(1, $result['lastattempt']['submission']['latest']);
+        $this->assertEquals('Submission text', $result['lastattempt']['submission']['plugins'][0]['editorfields'][0]['text']);
+        $this->assertEquals('/t.txt', $result['lastattempt']['submission']['plugins'][1]['fileareas'][0]['files'][0]['filepath']);
+    }
+
+    /**
+     * Test get_submission_status for a submitted submission.
+     */
+    public function test_get_submission_status_in_submission_status() {
+        $this->resetAfterTest(true);
+
+        list($assign, $instance, $student1, $student2, $teacher) = $this->create_submission_for_testing_status(true);
+
+        $result = mod_assign_external::get_submission_status($assign->get_instance()->id);
+        // We expect debugging because of the $PAGE object, this won't happen in a normal WS request.
+        $this->assertDebuggingCalled();
+        $result = external_api::clean_returnvalue(mod_assign_external::get_submission_status_returns(), $result);
+
+        $this->assertCount(0, $result['warnings']);
+        $this->assertFalse(isset($result['gradingsummary']));
+        $this->assertFalse(isset($result['feedback']));
+        $this->assertFalse(isset($result['previousattempts']));
+
+        $this->assertTrue($result['lastattempt']['submissionsenabled']);
+        $this->assertFalse($result['lastattempt']['canedit']);
+        $this->assertFalse($result['lastattempt']['cansubmit']);
+        $this->assertFalse($result['lastattempt']['locked']);
+        $this->assertFalse($result['lastattempt']['graded']);
+        $this->assertEmpty($result['lastattempt']['extensionduedate']);
+        $this->assertFalse($result['lastattempt']['blindmarking']);
+        $this->assertCount(0, $result['lastattempt']['submissiongroupmemberswhoneedtosubmit']);
+        $this->assertEquals('notgraded', $result['lastattempt']['gradingstatus']);
+
+    }
+
+    /**
+     * Test get_submission_status using the teacher role.
+     */
+    public function test_get_submission_status_in_submission_status_for_teacher() {
+        $this->resetAfterTest(true);
+
+        list($assign, $instance, $student1, $student2, $teacher) = $this->create_submission_for_testing_status(true);
+
+        // Now, as teacher, see the grading summary.
+        $this->setUser($teacher);
+        $result = mod_assign_external::get_submission_status($assign->get_instance()->id);
+        // We expect debugging because of the $PAGE object, this won't happen in a normal WS request.
+        $this->assertDebuggingCalled();
+        $result = external_api::clean_returnvalue(mod_assign_external::get_submission_status_returns(), $result);
+
+        $this->assertCount(0, $result['warnings']);
+        $this->assertFalse(isset($result['lastattempt']));
+        $this->assertFalse(isset($result['feedback']));
+        $this->assertFalse(isset($result['previousattempts']));
+
+        $this->assertEquals(2, $result['gradingsummary']['participantcount']);
+        $this->assertEquals(0, $result['gradingsummary']['submissiondraftscount']);
+        $this->assertEquals(1, $result['gradingsummary']['submissionsenabled']);
+        $this->assertEquals(1, $result['gradingsummary']['submissionssubmittedcount']);
+        $this->assertEquals(1, $result['gradingsummary']['submissionsneedgradingcount']);
+        $this->assertFalse($result['gradingsummary']['warnofungroupedusers']);
+    }
+
+    /**
+     * Test get_submission_status for a reopened submission.
+     */
+    public function test_get_submission_status_in_reopened_status() {
+        global $USER;
+
+        $this->resetAfterTest(true);
+
+        list($assign, $instance, $student1, $student2, $teacher) = $this->create_submission_for_testing_status(true);
+
+        $this->setUser($teacher);
+        // Grade and reopen.
+        $feedbackpluginparams = array();
+        $feedbackpluginparams['files_filemanager'] = file_get_unused_draft_itemid();
+        $feedbackeditorparams = array('text' => 'Yeeha!',
+                                        'format' => 1);
+        $feedbackpluginparams['assignfeedbackcomments_editor'] = $feedbackeditorparams;
+        $result = mod_assign_external::save_grade($instance->id,
+                                                  $student1->id,
+                                                  50.0,
+                                                  -1,
+                                                  false,
+                                                  'released',
+                                                  false,
+                                                  $feedbackpluginparams);
+        $USER->ignoresesskey = true;
+        $assign->testable_process_add_attempt($student1->id);
+
+        $this->setUser($student1);
+
+        $result = mod_assign_external::get_submission_status($assign->get_instance()->id);
+        // We expect debugging because of the $PAGE object, this won't happen in a normal WS request.
+        $this->assertDebuggingCalled();
+        $result = external_api::clean_returnvalue(mod_assign_external::get_submission_status_returns(), $result);
+
+        $this->assertCount(0, $result['warnings']);
+        $this->assertFalse(isset($result['gradingsummary']));
+
+        $this->assertTrue($result['lastattempt']['submissionsenabled']);
+        $this->assertTrue($result['lastattempt']['canedit']);
+        $this->assertFalse($result['lastattempt']['cansubmit']);
+        $this->assertFalse($result['lastattempt']['locked']);
+        $this->assertFalse($result['lastattempt']['graded']);
+        $this->assertEmpty($result['lastattempt']['extensionduedate']);
+        $this->assertFalse($result['lastattempt']['blindmarking']);
+        $this->assertCount(0, $result['lastattempt']['submissiongroupmemberswhoneedtosubmit']);
+        $this->assertEquals('notgraded', $result['lastattempt']['gradingstatus']);
+
+        // Check new attempt reopened.
+        $this->assertEquals($student1->id, $result['lastattempt']['submission']['userid']);
+        $this->assertEquals(1, $result['lastattempt']['submission']['attemptnumber']);
+        $this->assertEquals('reopened', $result['lastattempt']['submission']['status']);
+        $this->assertEquals(0, $result['lastattempt']['submission']['groupid']);
+        $this->assertEquals($assign->get_instance()->id, $result['lastattempt']['submission']['assignment']);
+        $this->assertEquals(1, $result['lastattempt']['submission']['latest']);
+        $this->assertCount(3, $result['lastattempt']['submission']['plugins']);
+
+        // Now see feedback and the attempts history (remember, is a submission reopened).
+        // Only 2 fields (no grade, no plugins data).
+        $this->assertCount(2, $result['feedback']);
+
+        // One previous attempt.
+        $this->assertCount(1, $result['previousattempts']);
+        $this->assertEquals(0, $result['previousattempts'][0]['attemptnumber']);
+        $this->assertEquals(50, $result['previousattempts'][0]['grade']['grade']);
+        $this->assertEquals($teacher->id, $result['previousattempts'][0]['grade']['grader']);
+        $this->assertEquals($student1->id, $result['previousattempts'][0]['grade']['userid']);
+        $this->assertEquals('Yeeha!', $result['previousattempts'][0]['feedbackplugins'][0]['editorfields'][0]['text']);
+        $submissionplugins = $result['previousattempts'][0]['submission']['plugins'];
+        $this->assertEquals('Submission text', $submissionplugins[0]['editorfields'][0]['text']);
+        $this->assertEquals('/t.txt', $submissionplugins[1]['fileareas'][0]['files'][0]['filepath']);
+    }
+
+    /**
+     * Test access control for get_submission_status.
+     */
+    public function test_get_submission_status_access_control() {
+        $this->resetAfterTest(true);
+
+        list($assign, $instance, $student1, $student2, $teacher) = $this->create_submission_for_testing_status();
+
+        $this->setUser($student2);
+
+        // Access control test.
+        $this->setExpectedException('required_capability_exception');
+        mod_assign_external::get_submission_status($assign->get_instance()->id, $student1->id);
+
+    }
 }
index 89dd620..2b2ee2b 100644 (file)
@@ -29,7 +29,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$version  = 2016031700.00;              // YYYYMMDD      = weekly release date of this DEV branch.
+$version  = 2016031700.01;              // YYYYMMDD      = weekly release date of this DEV branch.
                                         //         RR    = release increments - 00 in DEV branches.
                                         //           .XX = incremental changes.