MDL-10240:
[moodle-linuxchix.git] / mod / lesson / importppt.php
blobfb057f1a8746884f8d5f712441c10d66e4022b5e
1 <?php // $Id$
2 /**
3 * This is a very rough importer for powerpoint slides
4 * Export a powerpoint presentation with powerpoint as html pages
5 * Do it with office 2002 (I think?) and no special settings
6 * Then zip the directory with all of the html pages
7 * and the zip file is what you want to upload
8 *
9 * The script supports book and lesson.
11 * @version $Id$
12 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
13 * @package lesson
14 **/
16 require_once("../../config.php");
17 require_once("locallib.php");
19 $id = required_param('id', PARAM_INT); // Course Module ID
20 $pageid = optional_param('pageid', '', PARAM_INT); // Page ID
21 global $matches;
23 if (! $cm = get_coursemodule_from_id('lesson', $id)) {
24 error("Course Module ID was incorrect");
27 if (! $course = get_record("course", "id", $cm->course)) {
28 error("Course is misconfigured");
31 // allows for adaption for multiple modules
32 if(! $modname = get_field('modules', 'name', 'id', $cm->module)) {
33 error("Could not find module name");
36 if (! $mod = get_record($modname, "id", $cm->instance)) {
37 error("Course module is incorrect");
40 require_login($course->id, false);
41 $context = get_context_instance(CONTEXT_MODULE, $cm->id);
42 require_capability('mod/lesson:edit', $context);
44 $strimportppt = get_string("importppt", "lesson");
45 $strlessons = get_string("modulenameplural", "lesson");
47 $crumbs[] = array('name' => $strlessons, 'link' => "index.php?id=$course->id", 'type' => 'activity');
48 $crumbs[] = array('name' => format_string($mod->name,true), 'link' => "$CFG->wwwroot/mod/$modname/view.php?id=$cm->id", 'type' => 'activityinstance');
49 $crumbs[] = array('name' => $strimportppt, 'link' => '', 'type' => 'title');
51 $navigation = build_navigation($crumbs);
53 print_header_simple("$strimportppt", " $strimportppt", $navigation);
55 if ($form = data_submitted()) { /// Filename
57 if (empty($_FILES['newfile'])) { // file was just uploaded
58 notify(get_string("uploadproblem") );
61 if ((!is_uploaded_file($_FILES['newfile']['tmp_name']) or $_FILES['newfile']['size'] == 0)) {
62 notify(get_string("uploadnofilefound") );
64 } else { // Valid file is found
66 if ($rawpages = readdata($_FILES, $course->id, $modname)) { // first try to reall all of the data in
67 $pageobjects = extract_data($rawpages, $course->id, $mod->name, $modname); // parse all the html files into objects
68 clean_temp(); // all done with files so dump em
70 $mod_create_objects = $modname.'_create_objects';
71 $mod_save_objects = $modname.'_save_objects';
73 $objects = $mod_create_objects($pageobjects, $mod->id); // function to preps the data to be sent to DB
75 if(! $mod_save_objects($objects, $mod->id, $pageid)) { // sends it to DB
76 error("could not save");
78 } else {
79 error('could not get data');
82 echo "<hr>";
83 print_continue("$CFG->wwwroot/mod/$modname/view.php?id=$cm->id");
84 print_footer($course);
85 exit;
89 /// Print upload form
91 print_heading_with_help($strimportppt, "importppt", "lesson");
93 print_simple_box_start("center");
94 echo "<form id=\"theform\" enctype=\"multipart/form-data\" method=\"post\">";
95 echo "<input type=\"hidden\" name=\"id\" value=\"$cm->id\" />\n";
96 echo "<input type=\"hidden\" name=\"pageid\" value=\"$pageid\" />\n";
97 echo "<table cellpadding=\"5\">";
99 echo "<tr><td align=\"right\">";
100 print_string("upload");
101 echo ":</td><td>";
102 echo "<input name=\"newfile\" type=\"file\" size=\"50\" />";
103 echo "</td></tr><tr><td>&nbsp;</td><td>";
104 echo "<input type=\"submit\" name=\"save\" value=\"".get_string("uploadthisfile")."\" />";
105 echo "</td></tr>";
107 echo "</table>";
108 echo "</form>";
109 print_simple_box_end();
111 print_footer($course);
113 // START OF FUNCTIONS
115 function readdata($file, $courseid, $modname) {
116 // this function expects a zip file to be uploaded. Then it parses
117 // outline.htm to determine the slide path. Then parses each
118 // slide to get data for the content
120 global $CFG;
122 // create an upload directory in temp
123 make_upload_directory('temp/'.$modname);
125 $base = $CFG->dataroot."/temp/$modname/";
127 $zipfile = $_FILES["newfile"]["name"];
128 $tempzipfile = $_FILES["newfile"]["tmp_name"];
130 // create our directory
131 $path_parts = pathinfo($zipfile);
132 $dirname = substr($zipfile, 0, strpos($zipfile, '.'.$path_parts['extension'])); // take off the extension
133 if (!file_exists($base.$dirname)) {
134 mkdir($base.$dirname);
137 // move our uploaded file to temp/lesson
138 move_uploaded_file($tempzipfile, $base.$zipfile);
140 // unzip it!
141 unzip_file($base.$zipfile, $base, false);
143 $base = $base.$dirname; // update the base
145 // this is the file where we get the names of the files for the slides (in the correct order too)
146 $outline = $base.'/outline.htm';
148 $pages = array();
150 if (file_exists($outline) and is_readable($outline)) {
151 $outlinecontents = file_get_contents($outline);
152 $filenames = array();
153 preg_match_all("/javascript:GoToSld\('(.*)'\)/", $outlinecontents, $filenames); // this gets all of our files names
155 // file $pages with the contents of all of the slides
156 foreach ($filenames[1] as $file) {
157 $path = $base.'/'.$file;
158 if (is_readable($path)) {
159 $pages[$path] = file_get_contents($path);
160 } else {
161 return false;
164 } else {
165 // cannot find the outline, so grab all files that start with slide
166 $dh = opendir($base);
167 while (false !== ($file = readdir($dh))) { // read throug the directory
168 if ('slide' == substr($file, 0, 5)) { // check for name (may want to check extension later)
169 $path = $base.'/'.$file;
170 if (is_readable($path)) {
171 $pages[$path] = file_get_contents($path);
172 } else {
173 return false;
178 ksort($pages); // order them by file name
181 if (empty($pages)) {
182 return false;
185 return $pages;
188 function extract_data($pages, $courseid, $lessonname, $modname) {
189 // this function attempts to extract the content out of the slides
190 // the slides are ugly broken xml. and the xml is broken... yeah...
192 global $CFG;
193 global $matches;
195 $extratedpages = array();
197 // directory for images
198 make_mod_upload_directory($courseid); // make sure moddata is made
199 make_upload_directory($courseid.'/moddata/'.$modname, false); // we store our images in a subfolder in here
201 $imagedir = $CFG->dataroot.'/'.$courseid.'/moddata/'.$modname;
203 if ($CFG->slasharguments) {
204 $imagelink = $CFG->wwwroot.'/file.php/'.$courseid.'/moddata/'.$modname;
205 } else {
206 $imagelink = $CFG->wwwroot.'/file.php?file=/'.$courseid.'/moddata/'.$modname;
209 // try to make a unique subfolder to store the images
210 $lessonname = str_replace(' ', '_', $lessonname); // get rid of spaces
211 $i = 0;
212 while(true) {
213 if (!file_exists($imagedir.'/'.$lessonname.$i)) {
214 // ok doesnt exist so make the directory and update our paths
215 mkdir($imagedir.'/'.$lessonname.$i);
216 $imagedir = $imagedir.'/'.$lessonname.$i;
217 $imagelink = $imagelink.'/'.$lessonname.$i;
218 break;
220 $i++;
223 foreach ($pages as $file => $content) {
224 // to make life easier on our preg_match_alls, we strip out all tags except
225 // for div and img (where our content is). We want div because sometimes we
226 // can identify the content in the div based on the div's class
228 $tags = '<div><img>'; // should also allow <b><i>
229 $string = strip_tags($content,$tags);
230 //echo s($string);
232 $matches = array();
233 // this will look for a non nested tag that is closed
234 // want to allow <b><i>(maybe more) tags but when we do that
235 // the preg_match messes up.
236 preg_match_all("/(<([\w]+)[^>]*>)([^<\\2>]*)(<\/\\2>)/", $string, $matches);
237 //(<([\w]+)[^>]*>)([^<\\2>]*)(<\/\\2>) original pattern
238 //(<(div+)[^>]*>)[^(<div*)](<\/div>) work in progress
240 $path_parts = pathinfo($file);
241 $file = substr($path_parts['basename'], 0, strpos($path_parts['basename'], '.')); // get rid of the extension
243 $imgs = array();
244 // this preg matches all images
245 preg_match_all("/<img[^>]*(src\=\"(".$file."\_image[^>^\"]*)\"[^>]*)>/i", $string, $imgs);
247 // start building our page
248 $page = new stdClass;
249 $page->title = '';
250 $page->contents = array();
251 $page->images = array();
252 $page->source = $path_parts['basename']; // need for book only
254 // this foreach keeps the style intact. Found it doesn't help much. But if you want back uncomment
255 // this foreach and uncomment the line with the comment imgstyle in it. Also need to comment out
256 // the $page->images[]... line in the next foreach
257 /*foreach ($imgs[1] as $img) {
258 $page->images[] = '<img '.str_replace('src="', "src=\"$imagelink/", $img).' />';
260 foreach ($imgs[2] as $img) {
261 copy($path_parts['dirname'].'/'.$img, $imagedir.'/'.$img);
262 $page->images[] = "<img src=\"$imagelink/$img\" title=\"$img\" />"; // comment out this line if you are using the above foreach loop
264 for($i = 0; $i < count($matches[1]); $i++) { // go through all of our div matches
266 $class = isolate_class($matches[1][$i]); // first step in isolating the class
268 // check for any static classes
269 switch ($class) {
270 case 'T': // class T is used for Titles
271 $page->title = $matches[3][$i];
272 break;
273 case 'B': // I would guess that all bullet lists would start with B then go to B1, B2, etc
274 case 'B1': // B1-B4 are just insurance, should just hit B and all be taken care of
275 case 'B2':
276 case 'B3':
277 case 'B4':
278 $page->contents[] = build_list('<ul>', $i, 0); // this is a recursive function that will grab all the bullets and rebuild the list in html
279 break;
280 default:
281 if ($matches[3][$i] != '&#13;') { // odd crap generated... sigh
282 if (substr($matches[3][$i], 0, 1) == ':') { // check for leading : ... hate MS ...
283 $page->contents[] = substr($matches[3][$i], 1); // get rid of :
284 } else {
285 $page->contents[] = $matches[3][$i];
288 break;
291 /*if (count($page->contents) == 0) { // didnt find anything, grab everything
292 // potential to pull in a lot of crap
293 for($i = 0; $i < count($matches[1]); $i++) {
294 //if($class = isolate_class($matches[1][$i])) {
295 //if ($class == 'O') {
296 if ($matches[3][$i] != '&#13;') { // odd crap generated... sigh
297 if (substr($matches[3][$i], 0, 1) == ':') { // check for leading : ... hate MS ...
298 $page->contents[] = substr($matches[3][$i], 1); // get rid of :
299 } else {
300 $page->contents[] = $matches[3][$i];
307 // add the page to the array;
308 $extratedpages[] = $page;
310 } // end $pages foreach loop
312 return $extratedpages;
316 A recursive function to build a html list
318 function build_list($list, &$i, $depth) {
319 global $matches; // not sure why I global this...
321 while($i < count($matches[1])) {
323 $class = isolate_class($matches[1][$i]);
325 if (strstr($class, 'B')) { // make sure we are still working with bullet classes
326 if ($class == 'B') {
327 $this_depth = 0; // calling class B depth 0
328 } else {
329 // set the depth number. So B1 is depth 1 and B2 is depth 2 and so on
330 $this_depth = substr($class, 1);
331 if (!is_numeric($this_depth)) {
332 error("Depth not parsed!");
335 if ($this_depth < $depth) {
336 // we are moving back a level in the nesting
337 break;
339 if ($this_depth > $depth) {
340 // we are moving in a lvl in nesting
341 $list .= '<ul>';
342 $list = build_list($list, $i, $this_depth);
343 // once we return back, should go to the start of the while
344 continue;
346 // no depth changes, so add the match to our list
347 if ($cleanstring = ppt_clean_text($matches[3][$i])) {
348 $list .= '<li>'.ppt_clean_text($matches[3][$i]).'</li>';
350 $i++;
351 } else {
352 // not a B class, so get out of here...
353 break;
356 // end the list and return it
357 $list .= '</ul>';
358 return $list;
363 Given an html tag, this function will
365 function isolate_class($string) {
366 if($class = strstr($string, 'class=')) { // first step in isolating the class
367 $class = substr($class, strpos($class, '=')+1); // this gets rid of <div blawblaw class= there are no "" or '' around the class name ...sigh...
368 if (strstr($class, ' ')) {
369 // spaces found, so cut off everything off after the first space
370 return substr($class, 0, strpos($class, ' '));
371 } else {
372 // no spaces so nothing else in the div tag, cut off the >
373 return substr($class, 0, strpos($class, '>'));
375 } else {
376 // no class defined in the tag
377 return '';
382 This function strips off the random chars that ppt puts infront of bullet lists
384 function ppt_clean_text($string) {
385 $chop = 1; // default: just a single char infront of the content
387 // look for any other crazy things that may be infront of the content
388 if (strstr($string, '&lt;') and strpos($string, '&lt;') == 0) { // look for the &lt; in the sting and make sure it is in the front
389 $chop = 4; // increase the $chop
391 // may need to add more later....
393 $string = substr($string, $chop);
395 if ($string != '&#13;') {
396 return $string;
397 } else {
398 return false;
403 Clean up the temp directory
405 function clean_temp() {
406 global $CFG;
407 // this function is broken, use it to clean up later
408 // should only clean up what we made as well because someone else could be importing ppt as well
409 //delDirContents($CFG->dataroot.'/temp/lesson');
413 Creates objects an object with the page and answers that are to be inserted into the database
415 function lesson_create_objects($pageobjects, $lessonid) {
417 $branchtables = array();
418 $branchtable = new stdClass;
420 // all pages have this info
421 $page->lessonid = $lessonid;
422 $page->prevpageid = 0;
423 $page->nextpageid = 0;
424 $page->qtype = LESSON_BRANCHTABLE;
425 $page->qoption = 0;
426 $page->layout = 1;
427 $page->display = 1;
428 $page->timecreated = time();
429 $page->timemodified = 0;
431 // all answers are the same
432 $answer->lessonid = $lessonid;
433 $answer->jumpto = LESSON_NEXTPAGE;
434 $answer->grade = 0;
435 $answer->score = 0;
436 $answer->flags = 0;
437 $answer->timecreated = time();
438 $answer->timemodified = 0;
439 $answer->answer = "Next";
440 $answer->response = "";
442 $answers[] = clone($answer);
444 $answer->jumpto = LESSON_PREVIOUSPAGE;
445 $answer->answer = "Previous";
447 $answers[] = clone($answer);
449 $branchtable->answers = $answers;
451 $i = 1;
453 foreach ($pageobjects as $pageobject) {
454 $temp = prep_page($pageobject, $i); // makes our title and contents
455 $page->title = $temp->title;
456 $page->contents = $temp->contents;
457 $branchtable->page = clone($page); // add the page
458 $branchtables[] = clone($branchtable); // add it all to our array
459 $i++;
462 return $branchtables;
466 Creates objects an chapter object that is to be inserted into the database
468 function book_create_objects($pageobjects, $bookid) {
470 $chapters = array();
471 $chapter = new stdClass;
473 // same for all chapters
474 $chapter->bookid = $bookid;
475 $chapter->pagenum = count_records('book_chapters', 'bookid', $bookid)+1;
476 $chapter->timecreated = time();
477 $chapter->timemodified = time();
478 $chapter->subchapter = 0;
480 $i = 1;
481 foreach ($pageobjects as $pageobject) {
482 $page = prep_page($pageobject, $i); // get title and contents
483 $chapter->importsrc = addslashes($pageobject->source); // add the source
484 $chapter->title = $page->title;
485 $chapter->content = $page->contents;
486 $chapters[] = $chapter;
488 // increment our page number and our counter
489 $chapter->pagenum = $chapter->pagenum + 1;
490 $i++;
493 return $chapters;
497 Builds the title and content strings from an object
499 function prep_page($pageobject, $count) {
500 if ($pageobject->title == '') {
501 $page->title = "Page $count"; // no title set so make a generic one
502 } else {
503 $page->title = addslashes($pageobject->title);
506 $page->contents = '';
508 // nab all the images first
509 foreach ($pageobject->images as $image) {
510 $image = str_replace("\n", '', $image);
511 $image = str_replace("\r", '', $image);
512 $image = str_replace("'", '"', $image); // imgstyle
514 $page->contents .= addslashes($image);
516 // go through the contents array and put <p> tags around each element and strip out \n which I have found to be uneccessary
517 foreach ($pageobject->contents as $content) {
518 $content = str_replace("\n", '', $content);
519 $content = str_replace("\r", '', $content);
520 $content = str_replace('&#13;', '', $content); // puts in returns?
521 $content = '<p>'.$content.'</p>';
522 $page->contents .= addslashes($content);
524 return $page;
528 Saves the branchtable objects to the DB
530 function lesson_save_objects($branchtables, $lessonid, $after) {
531 // first set up the prevpageid and nextpageid
532 if ($after == 0) { // adding it to the top of the lesson
533 $prevpageid = 0;
534 // get the id of the first page. If not found, then no pages in the lesson
535 if (!$nextpageid = get_field('lesson_pages', 'id', 'prevpageid', 0, 'lessonid', $lessonid)) {
536 $nextpageid = 0;
538 } else {
539 // going after an actual page
540 $prevpageid = $after;
541 $nextpageid = get_field('lesson_pages', 'nextpageid', 'id', $after);
544 foreach ($branchtables as $branchtable) {
546 // set the doubly linked list
547 $branchtable->page->nextpageid = $nextpageid;
548 $branchtable->page->prevpageid = $prevpageid;
550 // insert the page
551 if(!$id = insert_record('lesson_pages', $branchtable->page)) {
552 error("insert page");
555 // update the link of the page previous to the one we just updated
556 if ($prevpageid != 0) { // if not the first page
557 if (!set_field("lesson_pages", "nextpageid", $id, "id", $prevpageid)) {
558 error("Insert page: unable to update next link $prevpageid");
562 // insert the answers
563 foreach ($branchtable->answers as $answer) {
564 $answer->pageid = $id;
565 if(!insert_record('lesson_answers', $answer)) {
566 error("insert answer $id");
570 $prevpageid = $id;
573 // all done with inserts. Now check to update our last page (this is when we import between two lesson pages)
574 if ($nextpageid != 0) { // if the next page is not the end of lesson
575 if (!set_field("lesson_pages", "prevpageid", $id, "id", $nextpageid)) {
576 error("Insert page: unable to update next link $prevpageid");
580 return true;
584 Save the chapter objects to the database
586 function book_save_objects($chapters, $bookid, $pageid='0') {
587 // nothing fancy, just save them all in order
588 foreach ($chapters as $chapter) {
589 if (!$chapter->id = insert_record('book_chapters', $chapter)) {
590 error('Could not update your book');
593 return true;