From 63cba014a7bca1051fc83f03db0d93878d3f6584 Mon Sep 17 00:00:00 2001 From: jmalmsten-panopto Date: Tue, 20 Dec 2022 18:23:10 -0800 Subject: [PATCH] 2022122000 release code. (#193) --- .github/workflows/moodle-plugin-ci.yml | 121 +++ SSO.php | 2 +- build_category_structure.php | 5 +- classes/admin/trim_configtext.php | 18 +- classes/categorytasks.php | 8 +- classes/privacy/provider.php | 8 +- classes/rollingsync.php | 83 +- db/upgrade.php | 2 +- .../AuthManagementWsdlClass.php | 6 +- .../SessionManagementWsdlClass.php | 6 +- .../UserManagementWsdlClass.php | 6 +- lib/cli/build_category_structure_cli.php | 8 +- lib/cli/remove_all_panopto_adhoc_tasks.php | 8 +- lib/cli/rename_all_folders_cli.php | 6 +- lib/cli/upgrade_all_folders_cli.php | 6 +- lib/lti/auth.php | 229 ++++++ lib/lti/panoptoblock_lti_utility.php | 748 ++++++++++++++++++ lib/panopto_auth_soap_client.php | 9 +- lib/panopto_category_data.php | 13 +- lib/panopto_data.php | 19 +- lib/panopto_session_soap_client.php | 12 +- lib/panopto_timeout_soap_client.php | 4 +- lib/panopto_user_soap_client.php | 5 +- lib/panoptoblock_lti_utility.php | 72 -- panopto_content.php | 5 +- provision_course.php | 6 +- provision_course_internal.php | 5 +- reinitialize_imports.php | 34 +- rename_all_folders.php | 6 +- settings.php | 5 - styles.css | 6 +- unprovision_course.php | 6 +- unprovision_course_internal.php | 6 +- upgrade_all_folders.php | 6 +- version.php | 2 +- views/ensure_category_branch_start.html.php | 1 + views/provisioned_course.html.php | 174 ++-- 37 files changed, 1343 insertions(+), 323 deletions(-) create mode 100644 .github/workflows/moodle-plugin-ci.yml create mode 100644 lib/lti/auth.php create mode 100644 lib/lti/panoptoblock_lti_utility.php delete mode 100644 lib/panoptoblock_lti_utility.php diff --git a/.github/workflows/moodle-plugin-ci.yml b/.github/workflows/moodle-plugin-ci.yml new file mode 100644 index 0000000..8f68247 --- /dev/null +++ b/.github/workflows/moodle-plugin-ci.yml @@ -0,0 +1,121 @@ +name: Moodle Plugin CI + +on: [push, pull_request] + +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 + + - 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/SSO.php b/SSO.php index 8d98fe9..eca6a16 100644 --- a/SSO.php +++ b/SSO.php @@ -102,7 +102,7 @@ break; } - // Strip ReturnUrl so we can append it on the end + // Strip ReturnUrl so we can append it on the end. parse_str(parse_url($callbackurl, PHP_URL_QUERY), $params); $returnurl = isset($params['ReturnUrl']) ? $params['ReturnUrl'] : ""; diff --git a/build_category_structure.php b/build_category_structure.php index 095dc8f..1832345 100644 --- a/build_category_structure.php +++ b/build_category_structure.php @@ -21,11 +21,8 @@ * @copyright Panopto 2009 - 2017 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -global $CFG; -if (empty($CFG)) { - require_once(dirname(__FILE__) . '/../../config.php'); -} +require_once(dirname(__FILE__) . '/../../config.php'); require_once($CFG->libdir . '/formslib.php'); require_once(dirname(__FILE__) . '/classes/panopto_build_category_structure_form.php'); require_once(dirname(__FILE__) . '/lib/block_panopto_lib.php'); diff --git a/classes/admin/trim_configtext.php b/classes/admin/trim_configtext.php index 65ff00a..265daa7 100644 --- a/classes/admin/trim_configtext.php +++ b/classes/admin/trim_configtext.php @@ -30,29 +30,13 @@ */ class admin_setting_configtext_trimmed extends admin_setting_configtext { - /** - * Config text constructor - * - * @param string $name unique ascii name, either 'mysetting' for settings that in config, - * or 'myplugin/mysetting' for ones in config_plugins. - * @param string $visiblename localised - * @param string $description long localised info - * @param string $defaultsetting - * @param mixed $paramtype int means PARAM_XXX type, string is a allowed format in regex - * @param int $size default field size - */ - public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, - $size=null) { - parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $size); - } - /** * Write data to storage * * @param string $data the data being written. */ public function write_setting($data) { - if ($this->paramtype === PARAM_INT and $data === '') { + if ($this->paramtype === PARAM_INT && $data === '') { // Do not complain if '' used instead of 0. $data = 0; } diff --git a/classes/categorytasks.php b/classes/categorytasks.php index 50af54d..86d17ea 100644 --- a/classes/categorytasks.php +++ b/classes/categorytasks.php @@ -23,10 +23,10 @@ */ defined('MOODLE_INTERNAL') || die(); -global $CFG; -if (empty($CFG)) { - require_once(dirname(__FILE__) . '/../../../config.php'); -} + +// No login check is expected since we can run this from console. +// @codingStandardsIgnoreLine +require_once(dirname(__FILE__) . '/../../../config.php'); require_once(dirname(__FILE__) . '/../lib/panopto_data.php'); /** diff --git a/classes/privacy/provider.php b/classes/privacy/provider.php index 4c1c20a..b550e82 100644 --- a/classes/privacy/provider.php +++ b/classes/privacy/provider.php @@ -30,12 +30,13 @@ use \core_privacy\local\metadata\collection; +// @codingStandardsIgnoreStart if (interface_exists('\core_privacy\local\request\userlist')) { interface my_userlist extends \core_privacy\local\request\userlist { } } else { interface my_userlist { - }; + } } if (interface_exists('\core_privacy\local\request\core_userlist_provider')) { @@ -43,7 +44,7 @@ interface my_userlist_provider extends \core_privacy\local\request\core_userlist } } else { interface my_userlist_provider { - }; + } } if (interface_exists('\core_privacy\local\request\core_user_data_provider')) { @@ -51,8 +52,9 @@ interface my_userdataprovider extends \core_privacy\local\request\core_user_data } } else { interface my_userdataprovider { - }; + } } +// @codingStandardsIgnoreEnd /** * Provider that stores user data. diff --git a/classes/rollingsync.php b/classes/rollingsync.php index 4215b32..cf19304 100644 --- a/classes/rollingsync.php +++ b/classes/rollingsync.php @@ -25,13 +25,11 @@ defined('MOODLE_INTERNAL') || die(); -global $CFG; -if (empty($CFG)) { - require_once(dirname(__FILE__) . '/../../../config.php'); -} - +// No login check is expected since we can run this from console. +// @codingStandardsIgnoreLine +require_once(dirname(__FILE__) . '/../../../config.php'); require_once(dirname(__FILE__) . '/../lib/panopto_data.php'); -require_once(dirname(__FILE__) . '/../lib/panoptoblock_lti_utility.php'); +require_once(dirname(__FILE__) . '/../lib/lti/panoptoblock_lti_utility.php'); require_once($CFG->libdir . '/pagelib.php'); require_once($CFG->libdir . '/blocklib.php'); @@ -150,16 +148,81 @@ public static function courserestored(\core\event\course_restored $event) { $newcourseid = intval($event->courseid); $originalcourseid = intval($event->other['originalcourseid']); + // Make sure we cannot copy/import course into itself. + if ($originalcourseid == $newcourseid) { + return; + } + $panoptodata = new \panopto_data($newcourseid); $originalpanoptodata = new \panopto_data($originalcourseid); - // We should only perform the import if both the target and the source course are provisioned in panopto. - if (isset($panoptodata->servername) && !empty($panoptodata->servername) && + $istargetcourseprovisioned = + isset($panoptodata->servername) && !empty($panoptodata->servername) && isset($panoptodata->applicationkey) && !empty($panoptodata->applicationkey) && - isset($panoptodata->sessiongroupid) && !empty($panoptodata->sessiongroupid) && + isset($panoptodata->sessiongroupid) && !empty($panoptodata->sessiongroupid); + + $isoriginalcourseprovisioned = isset($originalpanoptodata->servername) && !empty($originalpanoptodata->servername) && isset($originalpanoptodata->applicationkey) && !empty($originalpanoptodata->applicationkey) && - isset($originalpanoptodata->sessiongroupid) && !empty($originalpanoptodata->sessiongroupid)) { + isset($originalpanoptodata->sessiongroupid) && !empty($originalpanoptodata->sessiongroupid); + + // If any is provisioned, check if we need to provision the other course. + if ($istargetcourseprovisioned || $isoriginalcourseprovisioned) { + // Source course not provisioned, lets provision with target servername and applicationkey. + if (!$isoriginalcourseprovisioned) { + $panoptodata = new \panopto_data($newcourseid); + $originalpanoptodata->servername = $panoptodata->servername; + $originalpanoptodata->applicationkey = $panoptodata->applicationkey; + $originalprovisioninginfo = $originalpanoptodata->get_provisioning_info(); + $originalprovisioneddata = $originalpanoptodata->provision_course($originalprovisioninginfo, false); + if (isset($originalprovisioneddata->Id) && !empty($originalprovisioneddata->Id)) { + $isoriginalcourseprovisioned = true; + } + } + + // Target course not provisioned, lets provision with original servername and applicationkey. + if (!$istargetcourseprovisioned) { + $originalpanoptodata = new \panopto_data($originalcourseid); + $panoptodata->servername = $originalpanoptodata->servername; + $panoptodata->applicationkey = $originalpanoptodata->applicationkey; + $provisioninginfo = $panoptodata->get_provisioning_info(); + $targetprovisioneddata = $panoptodata->provision_course($provisioninginfo, false); + if (isset($targetprovisioneddata->Id) && !empty($targetprovisioneddata->Id)) { + $istargetcourseprovisioned = true; + } + } + } else { + // Neither course is provisioned. + + // Provision target course using automatic operation server. + $targetserver = panopto_get_target_panopto_server(); + $panoptodata->servername = $targetserver->name; + $panoptodata->applicationkey = $targetserver->appkey; + $provisioninginfo = $panoptodata->get_provisioning_info(); + $targetprovisioneddata = $panoptodata->provision_course($provisioninginfo, false); + if (isset($targetprovisioneddata->Id) && !empty($targetprovisioneddata->Id)) { + $istargetcourseprovisioned = true; + } + + // Provision original course using target course servername and applicationkey. + $panoptodata = new \panopto_data($newcourseid); + $originalpanoptodata->servername = $panoptodata->servername; + $originalpanoptodata->applicationkey = $panoptodata->applicationkey; + $originalprovisioninginfo = $originalpanoptodata->get_provisioning_info(); + $originalprovisioneddata = $originalpanoptodata->provision_course($originalprovisioninginfo, false); + if (isset($originalprovisioneddata->Id) && !empty($originalprovisioneddata->Id)) { + $isoriginalcourseprovisioned = true; + } + } + + // We should only perform the import if both the target and the source courses are provisioned in panopto. + if ($istargetcourseprovisioned && $isoriginalcourseprovisioned) { + + // If courses are provisioned to different servers, log an error and return. + if (strcmp($panoptodata->servername, $originalpanoptodata->servername) !== 0) { + \panopto_data::print_log('ERROR: Mismatch in server name inside "courserestored" during course import/copy.'); + return; + } $panoptodata->ensure_auth_manager(); $activepanoptoserverversion = $panoptodata->authmanager->get_server_version(); diff --git a/db/upgrade.php b/db/upgrade.php index ef525fb..a863a58 100644 --- a/db/upgrade.php +++ b/db/upgrade.php @@ -93,7 +93,7 @@ function xmldb_block_panopto_upgrade($oldversion = 0) { upgrade_block_savepoint(true, 2015012901, 'panopto'); } - if ($oldversion <= 2016101227) { + if ($oldversion < 2016101227) { // Move block global settings to _config_plugin table. // First, move each server configuration. We are not relying here on // block_panopto_server_number to determine number of servers, as there diff --git a/lib/AuthManagement/AuthManagementWsdlClass.php b/lib/AuthManagement/AuthManagementWsdlClass.php index 15e18ce..141d456 100644 --- a/lib/AuthManagement/AuthManagementWsdlClass.php +++ b/lib/AuthManagement/AuthManagementWsdlClass.php @@ -228,7 +228,7 @@ public function __construct($_arrayOfValues = array(),$_resetSoapClient = true) foreach($_arrayOfValues as $name=>$value) $this->_set($name,$value); } - + if(array_key_exists('panopto_socket_timeout', $_arrayOfValues)) { self::$soapClient->__setSocketTimeout($_arrayOfValues['panopto_socket_timeout']); } @@ -1000,7 +1000,7 @@ public function __construct ($wsdl, array $options = null) { } /** - * wrapper around dorequest so we can enforce https on all calls + * Wrapper around dorequest so we can enforce https on all calls * * @param object $request - the request being made * @param string $location - the location the request will be made to @@ -1008,7 +1008,7 @@ public function __construct ($wsdl, array $options = null) { * @param string $version * @param int $one_way */ - public function __doRequest ($request, $location, $action, $version, $one_way = 0) { + public function __doRequest($request, $location, $action, $version, $one_way = 0) { if (get_config('block_panopto', 'enforce_https_on_wsdl')) { $location = str_replace('http://', 'https://', $location); } diff --git a/lib/SessionManagement/SessionManagementWsdlClass.php b/lib/SessionManagement/SessionManagementWsdlClass.php index a4fb159..e586d8e 100644 --- a/lib/SessionManagement/SessionManagementWsdlClass.php +++ b/lib/SessionManagement/SessionManagementWsdlClass.php @@ -228,7 +228,7 @@ public function __construct($_arrayOfValues = array(),$_resetSoapClient = true) foreach($_arrayOfValues as $name=>$value) $this->_set($name,$value); } - + if(array_key_exists('panopto_socket_timeout', $_arrayOfValues)) { self::$soapClient->__setSocketTimeout($_arrayOfValues['panopto_socket_timeout']); } @@ -999,7 +999,7 @@ public function __construct ($wsdl, array $options = null) { } /** - * wrapper around dorequest so we can enforce https on all calls + * Wrapper around dorequest so we can enforce https on all calls * * @param object $request - the request being made * @param string $location - the location the request will be made to @@ -1007,7 +1007,7 @@ public function __construct ($wsdl, array $options = null) { * @param string $version * @param int $one_way */ - public function __doRequest ($request, $location, $action, $version, $one_way = 0) { + public function __doRequest($request, $location, $action, $version, $one_way = 0) { if (get_config('block_panopto', 'enforce_https_on_wsdl')) { $location = str_replace('http://', 'https://', $location); } diff --git a/lib/UserManagement/UserManagementWsdlClass.php b/lib/UserManagement/UserManagementWsdlClass.php index 9e42ee0..d48d080 100644 --- a/lib/UserManagement/UserManagementWsdlClass.php +++ b/lib/UserManagement/UserManagementWsdlClass.php @@ -224,7 +224,7 @@ public function __construct($_arrayOfValues = array(),$_resetSoapClient = true) foreach($_arrayOfValues as $name=>$value) $this->_set($name,$value); } - + if(array_key_exists('panopto_socket_timeout', $_arrayOfValues)) { self::$soapClient->__setSocketTimeout($_arrayOfValues['panopto_socket_timeout']); } @@ -994,7 +994,7 @@ public function __construct ($wsdl, array $options = null) { } /** - * wrapper around dorequest so we can enforce https on all calls + * Wrapper around dorequest so we can enforce https on all calls * * @param object $request - the request being made * @param string $location - the location the request will be made to @@ -1002,7 +1002,7 @@ public function __construct ($wsdl, array $options = null) { * @param string $version * @param int $one_way */ - public function __doRequest ($request, $location, $action, $version, $one_way = 0) { + public function __doRequest($request, $location, $action, $version, $one_way = 0) { if (get_config('block_panopto', 'enforce_https_on_wsdl')) { $location = str_replace('http://', 'https://', $location); } diff --git a/lib/cli/build_category_structure_cli.php b/lib/cli/build_category_structure_cli.php index fb573c7..5a70f08 100644 --- a/lib/cli/build_category_structure_cli.php +++ b/lib/cli/build_category_structure_cli.php @@ -28,14 +28,10 @@ define('CLI_SCRIPT', true); -global $CFG; -if (empty($CFG)) { - require_once(dirname(__FILE__) . '/../../../../config.php'); -} - +require_once(dirname(__FILE__) . '/../../../../config.php'); +require_once(dirname(__FILE__) . '/../panopto_category_data.php'); require_once($CFG->libdir . '/clilib.php'); require_once($CFG->libdir . '/formslib.php'); -require_once(dirname(__FILE__) . '/../panopto_category_data.php'); $admin = get_admin(); if (!$admin) { diff --git a/lib/cli/remove_all_panopto_adhoc_tasks.php b/lib/cli/remove_all_panopto_adhoc_tasks.php index d94b730..c9871f7 100644 --- a/lib/cli/remove_all_panopto_adhoc_tasks.php +++ b/lib/cli/remove_all_panopto_adhoc_tasks.php @@ -26,14 +26,10 @@ define('CLI_SCRIPT', true); -global $CFG; -if (empty($CFG)) { - require_once(dirname(__FILE__) . '/../../../../config.php'); -} - +require_once(dirname(__FILE__) . '/../../../../config.php'); +require_once(dirname(__FILE__) . '/../panopto_data.php'); require_once($CFG->libdir . '/clilib.php'); require_once($CFG->libdir . '/formslib.php'); -require_once(dirname(__FILE__) . '/../panopto_data.php'); $admin = get_admin(); if (!$admin) { diff --git a/lib/cli/rename_all_folders_cli.php b/lib/cli/rename_all_folders_cli.php index 4b262d1..6c6d698 100644 --- a/lib/cli/rename_all_folders_cli.php +++ b/lib/cli/rename_all_folders_cli.php @@ -29,11 +29,7 @@ define('CLI_SCRIPT', true); -global $CFG, $DB; -if (empty($CFG)) { - require_once(dirname(__FILE__) . '/../../../../config.php'); -} - +require_once(dirname(__FILE__) . '/../../../../config.php'); require_once($CFG->libdir . '/clilib.php'); require_once($CFG->libdir . '/formslib.php'); require_once(dirname(__FILE__) . '/../panopto_data.php'); diff --git a/lib/cli/upgrade_all_folders_cli.php b/lib/cli/upgrade_all_folders_cli.php index 722f30e..7820a0c 100644 --- a/lib/cli/upgrade_all_folders_cli.php +++ b/lib/cli/upgrade_all_folders_cli.php @@ -29,11 +29,7 @@ define('CLI_SCRIPT', true); -global $CFG, $DB; -if (empty($CFG)) { - require_once(dirname(__FILE__) . '/../../../../config.php'); -} - +require_once(dirname(__FILE__) . '/../../../../config.php'); require_once($CFG->libdir . '/clilib.php'); require_once($CFG->libdir . '/formslib.php'); require_once(dirname(__FILE__) . '/../panopto_data.php'); diff --git a/lib/lti/auth.php b/lib/lti/auth.php new file mode 100644 index 0000000..689cd69 --- /dev/null +++ b/lib/lti/auth.php @@ -0,0 +1,229 @@ +. + +/** + * Wrapper around mod/lti/auth so we can do our custom auth. + * + * @package block_panopto + * @copyright Panopto 2009 - 2016 /With contributions from Spenser Jones (sjones@ambrose.edu) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +require_once(dirname(__FILE__) . '/../../../../config.php'); +require_once($CFG->dirroot . '/mod/lti/locallib.php'); +require_once($CFG->libdir . '/weblib.php'); +require_once(dirname(__FILE__) . '/panoptoblock_lti_utility.php'); + +global $_POST, $_SERVER; + +if (!isloggedin() && empty($_POST['repost'])) { + header_remove("Set-Cookie"); + $PAGE->set_pagelayout('popup'); + $PAGE->set_context(context_system::instance()); + $output = $PAGE->get_renderer('mod_lti'); + $page = new \mod_lti\output\repost_crosssite_page($_SERVER['REQUEST_URI'], $_POST); + echo $output->header(); + echo $output->render($page); + echo $output->footer(); + return; +} + +$scope = optional_param('scope', '', PARAM_TEXT); +$responsetype = optional_param('response_type', '', PARAM_TEXT); +$clientid = optional_param('client_id', '', PARAM_TEXT); +$redirecturi = optional_param('redirect_uri', '', PARAM_URL); +$loginhint = optional_param('login_hint', '', PARAM_TEXT); +$ltimessagehint = optional_param('lti_message_hint', '', PARAM_TEXT); +$state = optional_param('state', '', PARAM_TEXT); +$responsemode = optional_param('response_mode', '', PARAM_TEXT); +$nonce = optional_param('nonce', '', PARAM_TEXT); +$prompt = optional_param('prompt', '', PARAM_TEXT); + +list($pluginname, $callback, $toolid, $resourcelinkid, $contenturl, $customdata) = explode(',', $ltimessagehint, 6); +$ispanoptoplugin = false; +$pluginpath = ''; +switch($pluginname) +{ + case 'mod_panoptosubmission': + $ispanoptoplugin = true; + $pluginpath = '/mod/panoptosubmission/contentitem_return.php'; + break; + case 'atto_panoptoltibutton': + $ispanoptoplugin = true; + $pluginpath = '/lib/editor/atto/plugins/panoptoltibutton/contentitem_return.php'; + break; + case 'mod_panoptocourseembed': + $ispanoptoplugin = true; + $pluginpath = '/mod/panoptocourseembed/contentitem_return.php'; + break; + default: + $ispanoptoplugin = false; + break; +} + +if (!$ispanoptoplugin) { + redirect($CFG->wwwroot . '/mod/lti/auth.php' . + "?client_id=$clientid" . + "&response_type=$responsetype" . + "&response_mode=$responsemode" . + '&redirect_uri=' . urlencode($redirecturi) . + "&scope=$scope" . + '&state=' . urlencode($state) . + '&nonce=' . urlencode($nonce) . + "&login_hint=$loginhint" . + "&prompt=$prompt" . + "<i_message_hint=$ltimessagehint" + ); +} + +$ok = !empty($scope) && !empty($responsetype) && !empty($clientid) && + !empty($redirecturi) && !empty($loginhint) && + !empty($nonce) && !empty($SESSION->lti_message_hint); + +if (!$ok) { + $error = 'invalid_request'; +} +if ($ok && ($scope !== 'openid')) { + $ok = false; + $error = 'invalid_scope'; +} +if ($ok && ($responsetype !== 'id_token')) { + $ok = false; + $error = 'unsupported_response_type'; +} +if ($ok) { + list($courseid, $typeid, $id, $titleb64, $textb64) = explode(',', $SESSION->lti_message_hint, 5); + + $ok = true; + if (!$ok) { + $error = 'invalid_request'; + } else { + $config = lti_get_type_type_config($typeid); + $ok = ($clientid === $config->lti_clientid); + if (!$ok) { + $error = 'unauthorized_client'; + } + } +} +if ($ok && ($loginhint !== $USER->id)) { + $ok = false; + $error = 'access_denied'; +} + +$context = null; +$course = null; + +if (empty($courseid)) { + $courseid = 1; + $course = $DB->get_record('course', array('id' => $courseid), '*', MUST_EXIST); +} else { + $context = context_course::instance($courseid); + $course = $DB->get_record('course', array('id' => $courseid), '*', MUST_EXIST); + $PAGE->set_context($context); + $PAGE->set_course($course); +} + +// If we're unable to load up config; we cannot trust the redirect uri for POSTing to. +if (empty($config)) { + throw new moodle_exception('invalidrequest', 'error'); +} else { + $uris = array_map("trim", explode("\n", $config->lti_redirectionuris)); + if (!in_array($redirecturi, $uris)) { + throw new moodle_exception('invalidrequest', 'error'); + } +} +if ($ok) { + if (isset($responsemode)) { + $ok = ($responsemode === 'form_post'); + if (!$ok) { + $error = 'invalid_request'; + $desc = 'Invalid response_mode'; + } + } else { + $ok = false; + $error = 'invalid_request'; + $desc = 'Missing response_mode'; + } +} +if ($ok && !empty($prompt) && ($prompt !== 'none')) { + $ok = false; + $error = 'invalid_request'; + $desc = 'Invalid prompt'; +} + +if ($ok) { + if ($toolid) { + $lti = new stdClass(); + $lti->id = $resourcelinkid; + $lti->typeid = $toolid; + $lti->launchcontainer = LTI_LAUNCH_CONTAINER_WINDOW; + $lti->toolurl = $contenturl; + $lti->custom = new stdClass(); + $lti->instructorcustomparameters = []; + $lti->debuglaunch = false; + $lti->course = $courseid; + if ($customdata) { + $decoded = json_decode($customdata, true); + + foreach ($decoded as $key => $value) { + $lti->custom->$key = $value; + } + } + + list($endpoint, $params) = panoptoblock_lti_utility::get_launch_data($lti, $nonce); + } else { + + require_login($course); + // Set the return URL. We send the launch container along to help us avoid frames-within-frames when the user returns. + $returnurlparams = [ + 'course' => $courseid, + 'id' => $typeid, + 'sesskey' => sesskey(), + 'callback' => $callback, + ]; + $returnurl = new \moodle_url($pluginpath, $returnurlparams); + + // Prepare the request. + $request = panoptoblock_lti_utility::build_content_item_selection_request($typeid, $course, $returnurl, '', '', + [], [], false, true, false, false, false, $nonce, $pluginname); + $endpoint = $request->url; + $params = $request->params; + } +} else { + $params['error'] = $error; + if (!empty($desc)) { + $params['error_description'] = $desc; + } +} +if (isset($state)) { + $params['state'] = $state; +} +$r = '
\n"; +if (!empty($params)) { + foreach ($params as $key => $value) { + $key = htmlspecialchars($key); + $value = htmlspecialchars($value); + $r .= " \n"; + } +} +$r .= "
\n"; +$r .= "\n"; +echo $r; diff --git a/lib/lti/panoptoblock_lti_utility.php b/lib/lti/panoptoblock_lti_utility.php new file mode 100644 index 0000000..95f9fd1 --- /dev/null +++ b/lib/lti/panoptoblock_lti_utility.php @@ -0,0 +1,748 @@ +. + +/** + * Panopto lti helper object. Contains info required for Panopto LTI tools to be used in text editors + * + * @package block_panopto + * @copyright Panopto 2021 + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class panoptoblock_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 targeting in moodle. + * @param array $requiredcustomparam - custom parameters array. + * @return int the id of the first matching tool + */ + public static function get_course_tool_id($courseid, $requiredcustomparam = '') { + global $DB, $CFG; + require_once($CFG->dirroot . '/mod/lti/locallib.php'); + + $ltitooltypes = $DB->get_records('lti_types', null, 'name'); + $targetservername = self::get_target_server_name($courseid); + + $idmatches = []; + foreach ($ltitooltypes as $type) { + $type->config = lti_get_config( + (object)[ + 'typeid' => $type->id, + ] + ); + + $config = lti_get_type_type_config($type->id); + $islti1p3 = $config->lti_ltiversion === LTI_VERSION_1P3; + + if (!empty($targetservername) && stripos($type->config['toolurl'], $targetservername) !== false && + $type->state == LTI_TOOL_STATE_CONFIGURED) { + $currentconfig = lti_get_type_config($type->id); + + if ($islti1p3 || (!empty($requiredcustomparam) && !empty($currentconfig['customparameters']) && + stripos($currentconfig['customparameters'], $requiredcustomparam) !== false)) { + // Append matches, so we can filter later. + $idmatches[] = ['id' => $type->id, 'islti1p3' => $islti1p3]; + } else if (empty($requiredcustomparam)) { + $idmatches[] = ['id' => $type->id, 'islti1p3' => $islti1p3]; + } + } + } + + foreach ($idmatches as $item) { + if ($item['islti1p3'] == 1) { + return $item['id']; + } + } + + return !empty($idmatches[0]['id']) ? $idmatches[0]['id'] : null; + } + + /** + * Get the tool url 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 targeting in moodle. + * @param array $requiredcustomparam - custom parameters array. + * @return string the tool url of the first matching tool + */ + public static function get_course_tool_url($courseid, $requiredcustomparam) { + global $DB, $CFG; + require_once($CFG->dirroot . '/mod/lti/locallib.php'); + + $ltitooltypes = $DB->get_records('lti_types', null, 'name'); + $targetservername = self::get_target_server_name($courseid); + + $urlmatches = []; + foreach ($ltitooltypes as $type) { + $type->config = lti_get_config( + (object)[ + 'typeid' => $type->id, + ] + ); + + $config = lti_get_type_type_config($type->id); + $islti1p3 = $config->lti_ltiversion === LTI_VERSION_1P3; + + if (!empty($targetservername) && stripos($type->config['toolurl'], $targetservername) !== false && + $type->state == LTI_TOOL_STATE_CONFIGURED) { + $currentconfig = lti_get_type_config($type->id); + + if ($islti1p3 || (!empty($currentconfig['customparameters']) && + strpos($currentconfig['customparameters'], $requiredcustomparam) !== false)) { + $urlmatches[] = ['url' => $type->config['toolurl'], 'islti1p3' => $islti1p3]; + } + } + } + + foreach ($urlmatches as $item) { + if ($item['islti1p3'] == 1) { + return $item['url']; + } + } + + return !empty($urlmatches[0]['url']) ? $urlmatches[0]['url'] : 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 + */ + public 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']; + } + + // Setup LTI 1.3 specific parameters. + $lti1p3params = new stdClass(); + $ltiversion1p3 = defined('LTI_VERSION_1P3') && ($ltiversion === LTI_VERSION_1P3); + + if (isset($tool->toolproxyid)) { + $toolproxy = lti_get_tool_proxy($tool->toolproxyid); + $key = $toolproxy->guid; + $secret = $toolproxy->secret; + + if ($ltiversion1p3) { + if (!empty($toolproxy->public_keyset_url)) { + $lti1p3params->lti_publickeyset = $toolproxy->public_keyset_url; + } + $lti1p3params->lti_keytype = LTI_JWK_KEYSET; + + if (!empty($toolproxy->initiatelogin)) { + $lti1p3params->lti_initiatelogin = $toolproxy->initiatelogin; + } + if (!empty($toolproxy->redirection_uris)) { + $lti1p3params->lti_redirectionuris = $toolproxy->redirection_uris; + } + } + } else { + $toolproxy = null; + if (!empty($instance->resourcekey)) { + $key = $instance->resourcekey; + } else if ($ltiversion1p3) { + $key = $tool->clientid; + if (!empty($typeconfig['publickeyset'])) { + $lti1p3params->lti_publickeyset = $typeconfig['publickeyset']; + } + $lti1p3params->lti_keytype = $typeconfig['keytype'] ?? LTI_JWK_KEYSET; + + if (!empty($typeconfig['initiatelogin'])) { + $lti1p3params->lti_initiatelogin = $typeconfig['initiatelogin']; + } + if (!empty($typeconfig['redirectionuris'])) { + $lti1p3params->lti_redirectionuris = $typeconfig['redirectionuris']; + } + } 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, (array)$lti1p3params, 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)) || $ltiversion1p3) { + + // 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')) { + 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); + } + + /** + * Get 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. + * This also returns latest LTI version first if there are multiple configured for the same server. + * + * @param int $courseid - the id of the course we are targeting in moodle. + * @return object $type - first matching tool + */ + public static function get_course_tool($courseid) { + global $DB, $CFG; + require_once($CFG->dirroot . '/mod/lti/locallib.php'); + + $ltitooltypes = $DB->get_records('lti_types', null, 'name'); + + $targetservername = null; + + $blockexists = $DB->get_record('block', array('name' => 'panopto'), 'name'); + if (!empty($blockexists)) { + $targetservername = $DB->get_field('block_panopto_foldermap', 'panopto_server', array('moodleid' => $courseid)); + } + + // If the course if not provisioned with the Panopto block then get the default panopto server fqdn. + if (empty($targetservername)) { + $targetservername = get_config('block_panopto', 'automatic_operation_target_server'); + } + + $idmatches = []; + foreach ($ltitooltypes as $type) { + $type->config = lti_get_config( + (object)[ + 'typeid' => $type->id, + ] + ); + + $config = lti_get_type_type_config($type->id); + $islti1p3 = $config->lti_ltiversion === LTI_VERSION_1P3; + + if (!empty($targetservername) && strpos($type->config['toolurl'], $targetservername) !== false && + $type->state == LTI_TOOL_STATE_CONFIGURED) { + $currentconfig = lti_get_type_config($type->id); + + if ($islti1p3 || (!empty($currentconfig['customparameters']) && + strpos($currentconfig['customparameters'], 'panopto_course_embed_tool') !== false)) { + // Append matches, so we can filter later. + $idmatches[] = ['type' => $type, 'islti1p3' => $islti1p3]; + } + } + } + + foreach ($idmatches as $item) { + if ($item['islti1p3'] == 1) { + return $item['type']; + } + } + + return !empty($idmatches[0]['type']) ? $idmatches[0]['type'] : null; + } + + /** + * Builds a standard LTI Content-Item selection request. + * + * @param int $id The tool type ID. + * @param stdClass $course The course object. + * @param moodle_url $returnurl The return URL in the tool consumer (TC) that the tool provider (TP) + * will use to return the Content-Item message. + * @param string $title The tool's title, if available. + * @param string $text The text to display to represent the content item. + * This value may be a long description of the content item. + * @param array $mediatypes Array of MIME types types supported by the TC. + * If empty, the TC will support ltilink by default. + * @param array $presentationtargets Array of ways in which the selected content item(s) + * can be requested to be opened (via the presentationDocumentTarget element for a returned content item). + * If empty, "frame", "iframe", and "window" will be supported by default. + * @param bool $autocreate Indicates whether any content items returned + * by the TP would be automatically persisted without. + * @param bool $multiple Indicates whether the user should be permitted to select more than one item. + * False by default. any option for the user to cancel the operation. False by default. + * @param bool $unsigned Indicates whether the TC is willing to accept an unsigned return message, or not. + * A signed message should always be required when + * the content item is being created automatically in the + * TC without further interaction from the user. False by default. + * @param bool $canconfirm Flag for can_confirm parameter. False by default. + * @param bool $copyadvice Indicates whether the TC is able and willing to make + * a local copy of a content item. False by default. + * @param string $nonce + * @param string $pluginname The name of the Panopto plug-in being used here. + * Some plug-ins need extra custom parameters to work properly. + * @return stdClass The object containing the signed request parameters + * and the URL to the TP's Content-Item selection interface. + * @throws moodle_exception When the LTI tool type does not exist.` + * @throws coding_exception For invalid media type and presentation target parameters. + */ + public static function build_content_item_selection_request($id, $course, moodle_url $returnurl, $title = '', $text = '', $mediatypes = [], + $presentationtargets = [], $autocreate = false, $multiple = true, + $unsigned = false, $canconfirm = false, $copyadvice = false, $nonce = '', $pluginname = '') { + global $USER, $CFG; + require_once($CFG->dirroot . '/mod/lti/locallib.php'); + + $tool = lti_get_type($id); + // Validate parameters. + if (!$tool) { + throw new moodle_exception('errortooltypenotfound', 'mod_lti'); + } + if (!is_array($mediatypes)) { + throw new coding_exception('The list of accepted media types should be in an array'); + } + if (!is_array($presentationtargets)) { + throw new coding_exception('The list of accepted presentation targets should be in an array'); + } + + // Check title. If empty, use the tool's name. + if (empty($title)) { + $title = $tool->name; + } + + $typeconfig = lti_get_type_config($id); + $key = ''; + $secret = ''; + $islti2 = false; + $islti13 = false; + if (isset($tool->toolproxyid)) { + $islti2 = true; + $toolproxy = lti_get_tool_proxy($tool->toolproxyid); + $key = $toolproxy->guid; + $secret = $toolproxy->secret; + } else { + $islti13 = $tool->ltiversion === LTI_VERSION_1P3; + $toolproxy = null; + if ($islti13 && !empty($tool->clientid)) { + $key = $tool->clientid; + } else if (!$islti13 && !empty($typeconfig['resourcekey'])) { + $key = $typeconfig['resourcekey']; + } + if (!empty($typeconfig['password'])) { + $secret = $typeconfig['password']; + } + } + $tool->enabledcapability = ''; + if (!empty($typeconfig['enabledcapability_ContentItemSelectionRequest'])) { + $tool->enabledcapability = $typeconfig['enabledcapability_ContentItemSelectionRequest']; + } + + $tool->parameter = ''; + if (!empty($typeconfig['parameter_ContentItemSelectionRequest'])) { + $tool->parameter = $typeconfig['parameter_ContentItemSelectionRequest']; + } + + // Set the tool URL. + if (!empty($typeconfig['toolurl_ContentItemSelectionRequest'])) { + $toolurl = new moodle_url($typeconfig['toolurl_ContentItemSelectionRequest']); + } else { + $toolurl = new moodle_url($typeconfig['toolurl']); + } + + // Check if SSL is forced. + if (!empty($typeconfig['forcessl'])) { + // Make sure the tool URL is set to https. + if (strtolower($toolurl->get_scheme()) === 'http') { + $toolurl->set_scheme('https'); + } + // Make sure the return URL is set to https. + if (strtolower($returnurl->get_scheme()) === 'http') { + $returnurl->set_scheme('https'); + } + } + $toolurlout = $toolurl->out(false); + + // Get base request parameters. + $instance = new stdClass(); + $instance->course = $course->id; + $requestparams = lti_build_request($instance, $typeconfig, $course, $id, $islti2); + + // Get LTI2-specific request parameters and merge to the request parameters if applicable. + if ($islti2) { + $lti2params = lti_build_request_lti2($tool, $requestparams); + $requestparams = array_merge($requestparams, $lti2params); + } + + // Get standard request parameters and merge to the request parameters. + $orgid = lti_get_organizationid($typeconfig); + $standardparams = lti_build_standard_message(null, $orgid, $tool->ltiversion, 'ContentItemSelectionRequest'); + $requestparams = array_merge($requestparams, $standardparams); + + // Get custom request parameters and merge to the request parameters. + $customstr = ''; + if (!empty($typeconfig['customparameters'])) { + $customstr = $typeconfig['customparameters']; + } + + switch($pluginname) { + // We need to add the custom parameter that initiates the student submission behavior here. + case 'mod_panoptosubmission': + $submissioncustomparam = "panopto_assignment_submission_content_item=true\npanopto_student_submission_tool=true"; + if (empty($customstr)) { + $customstr = $submissioncustomparam; + } else { + $customstr .= "\n" . $submissioncustomparam; + } + break; + default: + break; + } + + $customparams = lti_build_custom_parameters($toolproxy, $tool, $instance, $requestparams, $customstr, '', $islti2); + $requestparams = array_merge($requestparams, $customparams); + + // Add the parameters configured by the LTI services. + if ($id && !$islti2) { + $services = lti_get_services(); + foreach ($services as $service) { + $serviceparameters = $service->get_launch_parameters('ContentItemSelectionRequest', + $course->id, $USER->id , $id); + 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', [$instance, $toolurlout, $requestparams], []); + + if (!empty($pluginparams) && is_array($pluginparams)) { + $requestparams = array_merge($requestparams, $pluginparams); + } + } + + if (!$islti13) { + // Media types. Set to ltilink by default if empty. + if (empty($mediatypes)) { + $mediatypes = [ + 'application/vnd.ims.lti.v1.ltilink', + ]; + } + $requestparams['accept_media_types'] = implode(',', $mediatypes); + } else { + // Only LTI links are currently supported. + $requestparams['accept_types'] = 'ltiResourceLink'; + } + + // Presentation targets. Supports frame, iframe, window by default if empty. + if (empty($presentationtargets)) { + $presentationtargets = [ + 'frame', + 'iframe', + 'window', + ]; + } + $requestparams['accept_presentation_document_targets'] = implode(',', $presentationtargets); + + // Other request parameters. + $requestparams['accept_copy_advice'] = $copyadvice === true ? 'true' : 'false'; + $requestparams['accept_multiple'] = $multiple === true ? 'true' : 'false'; + $requestparams['accept_unsigned'] = $unsigned === true ? 'true' : 'false'; + $requestparams['auto_create'] = $autocreate === true ? 'true' : 'false'; + $requestparams['can_confirm'] = $canconfirm === true ? 'true' : 'false'; + $requestparams['content_item_return_url'] = $returnurl->out(false); + $requestparams['title'] = $title; + $requestparams['text'] = $text; + if (!$islti13) { + $signedparams = lti_sign_parameters($requestparams, $toolurlout, 'POST', $key, $secret); + } else { + $signedparams = lti_sign_jwt($requestparams, $toolurlout, $key, $id, $nonce); + } + $toolurlparams = $toolurl->params(); + + // Strip querystring params in endpoint url from $signedparams to avoid duplication. + if (!empty($toolurlparams) && !empty($signedparams)) { + foreach (array_keys($toolurlparams) as $paramname) { + if (isset($signedparams[$paramname])) { + unset($signedparams[$paramname]); + } + } + } + + // Check for params that should not be passed. Unset if they are set. + $unwantedparams = [ + 'resource_link_id', + 'resource_link_title', + 'resource_link_description', + 'launch_presentation_return_url', + 'lis_result_sourcedid', + ]; + foreach ($unwantedparams as $param) { + if (isset($signedparams[$param])) { + unset($signedparams[$param]); + } + } + + // Prepare result object. + $result = new stdClass(); + $result->params = $signedparams; + $result->url = $toolurlout; + + return $result; + } + + /** + * 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'); + } + + /** + * Returns target server name either if block exists or from course embed settings. + * + * @param string $courseid + * @return string + */ + private static function get_target_server_name($courseid) { + global $DB, $CFG; + require_once($CFG->dirroot . '/mod/lti/locallib.php'); + + $targetservername = null; + + $blockexists = $DB->get_record('block', array('name' => 'panopto'), 'name'); + if (!empty($blockexists)) { + $targetservername = $DB->get_field('block_panopto_foldermap', 'panopto_server', array('moodleid' => $courseid)); + } + + // If the course if not provisioned with the Panopto block then get the default panopto server fqdn. + if (empty($targetservername)) { + $targetservername = get_config('mod_panoptocourseembed', 'default_panopto_server'); + } + + return $targetservername; + } +} diff --git a/lib/panopto_auth_soap_client.php b/lib/panopto_auth_soap_client.php index 0c640d4..442fc66 100644 --- a/lib/panopto_auth_soap_client.php +++ b/lib/panopto_auth_soap_client.php @@ -24,14 +24,7 @@ */ // This can't be defined Moodle internal because it is called from Panopto to authorize login. - -/** - * The auth soap client for Panopto - * - * @copyright Panopto 2009 - 2016 with contributions from Spenser Jones (sjones@ambrose.edu), - * Skylar Kelty - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ +// @codingStandardsIgnoreLine require_once(dirname(__FILE__) . '/AuthManagement/AuthManagementAutoload.php'); require_once(dirname(__FILE__) . '/panopto_data.php'); require_once(dirname(__FILE__) . '/block_panopto_lib.php'); diff --git a/lib/panopto_category_data.php b/lib/panopto_category_data.php index 943fbfc..8bafe14 100644 --- a/lib/panopto_category_data.php +++ b/lib/panopto_category_data.php @@ -23,11 +23,6 @@ */ defined('MOODLE_INTERNAL') || die(); -global $CFG; -if (empty($CFG)) { - require_once(dirname(__FILE__) . '/../../config.php'); -} - require_once($CFG->libdir . '/dmllib.php'); require_once(dirname(__FILE__) . '/block_panopto_lib.php'); require_once(dirname(__FILE__) . '/panopto_session_soap_client.php'); @@ -200,6 +195,7 @@ public static function get_panopto_servername($moodlecategoryid) { * @param object $leafcoursedata course data */ public function ensure_category_branch($usehtmloutput, $leafcoursedata) { + require_once(dirname(dirname(dirname(dirname(__FILE__)))) . '/filter/multilang/filter.php'); global $DB; if (!$this->hasvalidpanoptoversion) { @@ -219,8 +215,13 @@ public function ensure_category_branch($usehtmloutput, $leafcoursedata) { $targetcategory = $DB->get_record('course_categories', array('id' => $this->moodlecategoryid)); + // Check if multilanguage categories are being used. + // If they are being used strip html from category name, and take first. If not just return category/folder name. + $multilanguagefilter = new filter_multilang('', array()); + $cleancategoryname = $multilanguagefilter->filter($targetcategory->name); + // Some users have categories with no name, so default it to id. - $targetcategoryname = !empty(trim($targetcategory->name)) ? $targetcategory->name : $targetcategory->id; + $targetcategoryname = !empty(trim($cleancategoryname)) ? $cleancategoryname : $targetcategory->id; $branchinfo = [ 'targetserver' => $this->servername, diff --git a/lib/panopto_data.php b/lib/panopto_data.php index e139c5c..78206cf 100644 --- a/lib/panopto_data.php +++ b/lib/panopto_data.php @@ -23,11 +23,6 @@ */ defined('MOODLE_INTERNAL') || die(); -global $CFG; -if (empty($CFG)) { - require_once('../../config.php'); -} - require_once($CFG->libdir . '/clilib.php'); require_once($CFG->libdir . '/dmllib.php'); require_once($CFG->libdir .'/filelib.php'); @@ -230,9 +225,10 @@ public function can_user_provision($courseid) { // Get the context of the course so we can get capabilities. $context = context_course::instance($courseid, MUST_EXIST); - return has_capability('block/panopto:provision_aspublisher', $context, $USER->id) || + return (has_capability('block/panopto:provision_aspublisher', $context, $USER->id) || has_capability('block/panopto:provision_asteacher', $context, $USER->id) || - has_capability('moodle/course:update', $context, $USER->id); + has_capability('moodle/course:update', $context, $USER->id)) && + has_capability('block/panopto:provision_course', $context, $USER->id); } /** @@ -415,7 +411,6 @@ public function provision_course($provisioninginfo, $skipusersync) { } if (!$skipusersync && $this->uname !== 'guest') { - // Uname will be guest is provisioning/upgrading through cli, no need to sync this 'user'. // This is intended to make sure provisioning teachers get access without relogging, // so we only need to perform this if we aren't syncing all enrolled users. @@ -694,9 +689,9 @@ public function copy_panopto_content($originalcourseid) { if (empty($aspxauthcookie)) { $importresult = new stdClass; $importresult->importedcourseid = $originalcourseid; - $importresult->errormessage = get_string('copy_api_auth_error', 'block_panopto', $this->servername); + $importresult->errormessage = get_string('copy_api_error_auth', 'block_panopto', $this->servername); $importresults[] = $importresult; - self::print_log(get_string('copy_api_auth_error', 'block_panopto', $importresult)); + self::print_log(get_string('copy_api_error_auth', 'block_panopto', $importresult)); return $importresults; } @@ -1503,7 +1498,7 @@ public function get_course_options() { // Only add a folder to the course options if it is not already mapped to a course on moodle. // Unless its the current course. - if (!$DB->get_records('block_panopto_foldermap', array('panopto_id' => $folderinfo->Id)) + if (!$DB->get_records('block_panopto_foldermap', array('panopto_id' => $folderinfo->Id)) || ($this->sessiongroupid === $folderinfo->Id)) { if ($this->sessiongroupid === $folderinfo->Id) { @@ -1715,7 +1710,7 @@ public static function print_log($logmessage) { FILE_APPEND ); } else { - error_log($logmessage); + debugging($logmessage); // These flush's are needed for longer processes like the Moodle upgrade process and import process. diff --git a/lib/panopto_session_soap_client.php b/lib/panopto_session_soap_client.php index 9af4e67..1c6997e 100644 --- a/lib/panopto_session_soap_client.php +++ b/lib/panopto_session_soap_client.php @@ -22,13 +22,6 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -/** - * The user soap client for Panopto - * - * @copyright Panopto 2009 - 2016 - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - defined('MOODLE_INTERNAL') || die(); require_once(dirname(__FILE__) . '/SessionManagement/SessionManagementAutoload.php'); @@ -96,8 +89,9 @@ public function __construct($servername, $apiuseruserkey, $apiuserauthcode) { $apiuseruserkey ); - $this->serviceparams = - panopto_generate_wsdl_service_params('https://'. $servername . '/Panopto/PublicAPI/4.6/SessionManagement.svc?singlewsdl'); + $this->serviceparams = panopto_generate_wsdl_service_params( + 'https://'. $servername . '/Panopto/PublicAPI/4.6/SessionManagement.svc?singlewsdl' + ); } /** diff --git a/lib/panopto_timeout_soap_client.php b/lib/panopto_timeout_soap_client.php index fbd090f..1d24b11 100644 --- a/lib/panopto_timeout_soap_client.php +++ b/lib/panopto_timeout_soap_client.php @@ -23,7 +23,7 @@ */ // This can't be defined Moodle internal because it is called from Panopto to authorize login. - +// @codingStandardsIgnoreStart /** * Panopto timeout soap client class. * @@ -186,5 +186,5 @@ public function __doRequest($request, $location, $action, $version, $one_way = f } } } - +// @codingStandardsIgnoreEnd /* End of file panopto_timeout_soap_client.php */ diff --git a/lib/panopto_user_soap_client.php b/lib/panopto_user_soap_client.php index 0d91ac0..cc896c1 100644 --- a/lib/panopto_user_soap_client.php +++ b/lib/panopto_user_soap_client.php @@ -84,8 +84,9 @@ public function __construct($servername, $apiuseruserkey, $apiuserauthcode) { null, $apiuseruserkey); - $this->serviceparams = - panopto_generate_wsdl_service_params('https://'. $servername . '/Panopto/PublicAPI/4.6/UserManagement.svc?singlewsdl'); + $this->serviceparams = panopto_generate_wsdl_service_params( + 'https://'. $servername . '/Panopto/PublicAPI/4.6/UserManagement.svc?singlewsdl' + ); // We need to make sure the UpdateContactInfo call succeeded so we need to ensure SOAP_WAIT_ONE_WAY_CALLS is set. $this->serviceparams['wsdl_features'] = SOAP_WAIT_ONE_WAY_CALLS | SOAP_SINGLE_ELEMENT_ARRAYS | SOAP_USE_XSI_ARRAY_TYPE; diff --git a/lib/panoptoblock_lti_utility.php b/lib/panoptoblock_lti_utility.php deleted file mode 100644 index 67be65b..0000000 --- a/lib/panoptoblock_lti_utility.php +++ /dev/null @@ -1,72 +0,0 @@ -. - -/** - * Panopto lti helper object. Contains info required for Panopto LTI tools to be used in text editors - * - * @package block_panopto - * @copyright Panopto 2021 - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class panoptoblock_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 targeting in moodle. - * @return int the id of the first matching tool - */ - public static function get_course_tool($courseid) { - global $DB; - require_once(dirname(dirname(dirname(dirname(__FILE__)))) . '/mod/lti/locallib.php'); - - $ltitooltypes = $DB->get_records('lti_types', null, 'name'); - - $targetservername = null; - - $blockexists = $DB->get_record('block', array('name' => 'panopto'), 'name'); - if (!empty($blockexists)) { - $targetservername = $DB->get_field('block_panopto_foldermap', 'panopto_server', array('moodleid' => $courseid)); - } - - // If the course if not provisioned with the Panopto block then get the default panopto server fqdn. - if (empty($targetservername)) { - $targetservername = get_config('block_panopto', 'automatic_operation_target_server'); - } - - $tooltypes = []; - foreach ($ltitooltypes as $type) { - $type->config = lti_get_config( - (object)[ - 'typeid' => $type->id, - ] - ); - - if (!empty($targetservername) && strpos($type->config['toolurl'], $targetservername) !== false && - $type->state == LTI_TOOL_STATE_CONFIGURED) { - $currentconfig = lti_get_type_config($type->id); - - if (!empty($currentconfig['customparameters']) && - strpos($currentconfig['customparameters'], 'panopto_course_embed_tool') !== false) { - return $type; - } - } - } - - return null; - } -} diff --git a/panopto_content.php b/panopto_content.php index 0afebd1..e8a3a00 100644 --- a/panopto_content.php +++ b/panopto_content.php @@ -23,6 +23,7 @@ */ define('AJAX_SCRIPT', true); +define('READ_ONLY_SESSION', true); require_once(dirname(__FILE__) . '/../../config.php'); require_once(dirname(__FILE__) . '/lib/panopto_data.php'); @@ -35,10 +36,8 @@ header('Content-Type: text/html; charset=utf-8'); global $CFG, $USER; + $CFG->enable_read_only_sessions = true; $content = new stdClass; - - // Close the session so that the users other tabs in the same session are not blocked. - \core\session\manager::write_close(); $content->text = ''; // Construct the Panopto data proxy object. diff --git a/provision_course.php b/provision_course.php index f623fc7..cd910a7 100644 --- a/provision_course.php +++ b/provision_course.php @@ -21,11 +21,7 @@ * @copyright Panopto 2009 - 2016 /With contributions from Spenser Jones (sjones@ambrose.edu) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -global $CFG; -if (empty($CFG)) { - require_once(dirname(__FILE__) . '/../../config.php'); -} - +require_once(dirname(__FILE__) . '/../../config.php'); require_once($CFG->libdir . '/formslib.php'); require_once(dirname(__FILE__) . '/classes/panopto_provision_form.php'); require_once(dirname(__FILE__) . '/lib/panopto_data.php'); diff --git a/provision_course_internal.php b/provision_course_internal.php index a29777c..23bc385 100644 --- a/provision_course_internal.php +++ b/provision_course_internal.php @@ -22,10 +22,7 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -global $CFG; -if (empty($CFG)) { - require_once(dirname(__FILE__) . '/../../config.php'); -} +require_once(dirname(__FILE__) . '/../../config.php'); require_once($CFG->libdir . '/formslib.php'); require_once(dirname(__FILE__) . '/classes/panopto_provision_course_form.php'); require_once(dirname(__FILE__) . '/lib/block_panopto_lib.php'); diff --git a/reinitialize_imports.php b/reinitialize_imports.php index add094d..45b6ec1 100644 --- a/reinitialize_imports.php +++ b/reinitialize_imports.php @@ -14,21 +14,24 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . -global $CFG; -if (empty($CFG)) { - require_once(dirname(__FILE__) . '/../../config.php'); -} - -require_once($CFG->libdir . '/formslib.php'); -require_once(dirname(__FILE__) . '/lib/panopto_data.php'); - /** * The reinitialize imports logic for Panopto * - * @package block_panopto + * @package block_panopto * @copyright Panopto 2020 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ +require_once(dirname(__FILE__) . '/../../config.php'); +require_once(dirname(__FILE__) . '/lib/panopto_data.php'); +require_once($CFG->libdir . '/formslib.php'); + +/** + * The reinitialize imports form. + * + * @package block_panopto + * @copyright Panopto 2020 + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ class panopto_reinitialize_imports_form extends moodleform { /** @@ -51,7 +54,6 @@ public function definition() { $this->add_action_buttons(true, get_string('begin_reinitializing_imports', 'block_panopto')); } - } require_login(); @@ -59,18 +61,24 @@ public function definition() { /** * Panopto reinitialize. * + * @package block_panopto + * @copyright Panopto 2020 + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ abstract class panopto_reinitialize { + /** + * @var string No course exists. + */ const NO_COURSE_EXISTS = 'NO_COURSE_EXISTS'; + /** + * @var string Invalid data. + */ const INVALID_PANOPTO_DATA = 'INVALID_PANOPTO_DATA'; } /** * Reinitialize all imports. * - * @package block_panopto - * @copyright Panopto 2020 - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ function reinitialize_all_imports() { global $DB; diff --git a/rename_all_folders.php b/rename_all_folders.php index 42c5391..1ed36f0 100644 --- a/rename_all_folders.php +++ b/rename_all_folders.php @@ -21,11 +21,7 @@ * @copyright Panopto 2020 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -global $CFG; -if (empty($CFG)) { - require_once(dirname(__FILE__) . '/../../config.php'); -} - +require_once(dirname(__FILE__) . '/../../config.php'); require_once($CFG->libdir . '/formslib.php'); require_once(dirname(__FILE__) . '/classes/panopto_rename_all_folders_form.php'); require_once(dirname(__FILE__) . '/lib/panopto_data.php'); diff --git a/settings.php b/settings.php index e836872..97f1872 100644 --- a/settings.php +++ b/settings.php @@ -22,15 +22,10 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die; -global $CFG; -if (empty($CFG)) { - require_once(dirname(__FILE__) . '/../../config.php'); -} require_once(dirname(__FILE__) . '/classes/admin/trim_configtext.php'); require_once(dirname(__FILE__) . '/lib/panopto_data.php'); - $numservers = get_config('block_panopto', 'server_number'); $numservers = isset($numservers) ? $numservers : 0; diff --git a/styles.css b/styles.css index 81cd9f7..6c2b7a0 100644 --- a/styles.css +++ b/styles.css @@ -1,5 +1,5 @@ .block_panopto .panoptoProcessInformation { - border: solid 1px #C0C0C0; + border: solid 1px #c0c0c0; padding: 5px; margin-bottom: 20px; } @@ -47,7 +47,7 @@ } .block_panopto .listItemAlt { - background: #F0F0F0; + background: #f0f0f0; } .block_panopto .rssLink { @@ -77,7 +77,7 @@ .block_panopto .panopto-progress-bar-container .panopto-progress-bar { position: relative; border: 1px solid #ccc; - left: 0px; + left: 0; margin-bottom: 5px; width: 100%; height: 25px; diff --git a/unprovision_course.php b/unprovision_course.php index 2c5a099..58092cc 100644 --- a/unprovision_course.php +++ b/unprovision_course.php @@ -21,11 +21,7 @@ * @copyright Panopto 2020 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -global $CFG; -if (empty($CFG)) { - require_once(dirname(__FILE__) . '/../../config.php'); -} - +require_once(dirname(__FILE__) . '/../../config.php'); require_once($CFG->libdir . '/formslib.php'); require_once(dirname(__FILE__) . '/classes/panopto_unprovision_form.php'); require_once(dirname(__FILE__) . '/lib/panopto_data.php'); diff --git a/unprovision_course_internal.php b/unprovision_course_internal.php index 1718989..32533c1 100644 --- a/unprovision_course_internal.php +++ b/unprovision_course_internal.php @@ -22,10 +22,7 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -global $CFG; -if (empty($CFG)) { - require_once(dirname(__FILE__) . '/../../config.php'); -} +require_once(dirname(__FILE__) . '/../../config.php'); require_once($CFG->libdir . '/formslib.php'); require_once(dirname(__FILE__) . '/classes/panopto_unprovision_course_form.php'); require_once(dirname(__FILE__) . '/lib/block_panopto_lib.php'); @@ -33,7 +30,6 @@ global $courses; - $numservers = get_config('block_panopto', 'server_number'); $numservers = isset($numservers) ? $numservers : 0; diff --git a/upgrade_all_folders.php b/upgrade_all_folders.php index ebff01c..591b16d 100644 --- a/upgrade_all_folders.php +++ b/upgrade_all_folders.php @@ -21,11 +21,7 @@ * @copyright Panopto 2009 - 2017 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -global $CFG; -if (empty($CFG)) { - require_once(dirname(__FILE__) . '/../../config.php'); -} - +require_once(dirname(__FILE__) . '/../../config.php'); require_once($CFG->libdir . '/formslib.php'); require_once(dirname(__FILE__) . '/classes/panopto_upgrade_all_folders_form.php'); require_once(dirname(__FILE__) . '/lib/panopto_data.php'); diff --git a/version.php b/version.php index 66963ef..5b8ed46 100644 --- a/version.php +++ b/version.php @@ -29,7 +29,7 @@ // Plugin version should normally be the same as the internal version. // If an admin wants to install with an older version number, however, set that here. -$plugin->version = 2022090700; +$plugin->version = 2022122000; // Requires this Moodle version - 2.7. $plugin->requires = 2014051200; diff --git a/views/ensure_category_branch_start.html.php b/views/ensure_category_branch_start.html.php index 4b4254a..b9a4c30 100644 --- a/views/ensure_category_branch_start.html.php +++ b/views/ensure_category_branch_start.html.php @@ -41,3 +41,4 @@ +
diff --git a/views/provisioned_course.html.php b/views/provisioned_course.html.php index 72b0a77..7d758b7 100644 --- a/views/provisioned_course.html.php +++ b/views/provisioned_course.html.php @@ -27,102 +27,102 @@
- errormessage)) { - ?> -
- errormessage ?> -
-
-
-
moodlecourseid ?>
-
-
servername ?>
+ errormessage)) { + ?> +
+ errormessage ?> +
+
+
+
moodlecourseid ?>
+
+
servername ?>
+ accesserror) && $provisioneddata->accesserror === true) { + ?> +
+ +
+
+
+
moodlecourseid ?>
+
+
servername ?>
+ unknownerror) && $provisioneddata->unknownerror === true) { + ?> +
+ +
+
+
+
moodlecourseid ?>
+
+
servername ?>
+ +
+
fullname ?>
+ +
+ +
+ + +
+ + +
+ +
+
+
accesserror) && $provisioneddata->accesserror === true) { - ?> -
- + if (!empty($provisioneddata->publishers)) { + echo join(', ', $provisioneddata->publishers); + } else { + ?>
-
-
-
moodlecourseid ?>
-
-
servername ?>
+
+
unknownerror) && $provisioneddata->unknownerror === true) { - ?> -
- + if (!empty($provisioneddata->creators)) { + echo join(', ', $provisioneddata->creators); + } else { + ?>
-
-
-
moodlecourseid ?>
-
-
servername ?>
+
+
-
-
fullname ?>
- -
- -
- - -
- - -
- -
-
-
- publishers)) { - echo join(', ', $provisioneddata->publishers); - } else { - ?>
-
-
-
- creators)) { - echo join(', ', $provisioneddata->creators); - } else { - ?>
-
-
-
- viewers)) { - echo join(', ', $provisioneddata->viewers); - } else { - ?>
-
- -
-
-
- Id) ?> -
+ if (!empty($provisioneddata->viewers)) { + echo join(', ', $provisioneddata->viewers); + } else { + ?>
- -
+ +
+
+
+ Id) ?> +
+
+
+