+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since 4.3
+ */
+
+import DefaultMutations from 'core_courseformat/local/courseeditor/mutations';
+import {getCurrentCourseEditor} from 'core_courseformat/courseeditor';
+import CourseActions from 'core_courseformat/local/content/actions';
+import {subscribe} from 'core/pubsub';
+import {handleModal} from 'core/moodlenet/send_resource';
+import MoodleNetEvents from 'core/moodlenet/events';
+
+class MoodleNetMutations extends DefaultMutations {
+
+ /**
+ * Share to MoodleNet.
+ *
+ * @param {StateManager} stateManager the current state manager
+ * @param {array} cmIds Course module ids.
+ */
+ shareToMoodleNet = async function(stateManager, cmIds) {
+ this.cmLock(stateManager, cmIds, true);
+ handleModal('partial', 'resource', cmIds);
+ this.cmLock(stateManager, cmIds, false);
+ subscribe(MoodleNetEvents.MOODLENET_SHARE_STARTED, () => {
+ // Only clear the selection if the user starts the sharing.
+ this.bulkReset(stateManager);
+ });
+ };
+}
+
+/**
+ * Initialize.
+ */
+export const init = () => {
+ const courseEditor = getCurrentCourseEditor();
+ courseEditor.addMutations(new MoodleNetMutations());
+ // Add direct mutation content actions.
+ CourseActions.addActions({
+ cmShareToMoodleNet: 'shareToMoodleNet'
+ });
+};
diff --git a/lib/amd/src/moodlenet/send_resource.js b/lib/amd/src/moodlenet/send_resource.js
index fff212152e9..a4ee3b1d554 100644
--- a/lib/amd/src/moodlenet/send_resource.js
+++ b/lib/amd/src/moodlenet/send_resource.js
@@ -23,15 +23,19 @@
*/
import Config from 'core/config';
-import {alert as displayAlert, addNotification, exception as displayException} from 'core/notification';
+import {addNotification, exception as displayException} from 'core/notification';
import {getString} from 'core/str';
import Prefetch from "core/prefetch";
import * as Templates from 'core/templates';
+import {publish} from 'core/pubsub';
import * as MoodleNetService from 'core/moodlenet/service';
import SendActivityModal from 'core/moodlenet/send_activity_modal';
+import * as MoodleNetAuthorize from 'core/moodlenet/authorize';
+import MoodleNetEvents from 'core/moodlenet/events';
const TYPE_ACTIVITY = "activity";
const TYPE_COURSE = "course";
+const TYPE_PARTIAL_COURSE = "partial";
let listenersRegistered = false;
let currentModal;
@@ -41,6 +45,7 @@ let courseId;
let resourceId;
let shareFormat;
let type;
+let selectedCmIds;
/**
* Handle send to MoodleNet.
@@ -49,7 +54,7 @@ let type;
* @param {int} resourceId The resource ID, it can be a course or an activity.
* @param {int} shareFormat The share format.
*/
-const sendToMoodleNet = (issuerId, resourceId, shareFormat) => {
+export const sendToMoodleNet = (issuerId, resourceId, shareFormat) => {
const $modal = currentModal.getModal();
const modal = $modal[0];
modal.querySelector('.modal-header').classList.remove('no-border');
@@ -63,6 +68,12 @@ const sendToMoodleNet = (issuerId, resourceId, shareFormat) => {
infoPromise = MoodleNetService.sendActivity(issuerId, resourceId, shareFormat);
} else if (type === TYPE_COURSE) {
infoPromise = MoodleNetService.sendCourse(issuerId, resourceId, shareFormat);
+ } else if (type === TYPE_PARTIAL_COURSE) {
+ if (selectedCmIds.length > 1) {
+ infoPromise = MoodleNetService.sendPartialCourse(issuerId, resourceId, selectedCmIds, shareFormat);
+ } else {
+ infoPromise = MoodleNetService.sendActivity(issuerId, selectedCmIds[0], shareFormat);
+ }
}
infoPromise.then(async(data) => {
const status = data.status;
@@ -94,61 +105,46 @@ const responseFromMoodleNet = (status, resourceUrl = '') => {
}
};
-/**
- * Handle authorization with MoodleNet server.
- *
- * @param {Number} issuerId The OAuth 2 issuer ID.
- * @return {promise}
- */
-const handleAuthorization = (issuerId) => {
- const windowsizewidth = 550;
- const windowsizeheight = 550;
-
- // Check if the user is authorized with MoodleNet or not.
- return MoodleNetService.authorizationCheck(issuerId, courseId).then((data) => {
- if (!data.status) {
- // Not yet authorized.
- // Declare moodleNetAuthorize variable, so we can call it later in the callback.
- window.moodleNetAuthorize = (error, errorDescription) => {
- // This will be called by the callback after the authorization is successful.
- if (error === '') {
- handleAuthorization(issuerId);
- } else if (error !== 'access_denied') {
- displayAlert(
- 'Authorization error',
- 'Error: ' + error + '
Error description: ' + errorDescription,
- 'Cancel'
- );
- }
- };
- // Open the login url of the OAuth 2 issuer for user to login into MoodleNet and authorize.
- return window.open(data.loginurl, 'moodlenet_auth',
- `location=0,status=0,width=${windowsizewidth},height=${windowsizeheight},scrollbars=yes`);
- } else {
- // Already authorized.
- return sendToMoodleNet(issuerId, resourceId, shareFormat);
- }
- }).catch(displayException);
-};
-
/**
* Render the modal to send resource to MoodleNet.
*
* @param {object} data The data of the resource to be shared.
* @param {string} shareType The type of the resource to be shared.
+ * @param {array} selectedActivities Selected activities.
*/
-const renderModal = async(data, shareType) => {
+const renderModal = async(data, shareType, selectedActivities) => {
if (data.status) {
siteSupportUrl = data.supportpageurl;
issuerId = data.issuerid;
- return SendActivityModal.create({
+ let modalConfig = {
templateContext: {
- activitytype: data.type,
- activityname: data.name,
- sharetype: await getString(`moodlenet:sharetype${shareType}`, 'moodle'),
- server: data.server,
+ 'activitytype': data.type,
+ 'activityname': data.name,
+ 'server': data.server,
}
- });
+ };
+ const sharetype = await getString('moodlenet:sharetype' + shareType, 'moodle');
+ if (selectedActivities.length > 0) {
+ selectedCmIds = selectedActivities;
+ }
+ if (selectedActivities.length > 1) {
+ modalConfig.templateContext.fullsharing = false;
+ modalConfig.templateContext.selectedactivitiesnotice = await getString('moodlenet:sharenoticepartialactivitynumber',
+ 'moodle', selectedActivities.length);
+ modalConfig.templateContext.sharenotice = await getString('moodlenet:sharenoticepartial',
+ 'moodle', sharetype);
+ } else {
+ modalConfig.templateContext.fullsharing = true;
+ if (type === TYPE_PARTIAL_COURSE && selectedActivities.length == 1) {
+ modalConfig.templateContext.sharenotice = await getString('moodlenet:sharenotice',
+ 'moodle', {'type': TYPE_ACTIVITY, 'sharetype': sharetype});
+ } else {
+ modalConfig.templateContext.sharenotice = await getString('moodlenet:sharenotice',
+ 'moodle', {'type': type, 'sharetype': sharetype});
+ }
+ }
+
+ return SendActivityModal.create(modalConfig);
} else {
return addNotification({
message: data.warnings[0].message,
@@ -157,6 +153,43 @@ const renderModal = async(data, shareType) => {
}
};
+/**
+ * Handle modal.
+ * @param {string} shareActionType Share action type.
+ * @param {string} shareType Share type.
+ * @param {array} selectedActivities Selected activities.
+ */
+export const handleModal = (shareActionType, shareType, selectedActivities = []) => {
+ const resourceId = Config.contextInstanceId;
+ type = shareActionType;
+
+ Promise.resolve(type)
+ .then((type) => {
+ if (type === TYPE_ACTIVITY) {
+ return MoodleNetService.getActivityInformation(resourceId);
+ } else if (type === TYPE_COURSE) {
+ return MoodleNetService.getCourseInformation(resourceId);
+ } else if (type === TYPE_PARTIAL_COURSE) {
+ if (selectedActivities.length > 1) {
+ // Selected more than one activity.
+ return MoodleNetService.getCourseInformation(resourceId);
+ } else {
+ // Select only one activity. Switch to activity mode.
+ return MoodleNetService.getActivityInformation(selectedActivities[0]);
+ }
+ }
+ throw new Error(`Unknown type ${type}`);
+ })
+ .then((data) => {
+ return renderModal(data, shareType, selectedActivities);
+ })
+ .then((modal) => {
+ currentModal = modal;
+ return currentModal;
+ })
+ .catch(displayException);
+};
+
/**
* Register events.
*/
@@ -168,31 +201,16 @@ const registerEventListeners = () => {
e.preventDefault();
type = shareAction.getAttribute('data-type');
const shareType = shareAction.getAttribute('data-sharetype');
- Promise.resolve(type)
- .then((type) => {
- if (type === TYPE_ACTIVITY) {
- return MoodleNetService.getActivityInformation(Config.contextInstanceId);
- } else if (type === TYPE_COURSE) {
- return MoodleNetService.getCourseInformation(Config.contextInstanceId);
- }
- throw new Error(`Unknown type ${type}`);
- })
- .then((data) => {
- return renderModal(data, shareType);
- })
- .then((modal) => {
- currentModal = modal;
- return currentModal;
- })
- .catch(displayException);
+ handleModal(shareAction.getAttribute('data-type'), shareType);
}
if (sendAction) {
e.preventDefault();
+ publish(MoodleNetEvents.MOODLENET_SHARE_STARTED, {});
courseId = Config.courseId;
resourceId = Config.contextInstanceId;
shareFormat = 0;
- handleAuthorization(issuerId);
+ MoodleNetAuthorize.handleAuthorization(issuerId, courseId, resourceId, shareFormat);
}
});
};
@@ -208,6 +226,8 @@ export const init = () => {
'core/moodlenet/send_activity_modal_packaging',
'core/moodlenet/send_activity_modal_done',
'core/moodlenet/send_activity_modal_footer_view',
+ 'core/moodlenet/send_activity_modal_footer_share',
+ 'core/moodlenet/send_course_modal_base',
]);
registerEventListeners();
listenersRegistered = true;
diff --git a/lib/amd/src/moodlenet/service.js b/lib/amd/src/moodlenet/service.js
index 9ad9cf69623..11dafb27e7c 100644
--- a/lib/amd/src/moodlenet/service.js
+++ b/lib/amd/src/moodlenet/service.js
@@ -101,6 +101,29 @@ export const sendActivity = (issuerId, cmId, shareFormat) => {
return Ajax.call([request])[0];
};
+/**
+ * Send the selected activities in a course to MoodleNet.
+ *
+ * @param {Integer} issuerId The OAuth 2 issuer ID.
+ * @param {Integer} courseId The course ID.
+ * @param {array} selectedCmIds Course module IDs in the course.
+ * @param {Integer} shareFormat The share format.
+ * @return {promise}
+ */
+export const sendPartialCourse = (issuerId, courseId, selectedCmIds, shareFormat) => {
+ const request = {
+ methodname: 'core_moodlenet_send_course',
+ args: {
+ issuerid: issuerId,
+ courseid: courseId,
+ shareformat: shareFormat,
+ cmids: selectedCmIds,
+ }
+ };
+
+ return Ajax.call([request])[0];
+};
+
/**
* Check if the user is already authorized with MoodleNet.
*
diff --git a/lib/classes/external/moodlenet_get_shared_course_info.php b/lib/classes/external/moodlenet_get_shared_course_info.php
index 42fc48d426f..656c9324d09 100644
--- a/lib/classes/external/moodlenet_get_shared_course_info.php
+++ b/lib/classes/external/moodlenet_get_shared_course_info.php
@@ -53,7 +53,7 @@ class moodlenet_get_shared_course_info extends external_api {
* @return array
*/
public static function execute(int $courseid): array {
- global $CFG, $USER;
+ global $USER;
[
'courseid' => $courseid
diff --git a/lib/classes/output/icon_system_fontawesome.php b/lib/classes/output/icon_system_fontawesome.php
index 99a05c93290..301e9a32798 100644
--- a/lib/classes/output/icon_system_fontawesome.php
+++ b/lib/classes/output/icon_system_fontawesome.php
@@ -324,6 +324,7 @@ class icon_system_fontawesome extends icon_system_font {
'core:i/section' => 'fa-folder-o',
'core:i/sendmessage' => 'fa-paper-plane',
'core:i/settings' => 'fa-cog',
+ 'core:i/share' => 'fa-share-square-o',
'core:i/show' => 'fa-eye-slash',
'core:i/siteevent' => 'fa-globe',
'core:i/star' => 'fa-star',
diff --git a/lib/navigationlib.php b/lib/navigationlib.php
index f28018ff829..cb9167444a8 100644
--- a/lib/navigationlib.php
+++ b/lib/navigationlib.php
@@ -4638,6 +4638,9 @@ class settings_navigation extends navigation_node {
}
// MoodleNet links.
+ if ($this->page->user_is_editing()) {
+ $this->page->requires->js_call_amd('core/moodlenet/mutations', 'init');
+ }
$usercanshare = utilities::can_user_share($coursecontext, $USER->id, 'course');
$issuerid = get_config('moodlenet', 'oauthservice');
try {
diff --git a/lib/templates/moodlenet/send_activity_modal_base.mustache b/lib/templates/moodlenet/send_activity_modal_base.mustache
index 8cba958ecad..44c8a8d67b2 100644
--- a/lib/templates/moodlenet/send_activity_modal_base.mustache
+++ b/lib/templates/moodlenet/send_activity_modal_base.mustache
@@ -45,8 +45,13 @@
- {{#pix}} e/help, core, {{#str}} moodlenet:sharenotice, moodle, {{sharetype}} {{/str}}{{/pix}} {{#str}} moodlenet:sharenotice, moodle, {{sharetype}} {{/str}}
+ {{sharenotice}}
+ {{^fullsharing}}
+
+ {{selectedactivitiesnotice}}
+
+ {{/fullsharing}}