diff --git a/lang/en/moodle.php b/lang/en/moodle.php index 430892c4a8b..68bff3befee 100644 --- a/lang/en/moodle.php +++ b/lang/en/moodle.php @@ -1369,20 +1369,6 @@ $string['moodlenet:configoauthservice'] = 'Select a MoodleNet OAuth 2 service to $string['moodlenet:eventresourceexported'] = 'Resource exported'; $string['moodlenet:outboundsettings'] = 'MoodleNet outbound settings'; $string['moodlenet:sharetomoodlenet'] = 'Share to MoodleNet'; -$string['moodlenet:go_to_moodlenet'] = 'Go to MoodleNet drafts'; -$string['moodlenet:share_notice'] = 'You are sharing this to MoodleNet as a {$a}'; -$string['moodlenet:share_fail_title'] = 'Something went wrong'; -$string['moodlenet:share_fail_text'] = 'There was an error sharing your content to MoodleNet.
Please try again later.'; -$string['moodlenet:share_fail_text_with_site_support'] = 'There was an error sharing your content to MoodleNet.
Please try again later or contact site support.'; -$string['moodlenet:share_success_title'] = 'Saved to MoodleNet drafts'; -$string['moodlenet:share_success_text'] = "Almost done! Visit your draft in MoodleNet to finish sharing your content."; -$string['moodlenet:share_to_moodlenet'] = 'Share to MoodleNet'; -$string['moodlenet:share_type_resource'] = 'resource'; -$string['moodlenet:sharing_status'] = 'Sharing to MoodleNet'; -$string['moodlenet:sharing_large_file'] = "Large files can take some time."; -$string['moodlenet:sharing_to'] = 'Sharing to: '; -$string['moodlenet:packagingandsending'] = 'Packaging your file and sending to MoodleNet...'; -$string['moodlenet:usernotconfigured'] = 'You do not have permission to share content to MoodleNet, or your account is incorrectly configured.'; $string['more'] = 'more'; $string['morehelp'] = 'More help'; $string['morehelpaboutmodule'] = 'More help about the {$a} activity'; diff --git a/lib/classes/external/moodlenet_send_activity.php b/lib/classes/external/moodlenet_send_activity.php new file mode 100644 index 00000000000..1d3ad4def09 --- /dev/null +++ b/lib/classes/external/moodlenet_send_activity.php @@ -0,0 +1,173 @@ +. + +namespace core\external; + +use context_course; +use core\http_client; +use core\moodlenet\activity_sender; +use core\moodlenet\moodlenet_client; +use core\moodlenet\utilities; +use core\oauth2\api; +use core_external\external_api; +use core_external\external_function_parameters; +use core_external\external_single_structure; +use core_external\external_value; +use core_external\external_warnings; +use moodle_url; + +/** + * The external API to send activity to MoodleNet. + * + * @package core + * @copyright 2023 Huong Nguyen + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class moodlenet_send_activity extends external_api { + + /** + * Describes the parameters for sending the activity. + * + * @return external_function_parameters + * @since Moodle 4.2 + */ + public static function execute_parameters(): external_function_parameters { + return new external_function_parameters([ + 'issuerid' => new external_value(PARAM_INT, 'OAuth 2 issuer ID', VALUE_REQUIRED), + 'courseid' => new external_value(PARAM_INT, 'Course ID', VALUE_REQUIRED), + 'cmid' => new external_value(PARAM_INT, 'Course module ID', VALUE_REQUIRED), + 'shareformat' => new external_value(PARAM_INT, 'Share format', VALUE_REQUIRED), + ]); + } + + /** + * External function to send the activity to MoodleNet. + * + * @param int $issuerid The MoodleNet OAuth 2 issuer ID + * @param int $courseid The course ID that contains the activity which being shared + * @param int $cmid The course module ID of the activity that being shared + * @param int $shareformat The share format being used, as defined by \core\moodlenet\activity_sender + * @return array + * @since Moodle 4.2 + */ + public static function execute(int $issuerid, int $courseid, int $cmid, int $shareformat): array { + global $CFG, $USER; + + [ + 'issuerid' => $issuerid, + 'courseid' => $courseid, + 'cmid' => $cmid, + 'shareformat' => $shareformat, + ] = self::validate_parameters(self::execute_parameters(), [ + 'issuerid' => $issuerid, + 'courseid' => $courseid, + 'cmid' => $cmid, + 'shareformat' => $shareformat, + ]); + + // Check capability. + $coursecontext = context_course::instance($courseid); + $usercanshare = utilities::can_user_share($coursecontext, $USER->id); + if (!$usercanshare) { + return self::return_errors($cmid, 'errorpermission', + get_string('nopermissions', 'error', get_string('moodlenet:sharetomoodlenet', 'moodle'))); + } + + // Check format. + if (!in_array($shareformat, [activity_sender::SHARE_FORMAT_BACKUP])) { + return self::return_errors($shareformat, 'errorinvalidformat', get_string('invalidparameter', 'debug')); + } + + // Get the issuer. + $issuer = api::get_issuer($issuerid); + // Validate the issuer and check if it is enabled or not. + if (!utilities::is_valid_instance($issuer)) { + return self::return_errors($issuerid, 'errorissuernotenabled', get_string('invalidparameter', 'debug')); + } + + // Get the OAuth Client. + if (!$oauthclient = api::get_user_oauth_client( + $issuer, + new moodle_url($CFG->wwwroot), + moodlenet_client::API_SCOPE_CREATE + )) { + return self::return_errors($issuerid, 'erroroauthclient', get_string('invalidparameter', 'debug')); + } + + // Check login state. + if (!$oauthclient->is_logged_in()) { + return self::return_errors($issuerid, 'erroroauthclient', get_string('moodlenet:issuerisnotauthorized', 'moodle')); + } + + // Get the HTTP Client. + $client = new http_client(); + + // Share activity. + $activitysender = new activity_sender($courseid, $cmid, $USER->id, $client, $oauthclient, $shareformat); + + try { + $result = $activitysender->share_activity(); + if (empty($result['drafturl'])) { + return self::return_errors($result['responsecode'], 'errorsendingactivity', + get_string('moodlenet:cannotconnecttoserver', 'moodle')); + } + } catch (\moodle_exception $e) { + return self::return_errors(0, 'errorsendingactivity', $e->getMessage()); + } + + return [ + 'status' => true, + 'resourceurl' => $result['drafturl'], + 'warnings' => [], + ]; + } + + /** + * Describes the data returned from the external function. + * + * @return external_single_structure + * @since Moodle 4.2 + */ + public static function execute_returns(): external_single_structure { + return new external_single_structure([ + 'status' => new external_value(PARAM_BOOL, 'Status: true if success'), + 'resourceurl' => new external_value(PARAM_URL, 'Resource URL from MoodleNet'), + 'warnings' => new external_warnings(), + ]); + } + + /** + * Handle return error. + * + * @param int $itemid Item id + * @param string $warningcode Warning code + * @param string $message Message + * @return array + */ + protected static function return_errors(int $itemid, string $warningcode, string $message): array { + $warnings[] = [ + 'item' => $itemid, + 'warningcode' => $warningcode, + 'message' => $message, + ]; + + return [ + 'status' => false, + 'resourceurl' => '', + 'warnings' => $warnings, + ]; + } +} diff --git a/lib/db/services.php b/lib/db/services.php index 3968eaf74ed..d3f46a37e4e 100644 --- a/lib/db/services.php +++ b/lib/db/services.php @@ -3042,6 +3042,12 @@ $functions = array( 'type' => 'write', 'ajax' => true, ], + 'core_moodlenet_send_activity' => [ + 'classname' => 'core\external\moodlenet_send_activity', + 'description' => 'Send activity to MoodleNet', + 'type' => 'read', + 'ajax' => true, + ], ); $services = array( diff --git a/lib/tests/external/moodlenet_send_activity_test.php b/lib/tests/external/moodlenet_send_activity_test.php new file mode 100644 index 00000000000..b7d25d786fb --- /dev/null +++ b/lib/tests/external/moodlenet_send_activity_test.php @@ -0,0 +1,114 @@ +. + +namespace core\external; + +use core\oauth2\api; +use core_external\external_api; +use externallib_advanced_testcase; + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; + +require_once($CFG->dirroot . '/lib/tests/moodlenet/helpers.php'); +require_once($CFG->dirroot . '/webservice/tests/helpers.php'); + +/** + * External functions test for moodlenet_send_activity. + * + * @package core + * @category test + * @copyright 2023 Huong Nguyen + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @coversDefaultClass \core\external\moodlenet_send_activity + */ +class moodlenet_send_activity_test extends externallib_advanced_testcase { + + /** + * Test the behaviour of moodlenet_send_activity(). + * + * @covers ::execute + */ + public function test_moodlenet_send_activity() { + global $CFG; + $this->resetAfterTest(); + $this->setAdminUser(); + + // Generate data. + $generator = $this->getDataGenerator(); + $course = $generator->create_course(); + $moduleinstance = $generator->create_module('assign', ['course' => $course->id]); + $user = $generator->create_user(); + $generator->enrol_user($user->id, $course->id, 'student'); + + // Create dummy issuer. + $issuer = \core\moodlenet\helpers::get_mock_issuer(0); + + // Test with the experimental flag off. + $result = moodlenet_send_activity::execute($issuer->get('id'), $course->id, $moduleinstance->cmid, 0); + $result = external_api::clean_returnvalue(moodlenet_send_activity::execute_returns(), $result); + $this->assertFalse($result['status']); + $this->assertNotEmpty($result['warnings']); + $this->assertEquals('errorissuernotenabled', $result['warnings'][0]['warningcode']); + + $CFG->enablesharingtomoodlenet = true; + + // Test with invalid format. + $result = moodlenet_send_activity::execute($issuer->get('id'), $course->id, $moduleinstance->cmid, 5); + $result = external_api::clean_returnvalue(moodlenet_send_activity::execute_returns(), $result); + $this->assertFalse($result['status']); + $this->assertNotEmpty($result['warnings']); + $this->assertEquals('errorinvalidformat', $result['warnings'][0]['warningcode']); + + // Test with the user does not have permission. + $this->setUser($user); + $result = moodlenet_send_activity::execute($issuer->get('id'), $course->id, $moduleinstance->cmid, 0); + $result = external_api::clean_returnvalue(moodlenet_send_activity::execute_returns(), $result); + $this->assertFalse($result['status']); + $this->assertNotEmpty($result['warnings']); + $this->assertEquals('errorpermission', $result['warnings'][0]['warningcode']); + + $this->setAdminUser(); + + // Test with the issuer is not enabled. + $result = moodlenet_send_activity::execute($issuer->get('id'), $course->id, $moduleinstance->cmid, 0); + $result = external_api::clean_returnvalue(moodlenet_send_activity::execute_returns(), $result); + $this->assertFalse($result['status']); + $this->assertNotEmpty($result['warnings']); + $this->assertEquals('errorissuernotenabled', $result['warnings'][0]['warningcode']); + + // Test with the issuer is enabled but not set in the MN Outbound setting. + $issuer->set('enabled', 1); + $irecord = $issuer->to_record(); + api::update_issuer($irecord); + $result = moodlenet_send_activity::execute($issuer->get('id'), $course->id, $moduleinstance->cmid, 0); + $result = external_api::clean_returnvalue(moodlenet_send_activity::execute_returns(), $result); + $this->assertFalse($result['status']); + $this->assertNotEmpty($result['warnings']); + $this->assertEquals('errorissuernotenabled', $result['warnings'][0]['warningcode']); + + set_config('oauthservice', $issuer->get('id'), 'moodlenet'); + // Test with the issuer not yet authorized. + $result = moodlenet_send_activity::execute($issuer->get('id'), $course->id, $moduleinstance->cmid, 0); + $result = external_api::clean_returnvalue(moodlenet_send_activity::execute_returns(), $result); + $this->assertFalse($result['status']); + $this->assertNotEmpty($result['warnings']); + $this->assertEquals('erroroauthclient', $result['warnings'][0]['warningcode']); + $this->assertEquals($issuer->get('id'), $result['warnings'][0]['item']); + $this->assertEquals(get_string('moodlenet:issuerisnotauthorized', 'moodle'), $result['warnings'][0]['message']); + } +} diff --git a/version.php b/version.php index 7bcc48cbbcc..9009dbf0d48 100644 --- a/version.php +++ b/version.php @@ -29,7 +29,7 @@ defined('MOODLE_INTERNAL') || die(); -$version = 2023041400.01; // YYYYMMDD = weekly release date of this DEV branch. +$version = 2023041400.02; // YYYYMMDD = weekly release date of this DEV branch. // RR = release increments - 00 in DEV branches. // .XX = incremental changes. $release = '4.2beta (Build: 20230414)'; // Human-friendly version name