PageRenderTime 65ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/portfolio/type/mahara/lib.php

https://github.com/cwaclawik/moodle
PHP | 352 lines | 302 code | 30 blank | 20 comment | 40 complexity | 0185d54183b18eb974a63f77eb9fd7f3 MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause, LGPL-2.1
  1. <?php
  2. define('PORTFOLIO_MAHARA_ERR_NETWORKING_OFF', 'err_networkingoff');
  3. define('PORTFOLIO_MAHARA_ERR_NOHOSTS', 'err_nomnethosts');
  4. define('PORTFOLIO_MAHARA_ERR_INVALIDHOST', 'err_invalidhost');
  5. define('PORTFOLIO_MAHARA_ERR_NOMNETAUTH', 'err_nomnetauth');
  6. require_once($CFG->dirroot . '/lib/portfoliolib.php');
  7. require_once($CFG->dirroot . '/mnet/lib.php');
  8. define('PORTFOLIO_MAHARA_QUEUE', PORTFOLIO_TIME_HIGH);
  9. define('PORTFOLIO_MAHARA_IMMEDIATE', PORTFOLIO_TIME_MODERATE);
  10. class portfolio_plugin_mahara extends portfolio_plugin_pull_base {
  11. private $hosts; // used in the admin config form
  12. private $mnethost; // privately set during export from the admin config value (mnethostid)
  13. private $hostrecord; // the host record that corresponds to the peer
  14. private $token; // during-transfer token
  15. private $sendtype; // whatever mahara has said it can handle (immediate or queued)
  16. private $filesmanifest; // manifest of files to send to mahara (set during prepare_package and sent later)
  17. private $totalsize; // total size of all included files added together
  18. private $continueurl; // if we've been sent back a specific url to continue to (eg folder id)
  19. public static function get_name() {
  20. return get_string('pluginname', 'portfolio_mahara');
  21. }
  22. public static function get_allowed_config() {
  23. return array('mnethostid');
  24. }
  25. public static function supported_formats() {
  26. return array(PORTFOLIO_FORMAT_FILE);
  27. }
  28. public function expected_time($callertime) {
  29. if ($this->sendtype == PORTFOLIO_MAHARA_QUEUE) {
  30. return PORTFOLIO_TIME_FORCEQUEUE;
  31. }
  32. return $callertime;
  33. }
  34. public static function has_admin_config() {
  35. return true;
  36. }
  37. public function admin_config_form(&$mform) {
  38. $strrequired = get_string('required');
  39. $hosts = self::get_mnet_hosts(); // this is called by sanity check but it's ok because it's cached
  40. foreach ($hosts as $host) {
  41. $hosts[$host->id] = $host->name;
  42. }
  43. $mform->addElement('select', 'mnethostid', get_string('mnethost', 'portfolio_mahara'), $hosts);
  44. $mform->addRule('mnethostid', $strrequired, 'required', null, 'client');
  45. }
  46. public function instance_sanity_check() {
  47. // make sure the host record exists since we don't have referential integrity
  48. if (!is_enabled_auth('mnet')) {
  49. return PORTFOLIO_MAHARA_ERR_NOMNETAUTH;
  50. }
  51. try {
  52. $this->ensure_mnethost();
  53. }
  54. catch (portfolio_exception $e) {
  55. return PORTFOLIO_MAHARA_ERR_INVALIDHOST;
  56. }
  57. // make sure we have the right services
  58. $hosts = $this->get_mnet_hosts();
  59. if (!array_key_exists($this->get_config('mnethostid'), $hosts)) {
  60. return PORTFOLIO_MAHARA_ERR_INVALIDHOST;
  61. }
  62. return 0;
  63. }
  64. public static function plugin_sanity_check() {
  65. global $CFG, $DB;
  66. $errorcode = 0;
  67. if (!isset($CFG->mnet_dispatcher_mode) || $CFG->mnet_dispatcher_mode != 'strict') {
  68. $errorcode = PORTFOLIO_MAHARA_ERR_NETWORKING_OFF;
  69. }
  70. if (!is_enabled_auth('mnet')) {
  71. $errorcode = PORTFOLIO_MAHARA_ERR_NOMNETAUTH;
  72. }
  73. if (!self::get_mnet_hosts()) {
  74. $errorcode = PORTFOLIO_MAHARA_ERR_NOHOSTS;
  75. }
  76. return $errorcode;
  77. }
  78. private static function get_mnet_hosts() {
  79. global $DB, $CFG;
  80. static $hosts;
  81. if (isset($this) && is_object($this) && isset($this->hosts)) {
  82. return $this->hosts;
  83. } else if (!isset($this) && isset($hosts)) {
  84. return $hosts;
  85. }
  86. $hosts = $DB->get_records_sql(' SELECT
  87. h.id,
  88. h.wwwroot,
  89. h.ip_address,
  90. h.name,
  91. h.public_key,
  92. h.public_key_expires,
  93. h.transport,
  94. h.portno,
  95. h.last_connect_time,
  96. h.last_log_id,
  97. h.applicationid,
  98. a.name as app_name,
  99. a.display_name as app_display_name,
  100. a.xmlrpc_server_url
  101. FROM {mnet_host} h
  102. JOIN {mnet_application} a ON h.applicationid=a.id
  103. JOIN {mnet_host2service} hs1 ON hs1.hostid = h.id
  104. JOIN {mnet_service} s1 ON hs1.serviceid = s1.id
  105. JOIN {mnet_host2service} hs2 ON hs2.hostid = h.id
  106. JOIN {mnet_service} s2 ON hs2.serviceid = s2.id
  107. JOIN {mnet_host2service} hs3 ON hs3.hostid = h.id
  108. JOIN {mnet_service} s3 ON hs3.serviceid = s3.id
  109. WHERE
  110. h.id <> ? AND
  111. h.deleted = 0 AND
  112. a.name = ? AND
  113. s1.name = ? AND hs1.publish = ? AND
  114. s2.name = ? AND hs2.subscribe = ? AND
  115. s3.name = ? AND hs3.subscribe = ?',
  116. array($CFG->mnet_localhost_id, 'mahara', 'sso_idp', 1, 'sso_sp', 1, 'pf', 1));;
  117. if (empty($hosts)) { $hosts = array(); }
  118. if (isset($this) && is_object($this)) {
  119. $this->hosts = $hosts;
  120. }
  121. return $hosts;
  122. }
  123. public function prepare_package() {
  124. $files = $this->exporter->get_tempfiles();
  125. $this->totalsize = 0;
  126. foreach ($files as $f) {
  127. $this->filesmanifest[$f->get_contenthash()] = array(
  128. 'filename' => $f->get_filename(),
  129. 'sha1' => $f->get_contenthash(),
  130. 'size' => $f->get_filesize(),
  131. );
  132. $this->totalsize += $f->get_filesize();
  133. }
  134. $this->set('file', $this->exporter->zip_tempfiles()); // this will throw a file_exception which the exporter catches separately.
  135. }
  136. private function ensure_environment() {
  137. global $MNET;
  138. if (empty($MNET)) {
  139. $MNET = new mnet_environment();
  140. $MNET->init();
  141. } // no idea why this happens :(
  142. }
  143. public function send_package() {
  144. global $CFG;
  145. $this->ensure_environment();
  146. // send the 'content_ready' request to mahara
  147. require_once($CFG->dirroot . '/mnet/xmlrpc/client.php');
  148. $client = new mnet_xmlrpc_client();
  149. $client->set_method('portfolio/mahara/lib.php/send_content_ready');
  150. $client->add_param($this->token);
  151. $client->add_param($this->get('user')->username);
  152. $client->add_param($this->resolve_format());
  153. $client->add_param(array(
  154. 'filesmanifest' => $this->filesmanifest,
  155. 'zipfilesha1' => $this->get('file')->get_contenthash(),
  156. 'zipfilesize' => $this->get('file')->get_filesize(),
  157. 'totalsize' => $this->totalsize,
  158. ));
  159. $client->add_param($this->get_export_config('wait'));
  160. $this->ensure_mnethost();
  161. if (!$client->send($this->mnethost)) {
  162. foreach ($client->error as $errormessage) {
  163. list($code, $message) = array_map('trim',explode(':', $errormessage, 2));
  164. $message .= "ERROR $code:<br/>$errormessage<br/>";
  165. }
  166. throw new portfolio_export_exception($this->get('exporter'), 'failedtoping', 'portfolio_mahara', '', $message);
  167. }
  168. // we should get back... an ok and a status
  169. // either we've been waiting a while and mahara has fetched the file or has queued it.
  170. $response = (object)$client->response;
  171. if (!$response->status) {
  172. throw new portfolio_export_exception($this->get('exporter'), 'failedtoping', 'portfolio_mahara');
  173. }
  174. if ($response->type =='queued') {
  175. $this->exporter->set_forcequeue();
  176. }
  177. if (isset($response->querystring)) {
  178. $this->continueurl = $response->querystring;
  179. }
  180. }
  181. public function get_continue_url() {
  182. $this->ensure_mnethost();
  183. $this->ensure_environment();
  184. $mnetauth = get_auth_plugin('mnet');
  185. $remoteurl = '/artefact/file/';// @todo penny this might change later when we change formats.
  186. if (isset($this->continueurl)) {
  187. $remoteurl .= $this->continueurl;
  188. }
  189. if (!$url = $mnetauth->start_jump_session($this->get_config('mnethostid'), $remoteurl)) {
  190. return false;
  191. }
  192. return $url;
  193. }
  194. public function steal_control($stage) {
  195. if ($stage != PORTFOLIO_STAGE_CONFIG) {
  196. return false;
  197. }
  198. global $CFG;
  199. return $CFG->wwwroot . '/portfolio/type/mahara/preconfig.php?id=' . $this->exporter->get('id');
  200. }
  201. public function verify_file_request_params($params) {
  202. return false;
  203. // the data comes from an xmlrpc request,
  204. // not a request to file.php
  205. }
  206. /**
  207. * sends the 'content_intent' ping to mahara
  208. * if all goes well, this will set the 'token' and 'sendtype' member variables.
  209. */
  210. public function send_intent() {
  211. global $CFG, $DB;
  212. require_once($CFG->dirroot . '/mnet/xmlrpc/client.php');
  213. $client = new mnet_xmlrpc_client();
  214. $client->set_method('portfolio/mahara/lib.php/send_content_intent');
  215. $client->add_param($this->get('user')->username);
  216. $this->ensure_mnethost();
  217. if (!$client->send($this->mnethost)) {
  218. foreach ($client->error as $errormessage) {
  219. list($code, $message) = array_map('trim',explode(':', $errormessage, 2));
  220. $message .= "ERROR $code:<br/>$errormessage<br/>";
  221. }
  222. throw new portfolio_export_exception($this->get('exporter'), 'failedtoping', 'portfolio_mahara', '', $message);
  223. }
  224. // we should get back... the send type and a shared token
  225. $response = (object)$client->response;
  226. if (empty($response->sendtype) || empty($response->token)) {
  227. throw new portfolio_export_exception($this->get('exporter'), 'senddisallowed', 'portfolio_mahara');
  228. }
  229. switch ($response->sendtype) {
  230. case 'immediate':
  231. $this->sendtype = PORTFOLIO_MAHARA_IMMEDIATE;
  232. break;
  233. case 'queue':
  234. $this->sendtype = PORTFOLIO_MAHARA_QUEUE;
  235. break;
  236. case 'none':
  237. default:
  238. throw new portfolio_export_exception($this->get('exporter'), 'senddisallowed', 'portfolio_mahara');
  239. }
  240. $this->token = $response->token;
  241. $this->get('exporter')->save();
  242. // put the entry in the mahara queue table now too
  243. $q = new stdClass;
  244. $q->token = $this->token;
  245. $q->transferid = $this->get('exporter')->get('id');
  246. $DB->insert_record('portfolio_mahara_queue', $q);
  247. }
  248. private function ensure_mnethost() {
  249. if (!empty($this->hostrecord) && !empty($this->mnethost)) {
  250. return;
  251. }
  252. global $DB;
  253. if (!$this->hostrecord = $DB->get_record('mnet_host', array('id' => $this->get_config('mnethostid')))) {
  254. throw new portfolio_plugin_exception(PORTFOLIO_MAHARA_ERR_INVALIDHOST, 'portfolio_mahara');
  255. }
  256. $this->mnethost = new mnet_peer();
  257. $this->mnethost->set_wwwroot($this->hostrecord->wwwroot);
  258. }
  259. public static function mnet_publishes() {
  260. $pf= array();
  261. $pf['name'] = 'pf'; // Name & Description go in lang file
  262. $pf['apiversion'] = 1;
  263. $pf['methods'] = array('send_content_intent', 'send_content_ready', 'fetch_file');
  264. return array($pf);
  265. }
  266. /**
  267. * xmlrpc (mnet) function to get the file.
  268. * reads in the file and returns it base_64 encoded
  269. * so that it can be enrypted by mnet.
  270. *
  271. * @param string $token the token recieved previously during send_content_intent
  272. */
  273. public static function fetch_file($token) {
  274. global $DB, $MNET_REMOTE_CLIENT;;
  275. try {
  276. if (!$transferid = $DB->get_field('portfolio_mahara_queue', 'transferid', array('token' => $token))) {
  277. exit(mnet_server_fault(8009, get_string('mnet_notoken', 'portfolio_mahara')));
  278. }
  279. $exporter = portfolio_exporter::rewaken_object($transferid);
  280. } catch (portfolio_exception $e) {
  281. exit(mnet_server_fault(8010, get_string('mnet_noid', 'portfolio_mahara')));
  282. }
  283. if ($exporter->get('instance')->get_config('mnethostid') != $MNET_REMOTE_CLIENT->id) {
  284. exit(mnet_server_fault(8011, get_string('mnet_wronghost', 'portfolio_mahara')));
  285. }
  286. global $CFG;
  287. try {
  288. $i = $exporter->get('instance');
  289. $f = $i->get('file');
  290. if (empty($f) || !($f instanceof stored_file)) {
  291. exit(mnet_server_fault(8012, get_string('mnet_nofile', 'portfolio_mahara')));
  292. }
  293. try {
  294. $c = $f->get_content();
  295. } catch (file_exception $e) {
  296. exit(mnet_server_fault(8013, get_string('mnet_nofilecontents', 'portfolio_mahara', $e->getMessage())));
  297. }
  298. $contents = base64_encode($c);
  299. } catch (Exception $e) {
  300. exit(mnet_server_fault(8013, get_string('mnet_nofile', 'portfolio_mahara')));
  301. }
  302. $exporter->log_transfer();
  303. $exporter->process_stage_cleanup(true);
  304. return $contents;
  305. }
  306. public function cleanup() {
  307. global $DB;
  308. $DB->delete_records('portfolio_mahara_queue', array('transferid' => $this->get('exporter')->get('id'), 'token' => $this->token));
  309. }
  310. private function resolve_format() {
  311. $thisformat = $this->get_export_config('format');
  312. $allformats = portfolio_supported_formats();
  313. $thisobj = new $allformats[$thisformat];
  314. foreach ($this->supported_formats() as $f) {
  315. $class = $allformats[$f];
  316. if ($thisobj instanceof $class) {
  317. return $f;
  318. }
  319. }
  320. }
  321. }
  322. ?>