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