diff --git a/.github/workflows/moodle-plugin-ci.yml b/.github/workflows/moodle-plugin-ci.yml new file mode 100644 index 0000000..b736c7f --- /dev/null +++ b/.github/workflows/moodle-plugin-ci.yml @@ -0,0 +1,129 @@ +name: Moodle Plugin CI + +on: + push: + paths-ignore: + - 'node_modules/**' + pull_request: + paths-ignore: + - 'node_modules/**' + + +jobs: + test: + runs-on: ubuntu-latest + + services: + postgres: + image: postgres:12 + env: + POSTGRES_USER: 'postgres' + POSTGRES_HOST_AUTH_METHOD: 'trust' + ports: + - 5432:5432 + options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 3 + mariadb: + image: mariadb:10 + env: + MYSQL_USER: 'root' + MYSQL_ALLOW_EMPTY_PASSWORD: "true" + MYSQL_CHARACTER_SET_SERVER: "utf8mb4" + MYSQL_COLLATION_SERVER: "utf8mb4_unicode_ci" + + ports: + - 3306:3306 + options: --health-cmd="mysqladmin ping" --health-interval 10s --health-timeout 5s --health-retries 3 + + strategy: + fail-fast: false + matrix: + include: + - php: '8.0' + moodle-branch: 'master' + database: 'pgsql' + - php: '8.0' + moodle-branch: 'MOODLE_400_STABLE' + database: 'mariadb' + - php: '7.4' + moodle-branch: 'MOODLE_311_STABLE' + database: 'pgsql' + - php: '7.4' + moodle-branch: 'MOODLE_39_STABLE' + database: 'mariadb' + + steps: + - name: Check out repository code + uses: actions/checkout@v2 + with: + path: plugin + + - name: Setup PHP ${{ matrix.php }} + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: ${{ matrix.extensions }} + ini-values: max_input_vars=5000 + # none to use phpdbg fallback. Specify pcov (Moodle 3.10 and up) or xdebug to use them instead. + coverage: none + + - name: Initialise moodle-plugin-ci + run: | + composer create-project -n --no-dev --prefer-dist moodlehq/moodle-plugin-ci ci ^3 + echo $(cd ci/bin; pwd) >> $GITHUB_PATH + echo $(cd ci/vendor/bin; pwd) >> $GITHUB_PATH + sudo locale-gen en_AU.UTF-8 + echo "NVM_DIR=$HOME/.nvm" >> $GITHUB_ENV + - name: Install moodle-plugin-ci + run: | + moodle-plugin-ci install --plugin ./plugin --db-host=127.0.0.1 + env: + DB: ${{ matrix.database }} + MOODLE_BRANCH: ${{ matrix.moodle-branch }} + + - name: PHP Lint + if: ${{ always() }} + run: moodle-plugin-ci phplint + + - name: PHP Copy/Paste Detector + continue-on-error: true # This step will show errors but will not fail + if: ${{ always() }} + run: moodle-plugin-ci phpcpd + + - name: PHP Mess Detector + continue-on-error: true # This step will show errors but will not fail + if: ${{ always() }} + run: moodle-plugin-ci phpmd + + - name: Moodle Code Checker + if: ${{ always() }} + # Allow 3 warnings for privacy provider interfaces (Moodle <3.6) + run: moodle-plugin-ci codechecker --max-warnings 3 + + - name: Moodle PHPDoc Checker + if: ${{ always() }} + run: moodle-plugin-ci phpdoc + + - name: Validating + if: ${{ always() }} + run: moodle-plugin-ci validate + + - name: Check upgrade savepoints + if: ${{ always() }} + run: moodle-plugin-ci savepoints + +# Mustache and Grunt are failing so commenting out for now +# - name: Mustache Lint +# if: ${{ always() }} +# run: moodle-plugin-ci mustache + +# - name: Grunt +# if: ${{ always() }} +# run: moodle-plugin-ci grunt --max-lint-warnings 0 + + - name: PHPUnit tests + if: ${{ always() }} + run: moodle-plugin-ci phpunit --fail-on-warning + + - name: Behat features + if: ${{ always() }} + run: moodle-plugin-ci behat --profile chrome \ No newline at end of file diff --git a/backup/moodle2/backup_panoptosubmission_stepslib.php b/backup/moodle2/backup_panoptosubmission_stepslib.php index 8fdcb2c..8de69c3 100644 --- a/backup/moodle2/backup_panoptosubmission_stepslib.php +++ b/backup/moodle2/backup_panoptosubmission_stepslib.php @@ -47,6 +47,7 @@ protected function define_structure() { 'introformat', 'timeavailable', 'timedue', + 'cutofftime', 'preventlate', 'resubmit', 'emailteachers', @@ -64,6 +65,9 @@ protected function define_structure() { 'source', 'width', 'height', + 'thumbnailsource', + 'thumbnailwidth', + 'thumbnailheight', 'grade', 'submissioncomment', 'format', diff --git a/classes/privacy/provider.php b/classes/privacy/provider.php index 03e8f3d..8c58e05 100644 --- a/classes/privacy/provider.php +++ b/classes/privacy/provider.php @@ -45,6 +45,16 @@ class provider implements \core_privacy\local\metadata\provider, */ public static function get_metadata(collection $collection) : collection { + $collection->add_external_location_link( + 'panoptosubmission_submission', + [ + 'userid' => 'privacy:metadata:panoptosubmission_submission:userid', + 'username' => 'privacy:metadata:panoptosubmission_submission:username', + 'email' => 'privacy:metadata:panoptosubmission_submission:email' + ], + 'privacy:metadata:panoptosubmission_submission' + ); + $collection->add_subsystem_link('core_message', [], 'privacy:metadata:emailteachersexplanation'); $collection->add_database_table( @@ -64,7 +74,8 @@ public static function get_metadata(collection $collection) : collection { ); $collection->add_user_preference('panoptosubmission_filter', - 'privacy:metadata:panoptosubmissionfilter'); + 'privacy:metadata:panoptosubmissionfilter' + ); $collection->add_user_preference('panoptosubmission_group_filter', 'privacy:metadata:panoptosubmissiongroupfilter' ); @@ -399,6 +410,7 @@ protected static function get_panoptosubmission_by_context($context) { "a.grade, " . "a.timedue, " . "a.timeavailable, " . + "a.cutofftime, " . "a.timemodified " . "FROM {panoptosubmission} a " . "JOIN {course_modules} cm ON a.id = cm.instance " . @@ -431,6 +443,10 @@ protected static function get_panoptosubmission_output($panoptosubmissiondata) { $panoptosubmission->timedue = transform::datetime($panoptosubmissiondata->timedue); } + if ($panoptosubmissiondata->cutofftime != 0) { + $panoptosubmission->cutofftime = transform::datetime($panoptosubmissiondata->cutofftime); + } + return $panoptosubmission; } diff --git a/contentitem.php b/contentitem.php index 7e67753..bb819b5 100644 --- a/contentitem.php +++ b/contentitem.php @@ -26,7 +26,7 @@ require_once(dirname(dirname(dirname(__FILE__))) . '/mod/lti/lib.php'); require_once(dirname(dirname(dirname(__FILE__))) . '/mod/lti/locallib.php'); require_once(dirname(__FILE__) . '/locallib.php'); -require_once(dirname(__FILE__) . '/lib/panoptosubmission_lti_utility.php'); +require_once($CFG->dirroot . '/blocks/panopto/lib/lti/panoptoblock_lti_utility.php'); $courseid = required_param('courseid', PARAM_INT); @@ -34,7 +34,7 @@ $course = get_course($courseid); require_login($course); -$toolid = \panoptosubmission_lti_utility::get_course_tool_id($courseid); +$toolid = \panoptoblock_lti_utility::get_course_tool_id($courseid, 'panopto_student_submission_tool'); // If no lti tool exists then we can not continue. if (is_null($toolid)) { @@ -42,6 +42,17 @@ return; } +// LTI 1.3 login request. +$config = lti_get_type_type_config($toolid); +if ($config->lti_ltiversion === LTI_VERSION_1P3) { + if (!isset($SESSION->lti_initiatelogin_status)) { + echo lti_initiate_login($courseid, "mod_panoptosubmission", null, $config); + exit; + } else { + unset($SESSION->lti_initiatelogin_status); + } +} + // Set the return URL. We send the launch container along to help us avoid // frames-within-frames when the user returns. $returnurlparams = [ diff --git a/contentitem_return.php b/contentitem_return.php index e55e4d0..18b694f 100644 --- a/contentitem_return.php +++ b/contentitem_return.php @@ -24,17 +24,41 @@ */ require_once(dirname(dirname(dirname(__FILE__))) . '/config.php'); -require_once(dirname(__FILE__) . '/lib/panoptosubmission_lti_utility.php'); - +require_once($CFG->dirroot . '/blocks/panopto/lib/lti/panoptoblock_lti_utility.php'); +require_once($CFG->dirroot . '/blocks/panopto/lib/panopto_data.php'); +require_once(dirname(dirname(dirname(__FILE__))) . '/mod/lti/lib.php'); +require_once(dirname(dirname(dirname(__FILE__))) . '/mod/lti/locallib.php'); $courseid = required_param('course', PARAM_INT); -$contentitemsraw = required_param('content_items', PARAM_RAW_TRIMMED); +$id = required_param('id', PARAM_INT); +$jwt = optional_param('JWT', '', PARAM_RAW); require_login($courseid); $context = context_course::instance($courseid); -$contentitems = json_decode($contentitemsraw); +$config = lti_get_type_type_config($id); +$islti1p3 = $config->lti_ltiversion === LTI_VERSION_1P3; +$items = ''; + +if (!empty($jwt)) { + $params = lti_convert_from_jwt($id, $jwt); + $consumerkey = $params['oauth_consumer_key'] ?? ''; + $messagetype = $params['lti_message_type'] ?? ''; + $version = $params['lti_version'] ?? ''; + $items = $params['content_items'] ?? ''; + $errormsg = $params['lti_errormsg'] ?? ''; + $msg = $params['lti_msg'] ?? ''; +} else { + $consumerkey = required_param('oauth_consumer_key', PARAM_RAW); + $messagetype = required_param('lti_message_type', PARAM_TEXT); + $version = required_param('lti_version', PARAM_TEXT); + $items = optional_param('content_items', '', PARAM_RAW_TRIMMED); + $errormsg = optional_param('lti_errormsg', '', PARAM_TEXT); + $msg = optional_param('lti_msg', '', PARAM_TEXT); +} + +$contentitems = json_decode($items); $errors = []; @@ -43,23 +67,55 @@ $errors[] = 'invalidjson'; } +// Get and validate frame and thumbnail sizes. $framewidth = 720; -if (!empty($contentitems->{'@graph'}[0]->placementAdvice->displayWidth)) { - $framewidth = $contentitems->{'@graph'}[0]->placementAdvice->displayWidth; +$fwidth = $contentitems->{'@graph'}[0]->placementAdvice->displayWidth; +if (!empty($fwidth)) { + $framewidth = is_numeric($fwidth) ? $fwidth : $framewidth; } + $frameheight = 480; -if (!empty($contentitems->{'@graph'}[0]->placementAdvice->displayHeight)) { - $frameheight = $contentitems->{'@graph'}[0]->placementAdvice->displayHeight; +$fheight = $contentitems->{'@graph'}[0]->placementAdvice->displayHeight; +if (!empty($fheight)) { + $frameheight = is_numeric($fheight) ? $fheight : $frameheight; } $thumbnailwidth = 128; -if (!empty($contentitems->{'@graph'}[0]->thumbnail->width)) { - $thumbnailwidth = $contentitems->{'@graph'}[0]->thumbnail->width; +$twidth = $contentitems->{'@graph'}[0]->thumbnail->width; +if (!empty($twidth)) { + $thumbnailwidth = is_numeric($twidth) ? $twidth : $thumbnailwidth; } $thumbnailheight = 72; -if (!empty($contentitems->{'@graph'}[0]->thumbnail->height)) { - $thumbnailheight = $contentitems->{'@graph'}[0]->thumbnail->height; +$theight = $contentitems->{'@graph'}[0]->thumbnail->height; +if (!empty($theight)) { + $thumbnailheight = is_numeric($theight) ? $theight : $thumbnailheight; +} + +$title = ""; +$itemtitle = $contentitems->{'@graph'}[0]->title; +if (!empty($itemtitle)) { + $invalidcharacters = array("$", "%", "#", "<", ">"); + $cleantitle = str_replace($invalidcharacters, "", $itemtitle); + $title = is_string($cleantitle) ? $cleantitle : $title; +} + +$url = ""; +$contenturl = $contentitems->{'@graph'}[0]->url; +if (!empty($contenturl)) { + $panoptodata = new \panopto_data($courseid); + $baseurl = parse_url($contenturl, PHP_URL_HOST); + if (strcmp($panoptodata->servername, $baseurl) === 0) { + $url = $contenturl; + } +} + +$thumbnailurl = ""; +$thumbnailurlfinal = !empty($contentitems->{'@graph'}[0]->thumbnail->id) + ? $contentitems->{'@graph'}[0]->thumbnail->id + : $contentitems->{'@graph'}[0]->thumbnail->{'@id'}; +if (!empty($thumbnailurlfinal)) { + $thumbnailurl = is_string($thumbnailurlfinal) ? $thumbnailurlfinal : $thumbnailurl; } $customdata = $contentitems->{'@graph'}[0]->custom; @@ -78,22 +134,21 @@ var sessionSelectedEvent; var detailObject = { 'detail': { - 'title': "{'@graph'}[0]->title ?>", + 'title': "", 'ltiViewerUrl': "out(false) ?>", - 'contentUrl': "{'@graph'}[0]->url ?>", + 'contentUrl': "", 'customData': "", 'width': , 'height': , - 'thumbnailUrl': "{'@graph'}[0]->thumbnail->id ?>", + 'thumbnailUrl': "", 'thumbnailWidth': , 'thumbnailHeight': , } }; - if(typeof window.CustomEvent === 'function') { + if (typeof window.CustomEvent === 'function') { sessionSelectedEvent = new CustomEvent('sessionSelected', detailObject); - } - else { + } else { // ie >= 9 sessionSelectedEvent = document.createEvent('CustomEvent'); sessionSelectedEvent.initCustomEvent('sessionSelected', false, false, detailObject); diff --git a/db/install.xml b/db/install.xml index 2aa1903..ba0c27a 100644 --- a/db/install.xml +++ b/db/install.xml @@ -1,24 +1,22 @@ - - + - +
- - - - - - - - - - - - - + + + + + + + + + + + + + + @@ -27,40 +25,36 @@
- +
- - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - - - - + + + +
diff --git a/db/upgrade.php b/db/upgrade.php index 1a5165f..57a6e84 100644 --- a/db/upgrade.php +++ b/db/upgrade.php @@ -28,5 +28,40 @@ * @return whether the upgrade was a success */ function xmldb_panoptosubmission_upgrade($oldversion) { + global $DB; + $dbman = $DB->get_manager(); + + if ($oldversion < 2022070704) { + // Define field cutofftime in the panoptosubmission table. + $table = new xmldb_table('panoptosubmission'); + $field = new xmldb_field('cutofftime', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, 0, 'timemodified'); + + // Conditionally launch add field creator_mapping. + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + + // Panopto savepoint reached. + upgrade_mod_savepoint(true, 2022070704, 'panoptosubmission'); + } + + if ($oldversion < 2022090744) { + // Changing type of field grade on table panoptosubmission_submission from int to number. + $table = new xmldb_table('panoptosubmission_submission'); + $field = new xmldb_field('grade', XMLDB_TYPE_NUMBER, '11,2', null, XMLDB_NOTNULL, null, 0, 'thumbnailheight'); + + // Launch change of type for field grade. + $dbman->change_field_type($table, $field); + + // Changing type of field grade on table panoptosubmission from int to number. + $table = new xmldb_table('panoptosubmission'); + $field = new xmldb_field('grade', XMLDB_TYPE_NUMBER, '10,2', null, XMLDB_NOTNULL, null, 0, 'emailteachers'); + + // Launch change of type for field grade. + $dbman->change_field_type($table, $field); + + // Panopto savepoint reached. + upgrade_mod_savepoint(true, 2022090744, 'panoptosubmission'); + } return true; } diff --git a/grade_submissions.php b/grade_submissions.php index 770ecac..3d6c297 100644 --- a/grade_submissions.php +++ b/grade_submissions.php @@ -38,9 +38,7 @@ $url->param('cmid', $id); if (!empty($mode)) { - if (!confirm_sesskey()) { - throw new moodle_exception('confirmsesskeybad', 'error'); - } + require_sesskey(); } list($cm, $course, $pansubmissionactivity) = panoptosubmission_validate_cmid($id); @@ -100,7 +98,6 @@ if ($data->perpage > 0) { set_user_preference('panoptosubmission_perpage', $data->perpage); } - } if (empty($data)) { @@ -227,7 +224,7 @@ } $renderer->display_submissions_table( - $cm, $data->group_filter, $data->filter, $data->perpage, $data->quickgrade, $tifirst, $tilast, $page + $cm, $data->perpage, $data->group_filter, $data->filter, $data->quickgrade, $tifirst, $tilast, $page ); $prefform->set_data($data); diff --git a/lang/en/panoptosubmission.php b/lang/en/panoptosubmission.php index b82b207..c76315c 100644 --- a/lang/en/panoptosubmission.php +++ b/lang/en/panoptosubmission.php @@ -28,8 +28,13 @@ $string['name'] = 'Name'; $string['availabledate'] = 'Allow submissions from'; $string['availabledate_help'] = 'If enabled, students will not be able to submit before this date. If disabled, students will be able to start submitting right away.'; +$string['cutoffdate'] = 'Cut-off date'; +$string['cutoffdate_help'] = 'If set, the assignment will not accept submissions after this date.'; +$string['cutoffdatevalidation'] = 'Cut-off date cannot be earlier than the due date.'; +$string['cutoffdatefromdatevalidation'] = 'Cut-off date cannot be earlier than the allow submissions from date.'; $string['duedate'] = 'Due Date'; $string['duedate_help'] = 'This is when the assignment is due. Submissions will still be allowed after this date, but any assignments submitted after this date will be marked as late. Set an assignment cut-off date to prevent submissions after a certain date.'; +$string['duedatevalidation'] = 'Due date cannot be earlier than the allow submissions from date.'; $string['preventlate'] = 'Prevent late submissions'; $string['preventlate_help'] = 'If enabled, this will prevent students from submitting the assignment after the due date.'; $string['allowdeleting'] = 'Allow resubmitting'; @@ -44,12 +49,14 @@ $string['replacevideo'] = 'Replace'; $string['gradesubmission'] = 'Grade'; $string['numberofsubmissions'] = 'Number of submissions: {$a}'; -$string['assignmentexpired'] = 'Submission cancelled. The assignment due date has passed'; +$string['assignmentexpired'] = 'Submission cancelled. The assignment cut off date has passed.'; +$string['assignmentpastdue'] = 'Submission cancelled. The assignment due date has passed'; $string['notallowedtoreplacemedia'] = 'You are not allowed to replace the media.'; $string['assignmentsubmitted'] = 'Success, your assignment has been submitted'; $string['deleteallsubmissions'] = 'Delete all video submissions'; $string['fullname'] = 'Name'; -$string['grade'] = 'Grade'; +$string['gradeverb'] = 'Grade'; +$string['gradenoun'] = 'Grade'; $string['submissioncomment'] = 'Comment'; $string['timemodified'] = 'Last modified (Submission)'; $string['grademodified'] = 'Last modified (Grade)'; @@ -120,8 +127,11 @@ $string['grade_out_of'] = 'Grade out of {$a}: '; $string['quickgrade_help'] = 'If enabled, multiple assignments can be graded at the same time. Update grades and feedback and then click "Save all feedback".'; $string['no_existing_lti_tools'] = 'A preconfigured Panopto LTI tool with the custom parameter "panopto_student_submission_tool" must exist to be able to use the Panopto Student Submission activity. Please see setup documentation for more information.'; +$string['no_automatic_operation_target_server'] = 'Please set Automatic Operation Target Server in the settings, so course can be provisioned.'; $string['privacy:metadata:emailteachersexplanation'] = 'Messages are sent to teachers through the messaging system.'; $string['privacy:metadata:panoptosubmission_submission'] = 'Panopto Student Submission submissions'; +$string['privacy:metadata:panoptosubmission_submission:email'] = 'Your email is sent to Panopto to allow use of Panopto\'s email features.'; +$string['privacy:metadata:panoptosubmission_submission:username'] = 'Your username is sent to Panopto to allow use of LTI features.'; $string['privacy:metadata:panoptosubmission_submission:userid'] = 'Moodle user id'; $string['privacy:metadata:panoptosubmission_submission:source'] = 'The LTI link that opens the submitted content'; $string['privacy:metadata:panoptosubmission_submission:grade'] = 'Grade score for the submission'; diff --git a/lib.php b/lib.php index 051fe8a..ca85da3 100644 --- a/lib.php +++ b/lib.php @@ -247,7 +247,7 @@ function panoptosubmission_scale_used_anywhere($scaleid) { global $DB; $param = array('grade' => -$scaleid); - if ($scaleid and $DB->record_exists('panoptosubmission', $param)) { + if ($scaleid && $DB->record_exists('panoptosubmission', $param)) { return true; } else { return false; @@ -285,8 +285,6 @@ function panoptosubmission_supports($feature) { } } - - /** * Lists all gradable areas for the advanced grading methods gramework * @@ -396,7 +394,10 @@ function panoptosubmission_reset_userdata($data) { // Updating dates - shift may be negative too. if ($data->timeshift) { - shift_course_mod_dates('panoptosubmission', array('timedue', 'timeavailable'), $data->timeshift, $data->courseid); + shift_course_mod_dates('panoptosubmission', + array('timedue', 'timeavailable', 'cutofftime'), + $data->timeshift, $data->courseid + ); $status[] = array('component' => $componentstr, 'item' => get_string('datechanged'), 'error' => false); } diff --git a/lib/panoptosubmission_lti_utility.php b/lib/panoptosubmission_lti_utility.php deleted file mode 100644 index 8419313..0000000 --- a/lib/panoptosubmission_lti_utility.php +++ /dev/null @@ -1,329 +0,0 @@ -. - -/** - * Panopto lti helper object. Contains info required for Panopto LTI tools to be used in text editors - * - * @package mod_panoptosubmission - * @copyright Panopto 2021 - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class panoptosubmission_lti_utility { - - /** - * Get the id of the pre-configured LTI tool that matched the Panopto server a course is provisioned to. - * If multiple LTI tools are configured to a single server this will get the first one. - * - * @param int $courseid - the id of the course we are targetting in moodle. - * @return int the id of the first matching tool - */ - public static function get_course_tool_id($courseid) { - global $DB; - - $ltitooltypes = $DB->get_records('lti_types', null, 'name'); - $targetservername = $DB->get_field('block_panopto_foldermap', 'panopto_server', array('moodleid' => $courseid)); - - $tooltypes = []; - foreach ($ltitooltypes as $type) { - $type->config = lti_get_config( - (object)[ - 'typeid' => $type->id, - ] - ); - - if (!empty($targetservername) && stripos($type->config['toolurl'], $targetservername) !== false && - $type->state == LTI_TOOL_STATE_CONFIGURED) { - $currentconfig = lti_get_type_config($type->id); - - if (!empty($currentconfig['customparameters']) && - stripos($currentconfig['customparameters'], 'panopto_student_submission_tool') !== false) { - return $type->id; - } - } - } - - return null; - } - /** - * Launch an external tool activity. - * - * @param stdClass $instance the external tool activity settings - * @return string The HTML code containing the javascript code for the launch - */ - public static function launch_tool($instance) { - list($endpoint, $params) = self::get_launch_data($instance); - - $debuglaunch = ( $instance->debuglaunch == 1 ); - - $content = lti_post_launch_html($params, $endpoint, $debuglaunch); - - return $content; - } - - /** - * Return the launch data required for opening the external tool. - * - * @param stdClass $instance the external tool activity settings - * @param string $nonce the nonce value to use (applies to LTI 1.3 only) - * @return array the endpoint URL and parameters (including the signature) - * @since Moodle 3.0 - */ - private static function get_launch_data($instance, $nonce = '') { - global $PAGE, $CFG, $USER; - - if (empty($CFG)) { - require_once(dirname(__FILE__) . '/../../../../../config.php'); - require_login(); - } - - require_once($CFG->dirroot . '/mod/lti/lib.php'); - require_once($CFG->dirroot . '/mod/lti/locallib.php'); - - if (empty($instance->typeid)) { - $tool = lti_get_tool_by_url_match($instance->toolurl, $instance->course); - if ($tool) { - $typeid = $tool->id; - $ltiversion = isset($tool->ltiversion) ? $tool->ltiversion : LTI_VERSION_1; - } else { - $tool = lti_get_tool_by_url_match($instance->securetoolurl, $instance->course); - if ($tool) { - $typeid = $tool->id; - $ltiversion = isset($tool->ltiversion) ? $tool->ltiversion : LTI_VERSION_1; - } else { - $typeid = null; - $ltiversion = LTI_VERSION_1; - } - } - } else { - $typeid = $instance->typeid; - $tool = lti_get_type($typeid); - $ltiversion = isset($tool->ltiversion) ? $tool->ltiversion : LTI_VERSION_1; - } - - if ($typeid) { - $typeconfig = lti_get_type_config($typeid); - } else { - // There is no admin configuration for this tool. Use configuration in the lti instance record plus some defaults. - $typeconfig = (array)$instance; - - $typeconfig['sendname'] = $instance->instructorchoicesendname; - $typeconfig['sendemailaddr'] = $instance->instructorchoicesendemailaddr; - $typeconfig['customparameters'] = $instance->instructorcustomparameters; - $typeconfig['acceptgrades'] = $instance->instructorchoiceacceptgrades; - $typeconfig['allowroster'] = $instance->instructorchoiceallowroster; - $typeconfig['forcessl'] = '0'; - } - - // Default the organizationid if not specified. - if (empty($typeconfig['organizationid'])) { - $urlparts = parse_url($CFG->wwwroot); - - $typeconfig['organizationid'] = $urlparts['host']; - } - - if (isset($tool->toolproxyid)) { - $toolproxy = lti_get_tool_proxy($tool->toolproxyid); - $key = $toolproxy->guid; - $secret = $toolproxy->secret; - } else { - $toolproxy = null; - if (!empty($instance->resourcekey)) { - $key = $instance->resourcekey; - } else if (defined('LTI_VERSION_1P3') && ($ltiversion === LTI_VERSION_1P3)) { - $key = $tool->clientid; - } else if (!empty($typeconfig['resourcekey'])) { - $key = $typeconfig['resourcekey']; - } else { - $key = ''; - } - if (!empty($instance->password)) { - $secret = $instance->password; - } else if (!empty($typeconfig['password'])) { - $secret = $typeconfig['password']; - } else { - $secret = ''; - } - } - - $endpoint = !empty($instance->toolurl) ? $instance->toolurl : $typeconfig['toolurl']; - $endpoint = trim($endpoint); - - // If the current request is using SSL and a secure tool URL is specified, use it. - if (lti_request_is_using_ssl() && !empty($instance->securetoolurl)) { - $endpoint = trim($instance->securetoolurl); - } - - // If SSL is forced, use the secure tool url if specified. Otherwise, make sure https is on the normal launch URL. - if (isset($typeconfig['forcessl']) && ($typeconfig['forcessl'] == '1')) { - if (!empty($instance->securetoolurl)) { - $endpoint = trim($instance->securetoolurl); - } - - $endpoint = lti_ensure_url_is_https($endpoint); - } else { - if (!strstr($endpoint, '://')) { - $endpoint = 'http://' . $endpoint; - } - } - - $orgid = $typeconfig['organizationid']; - - $course = $PAGE->course; - $islti2 = isset($tool->toolproxyid); - - if (!property_exists($instance, 'course')) { - $instance->course = $course->id; - } - - $allparams = lti_build_request($instance, $typeconfig, $course, $typeid, $islti2); - - if (property_exists($instance, 'custom')) { - foreach ($instance->custom as $customkey => $customvalue) { - $allparams['custom_' . $customkey] = $customvalue; - } - } - - if ($islti2) { - $requestparams = lti_build_request_lti2($tool, $allparams); - } else { - $requestparams = $allparams; - } - - // This is needed to make the lti tool support moodle v3.5.0. - if (function_exists('lti_build_standard_message')) { - $requestparams = array_merge($requestparams, lti_build_standard_message($instance, $orgid, $ltiversion)); - } else { - $requestparams = array_merge($requestparams, lti_build_standard_request($instance, $orgid, $islti2)); - } - - $customstr = ''; - if (isset($typeconfig['customparameters'])) { - $customstr = $typeconfig['customparameters']; - } - $requestparams = array_merge($requestparams, lti_build_custom_parameters( - $toolproxy, - $tool, - $instance, - $allparams, - $customstr, - $instance->instructorcustomparameters, - $islti2 - )); - - $launchcontainer = lti_get_launch_container($instance, $typeconfig); - $returnurlparams = array('course' => $course->id, - 'launch_container' => $launchcontainer, - 'instanceid' => $instance->typeid, - 'sesskey' => sesskey()); - - // Add the return URL. We send the launch container along to help us avoid frames-within-frames when the user returns. - $url = new \moodle_url('/mod/lti/return.php', $returnurlparams); - $returnurl = $url->out(false); - - if (isset($typeconfig['forcessl']) && ($typeconfig['forcessl'] == '1')) { - $returnurl = lti_ensure_url_is_https($returnurl); - } - - $target = ''; - switch($launchcontainer) { - case LTI_LAUNCH_CONTAINER_EMBED: - case LTI_LAUNCH_CONTAINER_EMBED_NO_BLOCKS: - $target = 'iframe'; - break; - case LTI_LAUNCH_CONTAINER_REPLACE_MOODLE_WINDOW: - $target = 'frame'; - break; - case LTI_LAUNCH_CONTAINER_WINDOW: - $target = 'window'; - break; - } - if (!empty($target)) { - $requestparams['launch_presentation_document_target'] = $target; - } - - $requestparams['launch_presentation_return_url'] = $returnurl; - - // Add the parameters configured by the LTI services. - if ($typeid && !$islti2) { - $services = lti_get_services(); - foreach ($services as $service) { - $serviceparameters = $service->get_launch_parameters('basic-lti-launch-request', - $course->id, $USER->id , $typeid, $instance->typeid); - foreach ($serviceparameters as $paramkey => $paramvalue) { - $requestparams['custom_' . $paramkey] = lti_parse_custom_parameter( - $toolproxy, $tool, $requestparams, $paramvalue, $islti2 - ); - } - } - } - - // Allow request params to be updated by sub-plugins. - $plugins = core_component::get_plugin_list('ltisource'); - foreach (array_keys($plugins) as $plugin) { - $pluginparams = component_callback('ltisource_'.$plugin, 'before_launch', - array($instance, $endpoint, $requestparams), array()); - - if (!empty($pluginparams) && is_array($pluginparams)) { - $requestparams = array_merge($requestparams, $pluginparams); - } - } - - if ((!empty($key) && !empty($secret)) || (defined('LTI_VERSION_1P3') && $ltiversion === LTI_VERSION_1P3)) { - - // Lti_sign_jwt was not added until 3.7 so we need to support the original style of processing this. - if (defined('LTI_VERSION_1P3') && function_exists('lti_sign_jwt')) { - if ($ltiversion !== LTI_VERSION_1P3) { - $params = lti_sign_parameters($requestparams, $endpoint, 'POST', $key, $secret); - } else { - $params = lti_sign_jwt($requestparams, $endpoint, $key, $typeid, $nonce); - } - } else { - $params = lti_sign_parameters($requestparams, $endpoint, 'POST', $key, $secret); - } - - $endpointurl = new \moodle_url($endpoint); - $endpointparams = $endpointurl->params(); - - // Strip querystring params in endpoint url from $params to avoid duplication. - if (!empty($endpointparams) && !empty($params)) { - foreach (array_keys($endpointparams) as $paramname) { - if (isset($params[$paramname])) { - unset($params[$paramname]); - } - } - } - - } else { - // If no key and secret, do the launch unsigned. - $returnurlparams['unsigned'] = '1'; - $params = $requestparams; - } - - return array($endpoint, $params); - } - - /** - * Returns true or false depending on if the active user is enrolled in a context - * - * @param object $targetcontext the context we are checking enrollment for - * @return bool true or false if the user is enrolled in the context - */ - public static function is_active_user_enrolled($targetcontext) { - global $USER; - - return is_enrolled($targetcontext, $USER, 'mod/assignment:submit'); - } -} diff --git a/locallib.php b/locallib.php index 8582bfb..a528074 100644 --- a/locallib.php +++ b/locallib.php @@ -31,6 +31,7 @@ require_once($CFG->libdir . '/gradelib.php'); require_once($CFG->dirroot . '/grade/grading/lib.php'); require_once($CFG->dirroot . '/blocks/panopto/lib/panopto_data.php'); +require_once($CFG->dirroot . '/blocks/panopto/lib/block_panopto_lib.php'); /** * Check if the assignment submission end date has passed or if late submissions @@ -49,6 +50,16 @@ function panoptosubmission_submission_past_due($targetactivity) { return $pastdue; } +/** + * Check if the assignment submission cut off has passed + * + * @param object $targetactivity - Instance of a Panopto Student Submission activity + * @return bool - true if past cut off, otherwise false + */ +function panoptosubmission_submission_past_cutoff($targetactivity) { + return (0 != $targetactivity->cutofftime) && (time() > $targetactivity->cutofftime); +} + /** * Check if the assignment submission start date is set and if it has arrived yet. * @@ -418,3 +429,39 @@ function panoptosubmission_get_grading_instance($cminstance, $context, $submissi } return $gradinginstance; } + +/** + * Provision the course if not provisioned already. + * + * @param int $courseid - the id of the course we are targetting in moodle. + * @return bool if success or failure + */ +function panoptosubmission_verify_panopto($courseid) { + $targetautoservername = get_config('block_panopto', 'automatic_operation_target_server'); + if (empty($targetautoservername)) { + throw new moodle_exception('no_automatic_operation_target_server', 'panoptosubmission'); + return false; + } + + try { + $targetserver = panopto_get_target_panopto_server(); + $panopto = new \panopto_data($courseid); + + if (!$panopto->has_valid_panopto()) { + $panopto->servername = $targetserver->name; + $panopto->applicationkey = $targetserver->appkey; + $provisioninginfo = $panopto->get_provisioning_info(); + + if ( (isset($provisioninginfo->unknownerror) && $provisioninginfo->unknownerror === true) + || (isset($provisioninginfo->accesserror) && $provisioninginfo->accesserror === true)) { + return false; + } + + $panopto->provision_course($provisioninginfo, false); + } + return true; + } catch (Exception $e) { + \panopto_data::print_log($e->getMessage()); + return false; + } +} diff --git a/mod_form.php b/mod_form.php index 98e93d1..28f7749 100644 --- a/mod_form.php +++ b/mod_form.php @@ -67,6 +67,11 @@ public function definition() { $mform->addHelpButton('timedue', 'duedate', 'panoptosubmission'); $mform->setDefault('timedue', time() + 7 * 24 * 3600); + $mform->addElement('date_time_selector', + 'cutofftime', get_string('cutoffdate', 'panoptosubmission'), array('optional' => true)); + $mform->addHelpButton('cutofftime', 'cutoffdate', 'panoptosubmission'); + $mform->setDefault('cutofftime', time() + 7 * 24 * 3600); + $ynoptions = array( 0 => get_string('no'), 1 => get_string('yes')); $mform->addElement('select', 'preventlate', get_string('preventlate', 'panoptosubmission'), $ynoptions); @@ -87,4 +92,33 @@ public function definition() { $this->add_action_buttons(); } + + + + /** + * Perform minimal validation on the settings form + * @param array $data + * @param array $files + */ + public function validation($data, $files) { + $errors = parent::validation($data, $files); + + if (!empty($data['timeavailable']) && !empty($data['timedue'])) { + if ($data['timedue'] < $data['timeavailable']) { + $errors['timedue'] = get_string('duedatevalidation', 'panoptosubmission'); + } + } + if (!empty($data['cutofftime']) && !empty($data['timedue'])) { + if ($data['cutofftime'] < $data['timedue'] ) { + $errors['cutofftime'] = get_string('cutoffdatevalidation', 'panoptosubmission'); + } + } + if (!empty($data['timeavailable']) && !empty($data['cutofftime'])) { + if ($data['cutofftime'] < $data['timeavailable']) { + $errors['cutofftime'] = get_string('cutoffdatefromdatevalidation', 'panoptosubmission'); + } + } + + return $errors; + } } diff --git a/renderer.php b/renderer.php index 4ed71e5..9d66638 100644 --- a/renderer.php +++ b/renderer.php @@ -31,7 +31,7 @@ /** * Table class for displaying video submissions for grading */ -class submissions_table extends table_sql { +class panoptosubmission_submissions_table extends table_sql { /** * @var bool $quickgrade Set to true if a quick grade form needs to be rendered. */ @@ -249,6 +249,7 @@ public function col_selectgrade($rowdata) { 'id' => 'panoptogradeinputbox', 'class' => 'panopto-grade-input-box', 'type' => 'number', + 'step' => 'any', 'min' => 0, 'max' => $this->cminstance->grade, 'name' => 'menu[' . $rowdata->id . ']', @@ -468,7 +469,7 @@ private function get_grade_button($rowdata) { $class = 'btn btn-secondary'; $buttontext = get_string('update'); } else { - $buttontext = get_string('grade'); + $buttontext = get_string('gradenoun', 'panoptosubmission'); } $attr = array('id' => 'up'.$rowdata->id, @@ -519,6 +520,13 @@ public function display_mod_info($pansubmissiondata, $context) { $html .= html_writer::end_tag('p'); } + if (!empty($pansubmissiondata->cutofftime)) { + $html .= html_writer::start_tag('p'); + $html .= html_writer::tag('b', get_string('cutoffdate', 'panoptosubmission').': '); + $html .= userdate($pansubmissiondata->cutofftime); + $html .= html_writer::end_tag('p'); + } + // Display a count of the numuber of submissions. if (has_capability('mod/panoptosubmission:gradesubmission', $context)) { @@ -760,9 +768,9 @@ public function display_instructor_buttons($cm, $userid) { /** * This function returns HTML markup to render a the submissions table * @param object $cm A course module object. - * @param int $groupfilter The group id to filter against. - * @param string $filter Filter users who have submitted, submitted and graded or everyone. * @param int $perpage The number of submissions to display on a page. + * @param int $groupfilter The group id to filter against. + * @param string $filter Filter users who have submitted, submitted and graded or everyone. * * @param bool $quickgrade True if quick grading was enabled * @param string $tifirst The first initial of the first name. * @param string $tilast The first initial of the last name. @@ -770,9 +778,9 @@ public function display_instructor_buttons($cm, $userid) { * @return string Returns HTML markup. */ public function display_submissions_table( - $cm, $groupfilter = 0, $filter = 'all', $perpage, $quickgrade = false, $tifirst = '', $tilast = '', $page = 0) { + $cm, $perpage, $groupfilter = 0, $filter = 'all', $quickgrade = false, $tifirst = '', $tilast = '', $page = 0) { - global $DB, $COURSE, $USER; + global $DB, $COURSE, $USER, $CFG; // Get a list of users who have submissions and retrieve grade data for those users. $users = panoptosubmission_get_submissions($cm->instance, $filter); @@ -920,11 +928,16 @@ public function display_submissions_table( } } - $table = new submissions_table('panopto_submit_table', $cm, $currentgrades, $quickgrade, $tifirst, $tilast, $page); + $table = new panoptosubmission_submissions_table('panopto_submit_table', $cm, $currentgrades, $quickgrade, $tifirst, $tilast, $page); + + // If Moodle version is less than 3.11.0 use user_picture, otherwise use core_user api. + $userfields = $CFG->version < 2021051700 + ? user_picture::fields('u') + : \core_user\fields::for_userpic()->get_sql('u', false, '', '', false)->selects; // In order for the sortable first and last names to work. User ID has to be the first column returned and must be. // Returned as id. Otherwise the table will display links to user profiles that are incorrect or do not exist. - $columns = user_picture::fields('u').', ps.id AS submitid, '; + $columns = $userfields .', ps.id AS submitid, '; $columns .= ' ps.grade, ps.submissioncomment, ps.timemodified, ps.source, ps.width, ps.height, ps.timemarked, '; $columns .= '1 AS status, 1 AS selectgrade ' . $groupscolumn; $where .= ' u.deleted = 0 AND u.id IN (' . implode(',', $students) . ') ' . $groupswhere; @@ -945,7 +958,7 @@ public function display_submissions_table( $col2 = get_string('fullname', 'panoptosubmission'); $col3 = get_string('useremail', 'panoptosubmission'); $col4 = get_string('status', 'panoptosubmission'); - $col5 = get_string('grade', 'panoptosubmission'); + $col5 = get_string('gradenoun', 'panoptosubmission'); $col6 = get_string('timemodified', 'panoptosubmission'); $col7 = get_string('grademodified', 'panoptosubmission'); $col8 = get_string('submissioncomment', 'panoptosubmission'); @@ -1097,7 +1110,8 @@ private function get_video_iframe($submission, $courseid, $cmid) { if (!is_null($submission) && !empty($submission->source)) { $contenturl = new moodle_url($submission->source); - $ltiviewerparams['resourcelinkid'] = sha1($submission->source . '&' . $courseid . '&' . $submission->id . '&' . $submission->timemodified); + $ltiviewerparams['resourcelinkid'] = + sha1($submission->source . '&' . $courseid . '&' . $submission->id . '&' . $submission->timemodified); $ltiviewerparams['custom'] = $submission->customdata; $ltiviewerparams['contenturl'] = $contenturl->out(false); } else { @@ -1259,12 +1273,12 @@ public function display_grade_feedback($pansubmissionactivity, $context) { $grade = $item->grades[$USER->id]; // Hidden or error. - if ($grade->hidden or $grade->grade === false) { + if ($grade->hidden || $grade->grade === false) { return; } // Nothing to show yet. - if ($grade->grade === null and empty($grade->str_feedback)) { + if ($grade->grade === null && empty($grade->str_feedback)) { return; } @@ -1301,7 +1315,7 @@ public function display_grade_feedback($pansubmissionactivity, $context) { echo ' '; echo ''; echo '
'; - echo get_string("grade").': '.$grade->str_long_grade; + echo get_string("gradenoun", "panoptosubmission").': '.$grade->str_long_grade; echo '
'; echo '
'; @@ -1324,7 +1338,7 @@ public function render_panoptosubmission_course_index_summary(panoptosubmission_ $courseformatname = $indexsummary->courseformatname; $strduedate = get_string('duedate', 'panoptosubmission'); $strsubmission = get_string('submission', 'panoptosubmission'); - $strgrade = get_string('grade'); + $strgrade = get_string('gradenoun', 'panoptosubmission'); $table = new html_table(); if ($indexsummary->usesections) { diff --git a/single_submission.php b/single_submission.php index a32e6bc..018ffd7 100644 --- a/single_submission.php +++ b/single_submission.php @@ -38,10 +38,7 @@ list($cm, $course, $pansubmissionactivity) = panoptosubmission_validate_cmid($id); require_login($course->id, false, $cm); - -if (!confirm_sesskey()) { - throw new moodle_exception('confirmsesskeybad', 'error'); -} +require_sesskey(); global $CFG, $PAGE, $OUTPUT, $USER; @@ -221,7 +218,7 @@ if (!empty($gradinginfo->outcomes)) { foreach ($gradinginfo->outcomes as $n => $old) { $name = 'outcome_'.$n; - if (isset($submitteddata->{$name}[$userid]) and + if (isset($submitteddata->{$name}[$userid]) && $old->grades[$userid]->grade != $submitteddata->{$name}[$userid]) { $data[$n] = $submitteddata->{$name}[$userid]; @@ -234,11 +231,9 @@ $course->id, 'mod', 'panoptosubmission', $pansubmissionactivity->id, $userid, $data); } } - } redirect($previousurl); - } echo $OUTPUT->header(); diff --git a/single_submission_form.php b/single_submission_form.php index 4205bb2..9c9332a 100644 --- a/single_submission_form.php +++ b/single_submission_form.php @@ -66,7 +66,7 @@ public function definition() { $mform->addelement('static', 'submittinguser', $this->_customdata->submissionuserpic, $this->_customdata->submissionuserinfo); - $submission = $this->_customdata->submission; + $submission = $this->_customdata->submission; $gradinginfo = $this->_customdata->grading_info; if (!empty($submission->source)) { @@ -91,11 +91,11 @@ public function definition() { $gradingdisabled ); - $mform->addElement('header', 'gradeheader', get_string('grade')); + $mform->addElement('header', 'gradeheader', get_string('gradeverb', 'panoptosubmission')); if ($gradinginstance) { $gradingelement = $mform->addElement('grading', 'advancedgrading', - get_string('grade').':', + get_string('gradeverb', 'panoptosubmission').':', array('gradinginstance' => $gradinginstance)); if ($gradingdisabled) { $gradingelement->freeze(); @@ -109,6 +109,7 @@ public function definition() { 'id' => 'panoptogradeinputbox', 'class' => 'panopto-grade-input-box', 'type' => 'number', + 'step' => 'any', 'min' => 0, 'max' => $this->_customdata->cminstance->grade ); @@ -118,17 +119,17 @@ public function definition() { $mform->setDefault('xgrade', $currentgrade); } else { $attributes = array(); - $mform->addElement('select', 'xgrade', get_string('grade') . ':', $grademenu, $attributes); + $mform->addElement('select', 'xgrade', get_string('gradenoun', 'panoptosubmission') . ':', $grademenu, $attributes); if (isset($submission->grade)) { $mform->setDefault('xgrade', $this->_customdata->submission->grade); } else { - $mform->setDefault('xgrade', '-1' ); + $mform->setDefault('xgrade', '-1'); } } } - $mform->setType('xgrade', PARAM_INT); + $mform->setType('xgrade', PARAM_NUMBER); if (!empty($this->_customdata->enableoutcomes) && !empty($gradinginfo)) { @@ -182,7 +183,7 @@ public function definition() { } $mform->addElement('static', 'finalgrade', get_string('currentgrade', 'panoptosubmission') . ':', $grade); - $mform->setType('finalgrade', PARAM_INT); + $mform->setType('finalgrade', PARAM_NUMBER); $mform->addElement('static', 'markingteacher', $this->_customdata->markingteacherpic, $this->_customdata->markingteacherinfo); diff --git a/styles.css b/styles.css index 470a8c2..4892665 100644 --- a/styles.css +++ b/styles.css @@ -1,7 +1,7 @@ .panopto-player-container { - transition: opacity .5s ease-in-out; - -moz-transition: opacity .5s ease-in-out; - -webkit-transition: opacity .5s ease-in-out; + transition: opacity .5s ease-in-out; + -moz-transition: opacity .5s ease-in-out; + -webkit-transition: opacity .5s ease-in-out; } .panopto-player-container.no-session { @@ -82,4 +82,8 @@ .panopto-grade-input-container { white-space: nowrap; +} + +.panopto-grade-input-container .panopto-grade-input-box { + width: 100px; } \ No newline at end of file diff --git a/submission.php b/submission.php index 2bacabe..557d45b 100644 --- a/submission.php +++ b/submission.php @@ -25,9 +25,7 @@ require_once(dirname(dirname(dirname(__FILE__))).'/config.php'); require_once(dirname(__FILE__).'/locallib.php'); -if (!confirm_sesskey()) { - throw new moodle_exception('confirmsesskeybad', 'error'); -} +require_sesskey(); $source = required_param('source', PARAM_URL); $customdata = required_param('customdata', PARAM_TEXT); @@ -60,8 +58,10 @@ $PAGE->set_heading($course->fullname); -if (panoptosubmission_submission_past_due($pansubmissionactivity)) { +if (panoptosubmission_submission_past_cutoff($pansubmissionactivity)) { throw new moodle_exception('assignmentexpired', 'panoptosubmission', 'course/view.php?id=' . $course->id); +} else if (panoptosubmission_submission_past_due($pansubmissionactivity)) { + throw new moodle_exception('assignmentpastdue', 'panoptosubmission', 'course/view.php?id=' . $course->id); } echo $OUTPUT->header(); @@ -144,7 +144,6 @@ } else { notice(get_string('failedtoinsertsubmission', 'panoptosubmission'), $url, $course); } - } $context = $PAGE->context; diff --git a/version.php b/version.php index b275506..764df4a 100644 --- a/version.php +++ b/version.php @@ -15,7 +15,7 @@ // along with Moodle. If not, see . /** - * contains the version information for the Panopto Student Submission mod + * Contains the version information for the Panopto Student Submission mod * * @package mod_panoptosubmission * @copyright Panopto 2021 @@ -24,13 +24,23 @@ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2022090700; +// The current plugin version (Date: YYYYMMDDXX). +$plugin->version = 2022122000; // Requires this Moodle version - 2.7. $plugin->requires = 2014051202; + +// Never run cron for this plugin. $plugin->cron = 0; + +// Full name of the plugin (used for diagnostics). $plugin->component = 'mod_panoptosubmission'; + +// This is considered as ready for production sites. $plugin->maturity = MATURITY_STABLE; + +// Dependencies. $plugin->dependencies = array( - 'block_panopto' => ANY_VERSION + 'block_panopto' => 2022122000, + 'mod_lti' => ANY_VERSION ); diff --git a/view.php b/view.php index 4225953..2cb2d73 100644 --- a/view.php +++ b/view.php @@ -69,6 +69,7 @@ $submitdisabled = false; if (panoptosubmission_submission_past_due($panactivityinstance) || + panoptosubmission_submission_past_cutoff($panactivityinstance) || !panoptosubmission_submission_available_yet($panactivityinstance)) { $submitdisabled = true; } diff --git a/view_submission.php b/view_submission.php index 6896e17..3ea6b68 100644 --- a/view_submission.php +++ b/view_submission.php @@ -22,65 +22,72 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -/** - * This page creates an lti request and echos the content from the response. Used to view submissions and other Panopto content. - */ -function init_panoptosubmission_view() { - global $CFG; - if (empty($CFG)) { - require_once(dirname(dirname(dirname(__FILE__))) . '/config.php'); - } - require_once(dirname(__FILE__) . '/lib/panoptosubmission_lti_utility.php'); - require_once(dirname(dirname(dirname(__FILE__))) . '/mod/lti/lib.php'); - require_once(dirname(dirname(dirname(__FILE__))) . '/mod/lti/locallib.php'); +require_once(dirname(dirname(dirname(__FILE__))) . '/config.php'); +require_once($CFG->dirroot . '/blocks/panopto/lib/lti/panoptoblock_lti_utility.php'); +require_once(dirname(__FILE__). '/locallib.php'); +require_once(dirname(dirname(dirname(__FILE__))) . '/mod/lti/lib.php'); +require_once(dirname(dirname(dirname(__FILE__))) . '/mod/lti/locallib.php'); - $courseid = required_param('course', PARAM_INT); - $contenturl = urldecode(optional_param('contenturl', '', PARAM_URL)); - $customdata = urldecode(optional_param('custom', '', PARAM_RAW_TRIMMED)); - $resourcelinkid = optional_param('resourcelinkid', '', PARAM_RAW_TRIMMED); +$courseid = required_param('course', PARAM_INT); +$contenturl = urldecode(optional_param('contenturl', '', PARAM_URL)); +$customdata = urldecode(optional_param('custom', '', PARAM_RAW_TRIMMED)); +$resourcelinkid = optional_param('resourcelinkid', '', PARAM_RAW_TRIMMED); - $course = get_course($courseid); +$course = get_course($courseid); - $context = context_course::instance($courseid); +$context = context_course::instance($courseid); - if (empty($resourcelinkid)) { - $ltiviewerurl = new moodle_url("/mod/panoptosubmission/view_submission.php"); - $resourcelinkid = sha1($ltiviewerurl->out(false) . - '&' . $courseid . - '&' . $course->timecreated - ); - } +if (empty($resourcelinkid)) { + $ltiviewerurl = new moodle_url("/mod/panoptosubmission/view_submission.php"); + $resourcelinkid = sha1($ltiviewerurl->out(false) . + '&' . $courseid . + '&' . $course->timecreated + ); +} - require_login($course); +require_login($course); +// Provision the course if we can. +if (panoptosubmission_verify_panopto($courseid)) { // Get a matching LTI tool for the course. - $toolid = \panoptosubmission_lti_utility::get_course_tool_id($courseid); + $toolid = \panoptoblock_lti_utility::get_course_tool_id($courseid, 'panopto_student_submission_tool'); - // If no lti tool exists then we can not continue. if (is_null($toolid)) { throw new moodle_exception('no_existing_lti_tools', 'panoptosubmission'); return; } +} else { + // If we were unable to provision the course, we cannot continue. + return; +} - $lti = new stdClass(); +$lti = new stdClass(); - // Give it some random id, this is not used in the code but will create a PHP notice if not provided. - $lti->id = $resourcelinkid; - $lti->typeid = $toolid; - $lti->launchcontainer = LTI_LAUNCH_CONTAINER_WINDOW; - $lti->toolurl = $contenturl; - $lti->custom = new stdClass(); - $lti->instructorcustomparameters = []; - $lti->debuglaunch = false; - if ($customdata) { - $decoded = json_decode($customdata, true); +// Give it some random id, this is not used in the code but will create a PHP notice if not provided. +$lti->id = $resourcelinkid; +$lti->typeid = $toolid; +$lti->launchcontainer = LTI_LAUNCH_CONTAINER_WINDOW; +$lti->toolurl = $contenturl; +$lti->custom = new stdClass(); +$lti->instructorcustomparameters = []; +$lti->debuglaunch = false; +if ($customdata) { + $decoded = json_decode($customdata, true); - foreach ($decoded as $key => $value) { - $lti->custom->$key = $value; - } + foreach ($decoded as $key => $value) { + $lti->custom->$key = $value; } +} - echo \panoptosubmission_lti_utility::launch_tool($lti); +// LTI 1.3 login request. +$config = lti_get_type_type_config($toolid); +if ($config->lti_ltiversion === LTI_VERSION_1P3) { + if (!isset($SESSION->lti_initiatelogin_status)) { + echo lti_initiate_login($courseid, "mod_panoptosubmission,'',{$toolid},{$resourcelinkid},{$contenturl},{$customdata}", $lti, $config); + exit; + } else { + unset($SESSION->lti_initiatelogin_status); + } } -init_panoptosubmission_view(); +echo \panoptoblock_lti_utility::launch_tool($lti);