1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 namespace core\moodlenet; 18 19 use core\event\moodlenet_resource_exported; 20 use core\oauth2\client; 21 use moodle_exception; 22 use stored_file; 23 24 /** 25 * API for sharing Moodle LMS courses to MoodleNet instances. 26 * 27 * @package core 28 * @copyright 2023 Safat Shahin <safat.shahin@moodle.com> 29 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 30 */ 31 class course_sender extends resource_sender { 32 33 /** 34 * @var \core\context\course|false The course context. 35 */ 36 protected \core\context\course|false $coursecontext; 37 38 /** 39 * Constructor for course sender. 40 * 41 * @param int $courseid The course ID of the course being shared 42 * @param int $userid The user ID who is sharing the activity 43 * @param moodlenet_client $moodlenetclient The moodlenet_client object used to perform the share 44 * @param client $oauthclient The OAuth 2 client for the MoodleNet instance 45 * @param int $shareformat The data format to share in. Defaults to a Moodle backup (SHARE_FORMAT_BACKUP) 46 */ 47 public function __construct( 48 int $courseid, 49 protected int $userid, 50 protected moodlenet_client $moodlenetclient, 51 protected client $oauthclient, 52 protected int $shareformat = self::SHARE_FORMAT_BACKUP, 53 ) { 54 parent::__construct($courseid, $userid, $moodlenetclient, $oauthclient, $shareformat); 55 $this->course = get_course($courseid); 56 $this->coursecontext = \core\context\course::instance($courseid); 57 $this->packager = new course_packager($this->course, $this->userid); 58 } 59 60 /** 61 * Share a course to MoodleNet. 62 * 63 * @return array The HTTP response code from MoodleNet and the MoodleNet draft resource URL (URL empty string on fail). 64 * Format: ['responsecode' => 201, 'drafturl' => 'https://draft.mnurl/here'] 65 */ 66 public function share_resource(): array { 67 68 $accesstoken = ''; 69 $issuer = $this->oauthclient->get_issuer(); 70 71 // Check user can share to the requested MoodleNet instance. 72 $usercanshare = utilities::can_user_share($this->coursecontext, $this->userid, 'course'); 73 74 if ($usercanshare && utilities::is_valid_instance($issuer) && $this->oauthclient->is_logged_in()) { 75 $accesstoken = $this->oauthclient->get_accesstoken()->token; 76 } 77 78 // Throw an exception if the user is not currently set up to be able to share to MoodleNet. 79 if (!$accesstoken) { 80 throw new moodle_exception('moodlenet:usernotconfigured'); 81 } 82 83 // Attempt to prepare and send the resource if validation has passed and we have an OAuth 2 token. 84 85 // Prepare file in requested format. 86 $filedata = $this->prepare_share_contents(); 87 88 // Avoid sending a file larger than the defined limit. 89 $filesize = $filedata->get_filesize(); 90 if ($filesize > self::MAX_FILESIZE) { 91 $filedata->delete(); 92 throw new moodle_exception('moodlenet:sharefilesizelimitexceeded', 'core', '', [ 93 'filesize' => $filesize, 94 'filesizelimit' => self::MAX_FILESIZE, 95 ]); 96 } 97 98 // MoodleNet only accept plaintext descriptions. 99 $resourcedescription = $this->get_resource_description(); 100 101 $response = $this->moodlenetclient->create_resource_from_stored_file( 102 $filedata, 103 $this->course->fullname, 104 $resourcedescription, 105 ); 106 $responsecode = $response->getStatusCode(); 107 108 $responsebody = json_decode($response->getBody(), false, 512, JSON_THROW_ON_ERROR); 109 $resourceurl = $responsebody->homepage ?? ''; 110 111 // Delete the generated file now it is no longer required. 112 // (It has either been sent, or failed - retries not currently supported). 113 $filedata->delete(); 114 115 // Log every attempt to share (and whether it was successful). 116 $this->log_event($resourceurl, $responsecode); 117 118 return [ 119 'responsecode' => $responsecode, 120 'drafturl' => $resourceurl, 121 ]; 122 } 123 124 /** 125 * Log an event to the admin logs for an outbound share attempt. 126 * 127 * @param string $resourceurl The URL of the draft resource if it was created 128 * @param int $responsecode The HTTP response code describing the outcome of the attempt 129 * @return void 130 */ 131 protected function log_event( 132 string $resourceurl, 133 int $responsecode, 134 ): void { 135 $event = moodlenet_resource_exported::create([ 136 'context' => $this->coursecontext, 137 'other' => [ 138 'courseid' => [$this->course->id], 139 'resourceurl' => $resourceurl, 140 'success' => ($responsecode === 201), 141 ], 142 ]); 143 $event->trigger(); 144 } 145 146 /** 147 * Return the list of supported share formats. 148 * 149 * @return array Array of supported share format values. 150 */ 151 protected static function get_allowed_share_formats(): array { 152 return [ 153 self::SHARE_FORMAT_BACKUP, 154 ]; 155 } 156 157 /** 158 * Fetch the description for the resource being created, in a supported text format. 159 * 160 * @return string Converted course description. 161 */ 162 protected function get_resource_description(): string { 163 global $PAGE; 164 165 // We need to set the page context here because content_to_text and format_text will need the page context to work. 166 $PAGE->set_context($this->coursecontext); 167 168 $processeddescription = strip_tags($this->course->summary); 169 $processeddescription = content_to_text 170 ( 171 format_text( 172 $processeddescription, 173 $this->course->summaryformat, 174 ), 175 $this->course->summaryformat 176 ); 177 178 return $processeddescription; 179 } 180 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body