|
Moodle
2.2.1
http://www.collinsharper.com
|
00001 <?php 00029 defined('MOODLE_INTERNAL') || die(); 00030 00044 class portfolio_format_leap2a_writer { 00045 00047 private $dom; 00049 private $feed; 00051 private $user; 00053 private $id; 00055 private $entries = array(); 00056 00063 public function __construct(stdclass $user) { // todo something else - exporter, format, etc 00064 global $CFG; 00065 $this->user = $user; 00066 $this->exporttime = time(); 00067 $this->id = $CFG->wwwroot . '/portfolio/export/leap2a/' . $this->user->id . '/' . $this->exporttime; 00068 00069 $this->dom = new DomDocument('1.0', 'utf-8'); 00070 00071 $this->feed = $this->dom->createElement('feed'); 00072 $this->feed->setAttribute('xmlns', 'http://www.w3.org/2005/Atom'); 00073 $this->feed->setAttribute('xmlns:rdf', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'); 00074 $this->feed->setAttribute('xmlns:leap2', 'http://terms.leapspecs.org/'); 00075 $this->feed->setAttribute('xmlns:categories', 'http://wiki.leapspecs.org/2A/categories'); 00076 $this->feed->setAttribute('xmlns:portfolio', $this->id); // this is just a ns for ids of elements for convenience 00077 00078 $this->dom->appendChild($this->feed); 00079 00080 $this->feed->appendChild($this->dom->createElement('id', $this->id)); 00081 $this->feed->appendChild($this->dom->createElement('title', get_string('leap2a_feedtitle', 'portfolio', fullname($this->user)))); 00082 $this->feed->appendChild($this->dom->createElement('leap2:version', 'http://www.leapspecs.org/2010-07/2A/')); 00083 00084 00085 $generator = $this->dom->createElement('generator', 'Moodle'); 00086 $generator->setAttribute('uri', $CFG->wwwroot); 00087 $generator->setAttribute('version', $CFG->version); 00088 00089 $this->feed->appendChild($generator); 00090 00091 $author = $this->dom->createElement('author'); 00092 $author->appendChild($this->dom->createElement('name', fullname($this->user))); 00093 $author->appendChild($this->dom->createElement('email', $this->user->email)); 00094 $author->appendChild($this->dom->CreateElement('uri', $CFG->wwwroot . '/user/view.php?id=' . $this->user->id)); 00095 00096 $this->feed->appendChild($author); 00097 // header done, we can start appending entry elements now 00098 } 00099 00105 public function add_entry(portfolio_format_leap2a_entry $entry) { 00106 if (array_key_exists($entry->id, $this->entries)) { 00107 if (!($entry instanceof portfolio_format_leap2a_file)) { 00108 throw new portfolio_format_leap2a_exception('leap2a_entryalreadyexists', 'portfolio', '', $entry->id); 00109 } 00110 } 00111 $this->entries[$entry->id] = $entry; 00112 return $entry; 00113 } 00114 00122 public function make_selection($selectionentry, $ids, $selectiontype) { 00123 $selectionid = null; 00124 if ($selectionentry instanceof portfolio_format_leap2a_entry) { 00125 $selectionid = $selectionentry->id; 00126 } else if (is_string($selectionentry)) { 00127 $selectionid = $selectionentry; 00128 } 00129 if (!array_key_exists($selectionid, $this->entries)) { 00130 throw new portfolio_format_leap2a_exception('leap2a_invalidentryid', 'portfolio', '', $selectionid); 00131 } 00132 foreach ($ids as $entryid) { 00133 if (!array_key_exists($entryid, $this->entries)) { 00134 throw new portfolio_format_leap2a_exception('leap2a_invalidentryid', 'portfolio', '', $entryid); 00135 } 00136 $this->entries[$selectionid]->add_link($entryid, 'has_part'); 00137 $this->entries[$entryid]->add_link($selectionid, 'is_part_of'); 00138 } 00139 $this->entries[$selectionid]->add_category($selectiontype, 'selection_type'); 00140 if ($this->entries[$selectionid]->type != 'selection') { 00141 debugging(get_string('leap2a_overwritingselection', 'portfolio', $this->entries[$selectionid]->type)); 00142 $this->entries[$selectionid]->type = 'selection'; 00143 } 00144 } 00145 00152 public function link_files($entry, $files) { 00153 foreach ($files as $file) { 00154 $fileentry = new portfolio_format_leap2a_file($file->get_filename(), $file); 00155 $this->add_entry($fileentry); 00156 $entry->add_link($fileentry, 'related'); 00157 $fileentry->add_link($entry, 'related'); 00158 } 00159 } 00160 00164 private function validate() { 00165 foreach ($this->entries as $entry) { 00166 // first call the entry's own validation method 00167 // which will throw an exception if there's anything wrong 00168 $entry->validate(); 00169 // now make sure that all links are in place 00170 foreach ($entry->links as $linkedid => $rel) { 00171 // the linked to entry exists 00172 if (!array_key_exists($linkedid, $this->entries)) { 00173 $a = (object)array('rel' => $rel->type, 'to' => $linkedid, 'from' => $entry->id); 00174 throw new portfolio_format_leap2a_exception('leap2a_nonexistantlink', 'portfolio', '', $a); 00175 } 00176 // and contains a link back to us 00177 if (!array_key_exists($entry->id, $this->entries[$linkedid]->links)) { 00178 00179 } 00180 // we could later check that the reltypes were properly inverse, but nevermind for now. 00181 } 00182 } 00183 } 00184 00191 public function to_xml() { 00192 $this->validate(); 00193 foreach ($this->entries as $entry) { 00194 $entry->id = 'portfolio:' . $entry->id; 00195 $this->feed->appendChild($entry->to_dom($this->dom, $this->user)); 00196 } 00197 return $this->dom->saveXML(); 00198 } 00199 } 00200 00205 class portfolio_format_leap2a_entry { 00206 00208 public $id; 00210 public $title; 00212 public $type; 00214 public $author; 00216 public $summary; 00218 public $content; 00220 public $updated; 00222 public $published; 00223 00225 private $requiredfields = array( 'id', 'title', 'type'); 00226 00228 private $optionalfields = array('author', 'updated', 'published', 'content', 'summary'); 00229 00231 public $links = array(); 00232 00234 public $attachments = array(); 00235 00237 private $categories = array(); 00238 00250 public function __construct($id, $title, $type, $content=null) { 00251 $this->id = $id; 00252 $this->title = $title; 00253 $this->type = $type; 00254 $this->content = $this->__set('content', $content); 00255 00256 } 00257 00262 public function __set($field, $value) { 00263 // detect the case where content is being set to be a file directly 00264 if ($field == 'content' && $value instanceof stored_file) { 00265 throw new portfolio_format_leap2a_exception('leap2a_filecontent', 'portfolio'); 00266 } 00267 if (in_array($field, $this->requiredfields) || in_array($field, $this->optionalfields)) { 00268 return $this->{$field} = $value; 00269 } 00270 throw new portfolio_format_leap2a_exception('leap2a_invalidentryfield', 'portfolio', '', $field); 00271 } 00272 00273 00279 public function validate() { 00280 foreach ($this->requiredfields as $key) { 00281 if (empty($this->{$key})) { 00282 throw new portfolio_format_leap2a_exception('leap2a_missingfield', 'portfolio', '', $key); 00283 } 00284 } 00285 if ($this->type == 'selection') { 00286 if (count($this->links) == 0) { 00287 throw new portfolio_format_leap2a_exception('leap2a_emptyselection', 'portfolio'); 00288 } 00289 //TODO make sure we have a category with a scheme 'selection_type' 00290 } 00291 } 00292 00306 public function add_link($otherentry, $reltype, $displayorder=null) { 00307 if ($otherentry instanceof portfolio_format_leap2a_entry) { 00308 $otherentry = $otherentry->id; 00309 } 00310 if ($otherentry == $this->id) { 00311 throw new portfolio_format_leap2a_exception('leap2a_selflink', 'portfolio', '', (object)array('rel' => $reltype, 'id' => $this->id)); 00312 } 00313 // add on the leap2: ns if required 00314 if (!in_array($reltype, array('related', 'alternate', 'enclosure'))) { 00315 $reltype = 'leap2:' . $reltype; 00316 } 00317 00318 $this->links[$otherentry] = (object)array('rel' => $reltype, 'order' => $displayorder); 00319 00320 return $this; 00321 } 00322 00334 public function add_category($term, $scheme=null, $label=null) { 00335 // "normalise" terms and set their label if they have spaces 00336 // see http://wiki.cetis.ac.uk/2009-03/LEAP2A_categories#Plain_tags for more information 00337 if (empty($scheme) && strpos($term, ' ') !== false) { 00338 $label = $term; 00339 $term = str_replace(' ', '-', $term); 00340 } 00341 $this->categories[] = (object)array( 00342 'term' => $term, 00343 'scheme' => $scheme, 00344 'label' => $label, 00345 ); 00346 } 00347 00357 public function to_dom(DomDocument $dom, $feedauthor) { 00358 $entry = $dom->createElement('entry'); 00359 $entry->appendChild($dom->createElement('id', $this->id)); 00360 $entry->appendChild($dom->createElement('title', $this->title)); 00361 if ($this->author && $this->author->id != $feedauthor->id) { 00362 $author = $dom->createElement('author'); 00363 $author->appendChild($dom->createElement('name', fullname($this->author))); 00364 $entry->appendChild($author); 00365 } 00366 // selectively add uncomplicated optional elements 00367 foreach (array('updated', 'published') as $field) { 00368 if ($this->{$field}) { 00369 $date = date(DATE_ATOM, $this->{$field}); 00370 $entry->appendChild($dom->createElement($field, $date)); 00371 } 00372 } 00373 if (empty($this->content)) { 00374 $entry->appendChild($dom->createElement('content')); 00375 } else { 00376 $content = $this->create_xhtmlish_element($dom, 'content', $this->content); 00377 $entry->appendChild($content); 00378 } 00379 00380 if (!empty($this->summary)) { 00381 $summary = $this->create_xhtmlish_element($dom, 'summary', $this->summary); 00382 $entry->appendChild($summary); 00383 } 00384 00385 $type = $dom->createElement('rdf:type'); 00386 $type->setAttribute('rdf:resource', 'leap2:' . $this->type); 00387 $entry->appendChild($type); 00388 00389 foreach ($this->links as $otherentry => $l) { 00390 $link = $dom->createElement('link'); 00391 $link->setAttribute('rel', $l->rel); 00392 $link->setAttribute('href', 'portfolio:' . $otherentry); 00393 if ($l->order) { 00394 $link->setAttribute('leap2:display_order', $l->order); 00395 } 00396 $entry->appendChild($link); 00397 } 00398 00399 $this->add_extra_links($dom, $entry); // hook for subclass 00400 00401 foreach ($this->categories as $category) { 00402 $cat = $dom->createElement('category'); 00403 $cat->setAttribute('term', $category->term); 00404 if ($category->scheme) { 00405 $cat->setAttribute('scheme', 'categories:' .$category->scheme . '#'); 00406 } 00407 if ($category->label && $category->label != $category->term) { 00408 $cat->setAttribute('label', $category->label); 00409 } 00410 $entry->appendChild($cat); 00411 } 00412 return $entry; 00413 } 00414 00427 private function create_xhtmlish_element(DomDocument $dom, $tagname, $content) { 00428 $topel = $dom->createElement($tagname); 00429 $maybexml = true; 00430 if (strpos($content, '<') === false && strpos($content, '>') === false) { 00431 $maybexml = false; 00432 } 00433 // try to load content as xml 00434 $tmp = new DomDocument(); 00435 if ($maybexml && @$tmp->loadXML('<div>' . $content . '</div>')) { 00436 $topel->setAttribute('type', 'xhtml'); 00437 $content = $dom->importNode($tmp->documentElement, true); 00438 $content->setAttribute('xmlns', 'http://www.w3.org/1999/xhtml'); 00439 $topel->appendChild($content); 00440 // if that fails, it could still be html 00441 } else if ($maybexml && @$tmp->loadHTML($content)) { 00442 $topel->setAttribute('type', 'html'); 00443 $topel->nodeValue = $content; 00444 // TODO figure out how to convert this to xml 00445 // TODO because we end up with <html><body> </body></html> wrapped around it 00446 // which is annoying 00447 // either we already know it's text from the first check 00448 // or nothing else has worked anyway 00449 } else { 00450 $topel->nodeValue = $content; 00451 $topel->setAttribute('type', 'text'); 00452 return $topel; 00453 } 00454 return $topel; 00455 } 00456 00460 protected function add_extra_links() {} 00461 } 00462 00463 00467 class portfolio_format_leap2a_file extends portfolio_format_leap2a_entry { 00468 00469 protected $referencedfile; 00470 00475 public function __construct($title, stored_file $file) { 00476 $id = portfolio_format_leap2a::file_id_prefix() . $file->get_id(); 00477 parent::__construct($id, $title, 'resource'); 00478 $this->referencedfile = $file; 00479 $this->published = $this->referencedfile->get_timecreated(); 00480 $this->updated = $this->referencedfile->get_timemodified(); 00481 $this->add_category('offline', 'resource_type'); 00482 } 00483 00487 protected function add_extra_links($dom, $entry) { 00488 $link = $dom->createElement('link'); 00489 $link->setAttribute('rel', 'enclosure'); 00490 $link->setAttribute('href', portfolio_format_leap2a::get_file_directory() . $this->referencedfile->get_filename()); 00491 $link->setAttribute('length', $this->referencedfile->get_filesize()); 00492 $link->setAttribute('type', $this->referencedfile->get_mimetype()); 00493 $entry->appendChild($link); 00494 } 00495 } 00496