Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.

Differences Between: [Versions 402 and 403]

   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 cm_info;
  20  use core\event\moodlenet_resource_exported;
  21  use core\oauth2\client;
  22  use moodle_exception;
  23  use stored_file;
  24  
  25  /**
  26   * API for sharing Moodle LMS activities to MoodleNet instances.
  27   *
  28   * @package   core
  29   * @copyright 2023 Michael Hawkins <michaelh@moodle.com>
  30   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  31   */
  32  class activity_sender extends resource_sender {
  33  
  34      /**
  35       * @var cm_info The context module info object for the activity being shared.
  36       */
  37      protected cm_info $cminfo;
  38  
  39      /**
  40       * Class constructor.
  41       *
  42       * @param int $cmid The course module ID of the activity being shared
  43       * @param int $userid The user ID who is sharing the activity
  44       * @param moodlenet_client $moodlenetclient The moodlenet_client object used to perform the share
  45       * @param client $oauthclient The OAuth 2 client for the MoodleNet instance
  46       * @param int $shareformat The data format to share in. Defaults to a Moodle backup (SHARE_FORMAT_BACKUP)
  47       */
  48      public function __construct(
  49          int $cmid,
  50          protected int $userid,
  51          protected moodlenet_client $moodlenetclient,
  52          protected client $oauthclient,
  53          protected int $shareformat = self::SHARE_FORMAT_BACKUP,
  54      ) {
  55          parent::__construct($cmid, $userid, $moodlenetclient, $oauthclient, $shareformat);
  56          [$this->course, $this->cminfo] = get_course_and_cm_from_cmid($cmid);
  57          $this->packager = new activity_packager($this->cminfo, $this->userid);
  58      }
  59  
  60      /**
  61       * Share an activity/resource 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       * @deprecated since Moodle 4.3
  66       * @todo Final deprecation MDL-79086
  67       */
  68      public function share_activity(): array {
  69          debugging('Method share_activity is deprecated, use share_resource instead.', DEBUG_DEVELOPER);
  70          return $this->share_resource();
  71      }
  72  
  73      /**
  74       * Share an activity/resource to MoodleNet.
  75       *
  76       * @return array The HTTP response code from MoodleNet and the MoodleNet draft resource URL (URL empty string on fail).
  77       *               Format: ['responsecode' => 201, 'drafturl' => 'https://draft.mnurl/here']
  78       */
  79      public function share_resource(): array {
  80          $accesstoken = '';
  81          $resourceurl = '';
  82          $issuer = $this->oauthclient->get_issuer();
  83  
  84          // Check user can share to the requested MoodleNet instance.
  85          $coursecontext = \core\context\course::instance($this->course->id);
  86          $usercanshare = utilities::can_user_share($coursecontext, $this->userid);
  87  
  88          if ($usercanshare && utilities::is_valid_instance($issuer) && $this->oauthclient->is_logged_in()) {
  89              $accesstoken = $this->oauthclient->get_accesstoken()->token;
  90          }
  91  
  92          // Throw an exception if the user is not currently set up to be able to share to MoodleNet.
  93          if (!$accesstoken) {
  94              throw new moodle_exception('moodlenet:usernotconfigured');
  95          }
  96  
  97          // Attempt to prepare and send the resource if validation has passed and we have an OAuth 2 token.
  98  
  99          // Prepare file in requested format.
 100          $filedata = $this->prepare_share_contents();
 101  
 102          // If we have successfully prepared a file to share of permitted size, share it to MoodleNet.
 103          if (!empty($filedata)) {
 104              // Avoid sending a file larger than the defined limit.
 105              $filesize = $filedata->get_filesize();
 106              if ($filesize > self::MAX_FILESIZE) {
 107                  $filedata->delete();
 108                  throw new moodle_exception('moodlenet:sharefilesizelimitexceeded', 'core', '', [
 109                      'filesize' => $filesize,
 110                      'filesizelimit' => self::MAX_FILESIZE,
 111                  ]);
 112              }
 113  
 114              // MoodleNet only accept plaintext descriptions.
 115              $resourcedescription = $this->get_resource_description($coursecontext);
 116  
 117              $response = $this->moodlenetclient->create_resource_from_stored_file(
 118                  $filedata,
 119                  $this->cminfo->name,
 120                  $resourcedescription,
 121              );
 122              $responsecode = $response->getStatusCode();
 123  
 124              $responsebody = json_decode($response->getBody());
 125              $resourceurl = $responsebody->homepage ?? '';
 126  
 127              // Delete the generated file now it is no longer required.
 128              // (It has either been sent, or failed - retries not currently supported).
 129              $filedata->delete();
 130          }
 131  
 132          // Log every attempt to share (and whether or not it was successful).
 133          $this->log_event($coursecontext, $this->cminfo->id, $resourceurl, $responsecode);
 134  
 135          return [
 136              'responsecode' => $responsecode,
 137              'drafturl' => $resourceurl,
 138          ];
 139      }
 140  
 141      /**
 142       * Log an event to the admin logs for an outbound share attempt.
 143       *
 144       * @param \context $coursecontext The course context being shared from.
 145       * @param int $cmid The CMID of the activity being shared.
 146       * @param string $resourceurl The URL of the draft resource if it was created.
 147       * @param int $responsecode The HTTP response code describing the outcome of the attempt.
 148       * @return void
 149       */
 150      protected function log_event(
 151          \core\context $coursecontext,
 152          int $cmid,
 153          string $resourceurl,
 154          int $responsecode,
 155      ): void {
 156          $event = moodlenet_resource_exported::create([
 157              'context' => $coursecontext,
 158              'other' => [
 159                  'cmids' => [$cmid],
 160                  'resourceurl' => $resourceurl,
 161                  'success' => ($responsecode == 201),
 162              ],
 163          ]);
 164          $event->trigger();
 165      }
 166  
 167      /**
 168       * Fetch the description for the resource being created, in a supported text format.
 169       *
 170       * @param \context $coursecontext The course context being shared from.
 171       * @return string Converted activity description.
 172       */
 173      protected function get_resource_description(
 174          \context $coursecontext,
 175      ): string {
 176          global $PAGE, $DB;
 177          // We need to set the page context here because content_to_text and format_text will need the page context to work.
 178          $PAGE->set_context($coursecontext);
 179  
 180          $intro = $DB->get_record($this->cminfo->modname, ['id' => $this->cminfo->instance], 'intro, introformat', MUST_EXIST);
 181          $processeddescription = strip_tags($intro->intro);
 182          $processeddescription = content_to_text(format_text(
 183              $processeddescription,
 184              $intro->introformat,
 185          ), $intro->introformat);
 186  
 187          return $processeddescription;
 188      }
 189  }