Search moodle.org's
Developer Documentation


  • Bug fixes for general core bugs in 2.8.x ended 9 November 2015 (12 months).
  • Bug fixes for security issues in 2.8.x ended 9 May 2016 (18 months).
  • minimum PHP 5.4.4 (always use latest PHP 5.4.x or 5.5.x on Windows - http://windows.php.net/download/), PHP 7 is NOT supported
  •    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  
      18  /**
      19   * This file contains the class definition for the mahara portfolio plugin
      20   *
      21   * @since Moodle 2.0
      22   * @package moodlecore
      23   * @subpackage portfolio
      24   * @copyright 2009 Penny Leach
      25   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
      26   */
      27  
      28  
      29  define('PORTFOLIO_MAHARA_ERR_NETWORKING_OFF', 'err_networkingoff');
      30  define('PORTFOLIO_MAHARA_ERR_NOHOSTS', 'err_nomnethosts');
      31  define('PORTFOLIO_MAHARA_ERR_INVALIDHOST', 'err_invalidhost');
      32  define('PORTFOLIO_MAHARA_ERR_NOMNETAUTH', 'err_nomnetauth');
      33  
      34  require_once($CFG->libdir . '/portfoliolib.php');
      35  require_once($CFG->libdir . '/portfolio/plugin.php');
      36  require_once($CFG->libdir . '/portfolio/exporter.php');
      37  require_once($CFG->dirroot . '/mnet/lib.php');
      38  
      39  define('PORTFOLIO_MAHARA_QUEUE', PORTFOLIO_TIME_HIGH);
      40  define('PORTFOLIO_MAHARA_IMMEDIATE', PORTFOLIO_TIME_MODERATE);
      41  
      42  class portfolio_plugin_mahara extends portfolio_plugin_pull_base {
      43  
      44      private $hosts; // used in the admin config form
      45      private $mnethost; // privately set during export from the admin config value (mnethostid)
      46      private $hostrecord; // the host record that corresponds to the peer
      47      private $token; // during-transfer token
      48      private $sendtype; // whatever mahara has said it can handle (immediate or queued)
      49      private $filesmanifest; // manifest of files to send to mahara (set during prepare_package and sent later)
      50      private $totalsize; // total size of all included files added together
      51      private $continueurl; // if we've been sent back a specific url to continue to (eg folder id)
      52  
      53      protected function init() {
      54          $this->mnet = get_mnet_environment();
      55      }
      56  
      57      public function __wakeup() {
      58          $this->mnet = get_mnet_environment();
      59      }
      60  
      61      public static function get_name() {
      62          return get_string('pluginname', 'portfolio_mahara');
      63      }
      64  
      65      public static function get_allowed_config() {
      66          return array('mnethostid', 'enableleap2a');
      67      }
      68  
      69      public function supported_formats() {
      70          if ($this->get_config('enableleap2a')) {
      71              return array(PORTFOLIO_FORMAT_FILE, PORTFOLIO_FORMAT_LEAP2A);
      72          }
      73          return array(PORTFOLIO_FORMAT_FILE);
      74      }
      75  
      76      public function expected_time($callertime) {
      77          if ($this->sendtype == PORTFOLIO_MAHARA_QUEUE) {
      78              return PORTFOLIO_TIME_FORCEQUEUE;
      79          }
      80          return $callertime;
      81      }
      82  
      83      public static function has_admin_config() {
      84          return true;
      85      }
      86  
      87      public static function admin_config_form(&$mform) {
      88          $strrequired = get_string('required');
      89          $hosts = self::get_mnet_hosts(); // this is called by sanity check but it's ok because it's cached
      90          foreach ($hosts as $host) {
      91              $hosts[$host->id] = $host->name;
      92          }
      93          $mform->addElement('select', 'mnethostid', get_string('mnethost', 'portfolio_mahara'), $hosts);
      94          $mform->addRule('mnethostid', $strrequired, 'required', null, 'client');
      95          $mform->setType('mnethostid', PARAM_INT);
      96          $mform->addElement('selectyesno', 'enableleap2a', get_string('enableleap2a', 'portfolio_mahara'));
      97          $mform->setType('enableleap2a', PARAM_BOOL);
      98      }
      99  
     100      public function instance_sanity_check() {
     101          // make sure the host record exists since we don't have referential integrity
     102          if (!is_enabled_auth('mnet')) {
     103              return PORTFOLIO_MAHARA_ERR_NOMNETAUTH;
     104          }
     105          try {
     106              $this->ensure_mnethost();
     107          }
     108          catch (portfolio_exception $e) {
     109              return PORTFOLIO_MAHARA_ERR_INVALIDHOST;
     110          }
     111          // make sure we have the right services
     112          $hosts = $this->get_mnet_hosts();
     113          if (!array_key_exists($this->get_config('mnethostid'), $hosts)) {
     114              return PORTFOLIO_MAHARA_ERR_INVALIDHOST;
     115          }
     116          return 0;
     117      }
     118  
     119      public static function plugin_sanity_check() {
     120          global $CFG, $DB;
     121          $errorcode = 0;
     122          if (!isset($CFG->mnet_dispatcher_mode) || $CFG->mnet_dispatcher_mode != 'strict') {
     123              $errorcode =  PORTFOLIO_MAHARA_ERR_NETWORKING_OFF;
     124          }
     125          if (!is_enabled_auth('mnet')) {
     126              $errorcode = PORTFOLIO_MAHARA_ERR_NOMNETAUTH;
     127          }
     128          if (!self::get_mnet_hosts()) {
     129              $errorcode =  PORTFOLIO_MAHARA_ERR_NOHOSTS;
     130          }
     131          return $errorcode;
     132      }
     133  
     134      private static function get_mnet_hosts() {
     135          global $DB, $CFG;
     136          static $hosts;
     137          if (isset($hosts)) {
     138              return $hosts;
     139          }
     140          $hosts = $DB->get_records_sql('  SELECT
     141                                      h.id,
     142                                      h.wwwroot,
     143                                      h.ip_address,
     144                                      h.name,
     145                                      h.public_key,
     146                                      h.public_key_expires,
     147                                      h.transport,
     148                                      h.portno,
     149                                      h.last_connect_time,
     150                                      h.last_log_id,
     151                                      h.applicationid,
     152                                      a.name as app_name,
     153                                      a.display_name as app_display_name,
     154                                      a.xmlrpc_server_url
     155                                  FROM {mnet_host} h
     156                                      JOIN {mnet_application} a ON h.applicationid=a.id
     157                                      JOIN {mnet_host2service} hs1 ON hs1.hostid = h.id
     158                                      JOIN {mnet_service} s1 ON hs1.serviceid = s1.id
     159                                      JOIN {mnet_host2service} hs2 ON hs2.hostid = h.id
     160                                      JOIN {mnet_service} s2 ON hs2.serviceid = s2.id
     161                                      JOIN {mnet_host2service} hs3 ON hs3.hostid = h.id
     162                                      JOIN {mnet_service} s3 ON hs3.serviceid = s3.id
     163                                  WHERE
     164                                      h.id <> ? AND
     165                                      h.deleted = 0 AND
     166                                      a.name = ? AND
     167                                      s1.name = ? AND hs1.publish = ? AND
     168                                      s2.name = ? AND hs2.subscribe = ? AND
     169                                      s3.name = ? AND hs3.subscribe = ? AND
     170                                      s3.name = ? AND hs3.publish = ?',
     171                          array($CFG->mnet_localhost_id, 'mahara', 'sso_idp', 1, 'sso_sp', 1, 'pf', 1, 'pf', 1));
     172          return $hosts;
     173      }
     174  
     175      public function prepare_package() {
     176          $files = $this->exporter->get_tempfiles();
     177          $this->totalsize = 0;
     178          foreach ($files as $f) {
     179              $this->filesmanifest[$f->get_contenthash()] = array(
     180                  'filename' => $f->get_filename(),
     181                  'sha1'     => $f->get_contenthash(),
     182                  'size'     => $f->get_filesize(),
     183              );
     184              $this->totalsize += $f->get_filesize();
     185          }
     186  
     187          $this->set('file', $this->exporter->zip_tempfiles());  // this will throw a file_exception which the exporter catches separately.
     188      }
     189  
     190      public function send_package() {
     191          global $CFG;
     192          // send the 'content_ready' request to mahara
     193          require_once($CFG->dirroot . '/mnet/xmlrpc/client.php');
     194          $client = new mnet_xmlrpc_client();
     195          $client->set_method('portfolio/mahara/lib.php/send_content_ready');
     196          $client->add_param($this->token);
     197          $client->add_param($this->get('user')->username);
     198          $client->add_param($this->resolve_format());
     199          $client->add_param(array(
     200              'filesmanifest' => $this->filesmanifest,
     201              'zipfilesha1'   => $this->get('file')->get_contenthash(),
     202              'zipfilesize'   => $this->get('file')->get_filesize(),
     203              'totalsize'     => $this->totalsize,
     204          ));
     205          $client->add_param($this->get_export_config('wait'));
     206          $this->ensure_mnethost();
     207          if (!$client->send($this->mnethost)) {
     208              foreach ($client->error as $errormessage) {
     209                  list($code, $message) = array_map('trim',explode(':', $errormessage, 2));
     210                  $message .= "ERROR $code:<br/>$errormessage<br/>";
     211              }
     212              throw new portfolio_export_exception($this->get('exporter'), 'failedtoping', 'portfolio_mahara', '', $message);
     213          }
     214          // we should get back...  an ok and a status
     215          // either we've been waiting a while and mahara has fetched the file or has queued it.
     216          $response = (object)$client->response;
     217          if (!$response->status) {
     218              throw new portfolio_export_exception($this->get('exporter'), 'failedtoping', 'portfolio_mahara');
     219          }
     220          if ($response->type =='queued') {
     221              $this->exporter->set_forcequeue();
     222          }
     223          if (isset($response->querystring)) {
     224              $this->continueurl = $response->querystring;
     225          }
     226          // if we're not queuing the logging might have already happened
     227          $this->exporter->update_log_url($this->get_static_continue_url());
     228      }
     229  
     230      public function get_static_continue_url() {
     231          $remoteurl = '';
     232          if ($this->resolve_format() == 'file') {
     233              $remoteurl = '/artefact/file/'; // we hopefully get the files that were imported highlighted
     234          }
     235          if (isset($this->continueurl)) {
     236              $remoteurl .= $this->continueurl;
     237          }
     238          return $remoteurl;
     239      }
     240  
     241      public function resolve_static_continue_url($remoteurl) {
     242          global $CFG;
     243          $this->ensure_mnethost();
     244          $u = new moodle_url('/auth/mnet/jump.php', array('hostid' => $this->get_config('mnethostid'), 'wantsurl' => $remoteurl));
     245          return $u->out();
     246      }
     247  
     248      public function get_interactive_continue_url() {
     249          return $this->resolve_static_continue_url($this->get_static_continue_url());
     250      }
     251  
     252      public function steal_control($stage) {
     253          if ($stage != PORTFOLIO_STAGE_CONFIG) {
     254              return false;
     255          }
     256          global $CFG;
     257          return $CFG->wwwroot . '/portfolio/mahara/preconfig.php?id=' . $this->exporter->get('id');
     258      }
     259  
     260      public function verify_file_request_params($params) {
     261          return false;
     262          // the data comes from an xmlrpc request,
     263          // not a request to file.php
     264      }
     265  
     266      /**
     267      * sends the 'content_intent' ping to mahara
     268      * if all goes well, this will set the 'token' and 'sendtype' member variables.
     269      */
     270      public function send_intent() {
     271          global $CFG, $DB;
     272          require_once($CFG->dirroot . '/mnet/xmlrpc/client.php');
     273          $client = new mnet_xmlrpc_client();
     274          $client->set_method('portfolio/mahara/lib.php/send_content_intent');
     275          $client->add_param($this->get('user')->username);
     276          $this->ensure_mnethost();
     277          if (!$client->send($this->mnethost)) {
     278              foreach ($client->error as $errormessage) {
     279                  list($code, $message) = array_map('trim',explode(':', $errormessage, 2));
     280                  $message .= "ERROR $code:<br/>$errormessage<br/>";
     281              }
     282              throw new portfolio_export_exception($this->get('exporter'), 'failedtoping', 'portfolio_mahara', '', $message);
     283          }
     284          // we should get back... the send type and a shared token
     285          $response = (object)$client->response;
     286          if (empty($response->sendtype) || empty($response->token)) {
     287              throw new portfolio_export_exception($this->get('exporter'), 'senddisallowed', 'portfolio_mahara');
     288          }
     289          switch ($response->sendtype) {
     290              case 'immediate':
     291                  $this->sendtype = PORTFOLIO_MAHARA_IMMEDIATE;
     292                  break;
     293              case 'queue':
     294                  $this->sendtype = PORTFOLIO_MAHARA_QUEUE;
     295                  break;
     296              case 'none':
     297              default:
     298                  throw new portfolio_export_exception($this->get('exporter'), 'senddisallowed', 'portfolio_mahara');
     299          }
     300          $this->token = $response->token;
     301          $this->get('exporter')->save();
     302          // put the entry in the mahara queue table now too
     303          $q = new stdClass;
     304          $q->token = $this->token;
     305          $q->transferid = $this->get('exporter')->get('id');
     306          $DB->insert_record('portfolio_mahara_queue', $q);
     307      }
     308  
     309      private function ensure_mnethost() {
     310          if (!empty($this->hostrecord) && !empty($this->mnethost)) {
     311              return;
     312          }
     313          global $DB;
     314          if (!$this->hostrecord = $DB->get_record('mnet_host', array('id' => $this->get_config('mnethostid')))) {
     315              throw new portfolio_plugin_exception(PORTFOLIO_MAHARA_ERR_INVALIDHOST, 'portfolio_mahara');
     316          }
     317          $this->mnethost = new mnet_peer();
     318          $this->mnethost->set_wwwroot($this->hostrecord->wwwroot);
     319      }
     320  
     321      /**
     322      * xmlrpc (mnet) function to get the file.
     323      * reads in the file and returns it base_64 encoded
     324      * so that it can be enrypted by mnet.
     325      *
     326      * @param string $token the token recieved previously during send_content_intent
     327      */
     328      public static function fetch_file($token) {
     329          global $DB;
     330          $remoteclient = get_mnet_remote_client();
     331          try {
     332              if (!$transferid = $DB->get_field('portfolio_mahara_queue', 'transferid', array('token' => $token))) {
     333                  throw new mnet_server_exception(8009, 'mnet_notoken', 'portfolio_mahara');
     334              }
     335              $exporter = portfolio_exporter::rewaken_object($transferid);
     336          } catch (portfolio_exception $e) {
     337              throw new mnet_server_exception(8010, 'mnet_noid', 'portfolio_mahara');
     338          }
     339          if ($exporter->get('instance')->get_config('mnethostid') != $remoteclient->id) {
     340              throw new mnet_server_exception(8011, 'mnet_wronghost', 'portfolio_mahara');
     341          }
     342          global $CFG;
     343          try {
     344              $i = $exporter->get('instance');
     345              $f = $i->get('file');
     346              if (empty($f) || !($f instanceof stored_file)) {
     347                  throw new mnet_server_exception(8012, 'mnet_nofile', 'portfolio_mahara');
     348              }
     349              try {
     350                  $c = $f->get_content();
     351              } catch (file_exception $e) {
     352                  throw new mnet_server_exception(8013, 'mnet_nofilecontents', 'portfolio_mahara', $e->getMessage());
     353              }
     354              $contents = base64_encode($c);
     355          } catch (Exception $e) {
     356              throw new mnet_server_exception(8013, 'mnet_nofile', 'portfolio_mahara');
     357          }
     358          $exporter->log_transfer();
     359          $exporter->process_stage_cleanup(true);
     360          return $contents;
     361      }
     362  
     363      public function cleanup() {
     364          global $DB;
     365          $DB->delete_records('portfolio_mahara_queue', array('transferid' => $this->get('exporter')->get('id'), 'token' => $this->token));
     366      }
     367  
     368  
     369      /**
     370       * internal helper function, that converts between the format constant,
     371       * which might be too specific (eg 'image') and the class in our *supported* list
     372       * which might be higher up the format hierarchy tree (eg 'file')
     373       */
     374      private function resolve_format() {
     375          global $CFG;
     376          $thisformat = $this->get_export_config('format');
     377          $allformats = portfolio_supported_formats();
     378          require_once($CFG->libdir . '/portfolio/formats.php');
     379          $thisobj = new $allformats[$thisformat];
     380          foreach ($this->supported_formats() as $f) {
     381              $class = $allformats[$f];
     382              if ($thisobj instanceof $class) {
     383                  return $f;
     384              }
     385          }
     386      }
     387  }
     388  
     389  
    

    Search This Site: