MDL-29956: Common Cartridge should handle HTML content more natively (r17188, r17210...
authorMark Nielsen <mark@moodlerooms.com>
Wed, 26 Oct 2011 17:38:36 +0000 (10:38 -0700)
committerEloy Lafuente (stronk7) <stronk7@moodle.org>
Mon, 14 Nov 2011 20:25:08 +0000 (21:25 +0100)
Also implemented support for LTI icon during import.

21 files changed:
backup/cc/cc112moodle.php
backup/cc/cc2moodle.php
backup/cc/cc_includes.php
backup/cc/cc_lib/cc_converter_lti.php
backup/cc/cc_lib/cc_converter_page.php [new file with mode: 0644]
backup/cc/cc_lib/cc_converters.php
backup/cc/cc_lib/cc_general.php
backup/cc/cc_lib/cc_page.php [new file with mode: 0644]
backup/cc/cc_lib/cc_utils.php
backup/cc/cc_lib/gral_lib/ccdependencyparser.php
backup/cc/cc_lib/xmlbase.php
backup/cc/entities.class.php
backup/cc/entities11.class.php
backup/cc/entity.label.class.php
backup/cc/entity.resource.class.php
backup/cc/entity11.lti.class.php
backup/cc/entity11.resource.class.php
backup/cc/sheets/course_modules_mod_lti.xml
backup/cc/sheets/course_modules_mod_resource.xml
backup/converter/imscc1/backuplib.php [new file with mode: 0644]
backup/converter/imscc1/lib.php [new file with mode: 0644]

index d9c03e2..2a4fdfe 100644 (file)
@@ -63,8 +63,11 @@ class cc112moodle extends cc2moodle {
     public function generate_moodle_xml () {
 
         global $CFG;
+        $cdir = static::$path_to_manifest_folder . DIRECTORY_SEPARATOR . 'course_files';
 
-        mkdir(static::$path_to_manifest_folder . DIRECTORY_SEPARATOR . 'course_files');
+        if (!file_exists($cdir)) {
+            mkdir($cdir);
+        }
 
         $sheet_base = static::loadsheet(SHEET_BASE);
 
@@ -175,6 +178,7 @@ class cc112moodle extends cc2moodle {
     }
 
     protected function create_node_course_modules_mod () {
+        $labels = new cc_label();
         $resources = new cc11_resource();
         $forums = new cc11_forum();
         $quiz = new cc11_quiz();
@@ -182,6 +186,9 @@ class cc112moodle extends cc2moodle {
 
         static::log_action('Creating node: COURSE/MODULES/MOD');
 
+        // LABELS
+        $node_course_modules_mod_label = $labels->generate_node();
+
         // RESOURCES (WEB CONTENT AND WEB LINK)
         $node_course_modules_mod_resource = $resources->generate_node();
 
@@ -194,7 +201,8 @@ class cc112moodle extends cc2moodle {
         //BasicLTI
         $node_course_modules_mod_basiclti = $basiclti->generate_node();
 
-        $node_course_modules = $node_course_modules_mod_resource .
+        $node_course_modules = $node_course_modules_mod_label.
+                               $node_course_modules_mod_resource .
                                $node_course_modules_mod_forum .
                                $node_course_modules_mod_quiz .
                                $node_course_modules_mod_basiclti;
index 43d3134..b3c1a2f 100644 (file)
@@ -141,7 +141,11 @@ class cc2moodle {
 
         global $CFG;
 
-        mkdir(static::$path_to_manifest_folder . DIRECTORY_SEPARATOR . 'course_files');
+        $cdir = static::$path_to_manifest_folder . DIRECTORY_SEPARATOR . 'course_files';
+
+        if (!file_exists($cdir)) {
+            mkdir($cdir);
+        }
 
         $sheet_base = static::loadsheet(SHEET_BASE);
 
@@ -295,7 +299,8 @@ class cc2moodle {
 
         // QUIZ
         $node_course_modules_mod_quiz = $quiz->generate_node_course_modules_mod();
-        $node_course_modules = /*$node_course_modules_mod_label .*/ $node_course_modules_mod_resource . $node_course_modules_mod_forum . $node_course_modules_mod_quiz;
+        //TODO: label
+        $node_course_modules = $node_course_modules_mod_label . $node_course_modules_mod_resource . $node_course_modules_mod_forum . $node_course_modules_mod_quiz;
 
         return $node_course_modules;
     }
@@ -527,7 +532,8 @@ class cc2moodle {
         $forum_mod = $count_forum ? $this->create_mod_info_details_mod(MOODLE_TYPE_FORUM, $forum_instance) : '';
         $label_mod = $count_label ? $this->create_mod_info_details_mod(MOODLE_TYPE_LABEL, $label_instance) : '';
 
-        return /*$label_mod .*/ $resource_mod . $quiz_mod . $forum_mod;
+        //TODO: label
+        return $label_mod . $resource_mod . $quiz_mod . $forum_mod;
 
     }
 
@@ -552,6 +558,11 @@ class cc2moodle {
         for ($i = 1; $i <= $instances_quantity; $i++) {
 
             $user_info = ($instances[$i - 1]['common_cartriedge_type'] == static::CC_TYPE_FORUM) ? 'true' : 'false';
+            if ($instances[$i - 1]['common_cartriedge_type'] == static::CC_TYPE_EMPTY) {
+                if ($instances[$i - 1]['deep'] <= ROOT_DEEP ) {
+                    continue;
+                }
+            }
 
             $replace_values = array($instances[$i - 1]['instance'],
                                     htmlentities($instances[$i - 1]['title']),
@@ -577,7 +588,7 @@ class cc2moodle {
 
                 $array_index++;
 
-                if ($item->nodeName == "item") {
+                if ($item->nodeName == "item")  {
 
                     $identifierref = $xpath->query('@identifierref', $item);
                     $identifierref = !empty($identifierref->item(0)->nodeValue) ? $identifierref->item(0)->nodeValue : '';
@@ -587,7 +598,6 @@ class cc2moodle {
 
                     $cc_type = $this->get_item_cc_type($identifierref);
                     $moodle_type = $this->convert_to_moodle_type($cc_type);
-
                 }
                 elseif ($item->nodeName == "resource")  {
 
@@ -615,8 +625,10 @@ class cc2moodle {
                 static::$instances['index'][$array_index]['resource_indentifier'] = $identifierref;
 
                 static::$instances['instances'][$moodle_type][] = array('title' => $title,
-                        'instance' => static::$instances['index'][$array_index]['instance'],
-                        'common_cartriedge_type' => $cc_type , 'resource_indentifier' => $identifierref);
+                                                                        'instance' => static::$instances['index'][$array_index]['instance'],
+                                                                        'common_cartriedge_type' => $cc_type,
+                                                                        'resource_indentifier' => $identifierref,
+                                                                        'deep' => $level);
 
                 $more_items = $xpath->query('imscc:item', $item);
 
@@ -677,9 +689,10 @@ class cc2moodle {
         if ($cc_type == static::CC_TYPE_QUESTION_BANK) {
             $type = MOODLE_TYPE_QUESTION_BANK;
         }
-        /*if ($cc_type == static::CC_TYPE_EMPTY) {
+        //TODO: label
+        if ($cc_type == static::CC_TYPE_EMPTY) {
             $type = MOODLE_TYPE_LABEL;
-        }*/
+        }
 
         return $type;
     }
index 8a5817a..bd3369c 100644 (file)
@@ -38,4 +38,5 @@ require_once($CFG->dirroot .'/backup/cc/cc_lib/cc_converter_forum.php');
 require_once($CFG->dirroot .'/backup/cc/cc_lib/cc_converter_url.php');
 require_once($CFG->dirroot .'/backup/cc/cc_lib/cc_converter_resource.php');
 require_once($CFG->dirroot .'/backup/cc/cc_lib/cc_converter_quiz.php');
+require_once($CFG->dirroot .'/backup/cc/cc_lib/cc_converter_page.php');
 require_once($CFG->dirroot .'/backup/cc/cc_lib/cc_convert_moodle2.php');
index 9a1a255..458f042 100644 (file)
@@ -37,6 +37,7 @@ class cc_converter_lti extends cc_converter {
         $rt = new basicltil1_resurce_file();
         $contextid = $this->doc->nodeValue('/activity/@contextid');
         $title = $this->doc->nodeValue('/activity/lti/name');
+        $text = $this->doc->nodeValue('/activity/lti/intro');
         $rt->set_title($title);
         $result = cc_helpers::process_linked_files($text,
                                                    $this->manifest,
diff --git a/backup/cc/cc_lib/cc_converter_page.php b/backup/cc/cc_lib/cc_converter_page.php
new file mode 100644 (file)
index 0000000..bd53e90
--- /dev/null
@@ -0,0 +1,59 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+/**
+ * @package    backup-convert
+ * @subpackage cc-library
+ * @copyright  2011 Darko Miletic <dmiletic@moodlerooms.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once 'cc_converters.php';
+require_once 'cc_general.php';
+require_once 'cc_page.php';
+
+class cc_converter_page extends cc_converter {
+    public function __construct(cc_i_item &$item, cc_i_manifest &$manifest, $rootpath, $path){
+        $this->cc_type     = cc_version11::webcontent;
+        $this->defaultfile = 'page.xml';
+        $this->defaultname = uniqid().'.html';
+        parent::__construct($item, $manifest, $rootpath, $path);
+    }
+
+    public function convert($outdir) {
+        $rt = new page11_resurce_file();
+        $title = $this->doc->nodeValue('/activity/page/name');
+        $intro = $this->doc->nodeValue('/activity/page/intro');
+        $contextid = $this->doc->nodeValue('/activity/@contextid');
+        $pagecontent = $this->doc->nodeValue('/activity/page/content');
+        $rt->set_title($title);
+        $rawname = str_replace(' ', '_', strtolower(trim(clean_param($title, PARAM_FILE))));
+        if (!empty($rawname)) {
+            $this->defaultname = $rawname.".html";
+        }
+
+        $result = cc_helpers::process_linked_files( $pagecontent,
+                                                    $this->manifest,
+                                                    $this->rootpath,
+                                                    $contextid,
+                                                    $outdir,
+                                                    true);
+        $rt->set_content($result[0]);
+        $rt->set_intro($intro);
+        //store everything
+        $this->store($rt, $outdir, $title, $result[1]);
+        return true;
+    }
+}
index d531964..26e6d37 100644 (file)
@@ -117,13 +117,16 @@ abstract class cc_converter {
     protected function store(general_cc_file $doc, $outdir, $title, $deps = null) {
         $rdir = new cc_resource_location($outdir);
         $rtp = $rdir->fullpath(true).$this->defaultname;
-        $doc->saveTo($rtp);
-        $resource = new cc_resource($rdir->rootdir(), $this->defaultname, $rdir->dirname(true));
-        $resource->dependency = empty($deps) ? array() : $deps;
-        $res = $this->manifest->add_resource($resource, null, $this->cc_type);
-        $resitem = new cc_item();
-        $resitem->attach_resource($res[0]);
-        $resitem->title = $title;
-        $this->item->add_child_item($resitem);
+        if ( $doc->saveTo($rtp) ) {
+            $resource = new cc_resource($rdir->rootdir(), $this->defaultname, $rdir->dirname(true));
+            $resource->dependency = empty($deps) ? array() : $deps;
+            $res = $this->manifest->add_resource($resource, null, $this->cc_type);
+            $resitem = new cc_item();
+            $resitem->attach_resource($res[0]);
+            $resitem->title = $title;
+            $this->item->add_child_item($resitem);
+        } else {
+            throw new RuntimeException("Unable to save file {$rtp}!");
+        }
     }
 }
index 23ffd43..c264d7f 100644 (file)
@@ -60,10 +60,13 @@ class general_cc_file extends XMLGenericDocument {
             $vt = empty($schemaLocation) ? '' : ' ';
             $schemaLocation .= $vt.$this->ccnamespaces[$key].' '.$value;
         }
-        $this->append_new_attribute_ns($rootel,
-                                       $this->ccnamespaces['xsi'],
-                                       'xsi:schemaLocation',
-                                        $schemaLocation);
+
+        if (!empty($schemaLocation) && isset($this->ccnamespaces['xsi'])) {
+            $this->append_new_attribute_ns($rootel,
+                                           $this->ccnamespaces['xsi'],
+                                           'xsi:schemaLocation',
+                                            $schemaLocation);
+        }
 
         $this->root = $rootel;
     }
diff --git a/backup/cc/cc_lib/cc_page.php b/backup/cc/cc_lib/cc_page.php
new file mode 100644 (file)
index 0000000..1da15a8
--- /dev/null
@@ -0,0 +1,110 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+/**
+ * @package    backup-convert
+ * @copyright  2011 Darko Miletic <dmiletic@moodlerooms.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once 'cc_general.php';
+
+class page11_resurce_file extends general_cc_file {
+    protected $rootns = 'xmlns';
+    protected $rootname = 'html';
+    protected $ccnamespaces = array('xmlns' => 'http://www.w3.org/1999/xhtml');
+
+    protected $content = null;
+    protected $title = null;
+    protected $intro = null;
+
+    public function set_content($value) {
+        $this->content = $value;
+    }
+
+    public function set_title($value) {
+        $this->title = $value;
+    }
+
+    public function set_intro($value) {
+        $this->intro = htmlspecialchars(strip_tags($value), ENT_COMPAT, 'UTF-8', false);
+    }
+
+    protected function on_create() {
+        $impl = new DOMImplementation();
+        $dtd  = $impl->createDocumentType( 'html',
+                                           '-//W3C//DTD XHTML 1.0 Strict//EN',
+                                           'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd');
+        $doc = $impl->createDocument($this->ccnamespaces[$this->rootns], null, $dtd);
+        $doc->formatOutput = true;
+        $doc->preserveWhiteSpace = true;
+        $this->doc = $doc;
+        parent::on_create();
+    }
+
+    public function on_save() {
+
+        $rns = $this->ccnamespaces[$this->rootns];
+        //Add the basic tags
+        $head = $this->append_new_element_ns($this->root, $rns, 'head');
+        $this->append_new_attribute_ns($head, $rns, 'profile', 'http://dublincore.org/documents/dc-html/');
+
+        //Linking Dublin Core Metadata 1.1
+        $link_dc = $this->append_new_element_ns($head, $rns, 'link');
+        $this->append_new_attribute_ns($link_dc, $rns, 'rel', 'schema.DC');
+        $this->append_new_attribute_ns($link_dc, $rns, 'href', 'http://purl.org/dc/elements/1.1/');
+        $link_dcterms = $this->append_new_element_ns($head, $rns, 'link');
+        $this->append_new_attribute_ns($link_dcterms, $rns, 'rel', 'schema.DCTERMS');
+        $this->append_new_attribute_ns($link_dcterms, $rns, 'href', 'http://purl.org/dc/terms/');
+        //Content type
+        $meta_type = $this->append_new_element_ns($head, $rns, 'meta');
+        $this->append_new_attribute_ns($meta_type, $rns, 'name'   , 'DC.type'         );
+        $this->append_new_attribute_ns($meta_type, $rns, 'scheme' , 'DCTERMS.DCMIType');
+        $this->append_new_attribute_ns($meta_type, $rns, 'content', 'Text'            );
+
+        //Content description
+        if (!empty($this->intro)) {
+            $meta_description = $this->append_new_element_ns($head, $rns     , 'meta'          );
+            $this->append_new_attribute_ns($meta_description, $rns, 'name'   , 'DC.description');
+            $this->append_new_attribute_ns($meta_description, $rns, 'content', $this->intro    );
+        }
+
+        $meta = $this->append_new_element_ns($head, $rns, 'meta');
+        $this->append_new_attribute_ns($meta, $rns, 'http-equiv', 'Content-type');
+        $this->append_new_attribute_ns($meta, $rns, 'content', 'text/html; charset=UTF-8');
+        //set the title
+        $title = $this->append_new_element_ns($head, $rns, 'title', $this->title);
+        $body = $this->append_new_element_ns($this->root, $rns, 'body');
+        //We are unable to use DOM for embedding HTML due to numerous content errors
+        //Therefore we place a dummy tag that will be later replaced with the real content
+        $this->append_new_element_ns($body, $rns, 'div', '##REPLACE##');
+
+        return true;
+    }
+
+    public function saveTo($fname) {
+        $result = $this->on_save();
+        if ($result) {
+            $dret = str_replace('<?xml version="1.0"?>'."\n", '', $this->viewXML());
+            $dret = str_replace('<div>##REPLACE##</div>', $this->content, $dret);
+            $result = (file_put_contents($fname, $dret) !== false);
+            if ($result) {
+                $this->filename = $fname;
+                $this->processPath();
+            }
+        }
+        return $result;
+    }
+}
\ No newline at end of file
index c341833..5bb6a30 100644 (file)
@@ -73,6 +73,31 @@ abstract class cc_helpers {
         return $dirname;
     }
 
+    public static function build_query($attributes, $search) {
+        $result = '';
+        foreach ($attributes as $attribute) {
+            if ($result != '') {
+                $result .= ' | ';
+            }
+            $result .= "//*[starts-with(@{$attribute},'{$search}')]/@{$attribute}";
+        }
+        return $result;
+    }
+
+    public static function process_embedded_files(&$doc, $attributes, $search, $customslash = null) {
+        $result = array();
+        $query = self::build_query($attributes, $search);
+        $list = $doc->nodeList($query);
+        foreach ($list as $filelink) {
+            $rvalue = str_replace($search, '', $filelink->nodeValue);
+            if (!empty($customslash)) {
+                $rvalue = str_replace($customslash, '/', $rvalue);
+            }
+            $result[] = rawurldecode($rvalue);
+        }
+        return $result;
+    }
+
     /**
      *
      * Get list of embedded files
@@ -82,12 +107,13 @@ abstract class cc_helpers {
     public static function embedded_files($html) {
         $result = array();
         $doc = new XMLGenericDocument();
+        $doc->doc->validateOnParse = false;
+        $doc->doc->strictErrorChecking = false;
         if (!empty($html) && $doc->loadHTML($html)) {
-            $list = $doc->nodeList("//img[starts-with(@src,'@@PLUGINFILE@@')]/@src | //a[starts-with(@href,'@@PLUGINFILE@@')]/@href");
-            foreach ($list as $filelink) {
-                $rawvalue = rawurldecode(str_replace('@@PLUGINFILE@@', '', $filelink->nodeValue));
-                $result[] = $rawvalue;
-            }
+            $attributes = array('src', 'href');
+            $result1 = self::process_embedded_files($doc, $attributes, '@@PLUGINFILE@@');
+            $result2 = self::process_embedded_files($doc, $attributes, '$@FILEPHP@$', '$@SLASH@$');
+            $result = array_merge($result1, $result2);
         }
         return $result;
     }
@@ -139,10 +165,10 @@ abstract class cc_helpers {
             if (!$allinone) {
                 $rdir = new cc_resource_location($outdir);
             }
-            $rtp = $rdir->fullpath(true).$clean_filename;
+            $rtp = $rdir->fullpath().$values[7].$clean_filename;
             //Are there any relative virtual directories?
             //let us try to recreate them
-            $justdir = $rdir->fullpath(true).$values[7];
+            $justdir = $rdir->fullpath(false).$values[7];
             if (!file_exists($justdir)) {
                 if (!mkdir($justdir, 0777, true)) {
                     throw new RuntimeException('Unable to create directories!');
@@ -153,9 +179,15 @@ abstract class cc_helpers {
             if (!copy($source, $rtp)) {
                 throw new RuntimeException('Unable to copy files!');
             }
-            $resource = new cc_resource($rdir->rootdir(), $clean_filename, $rdir->dirname(true));
+            $resource = new cc_resource($rdir->rootdir(),
+                                        $values[7].$clean_filename,
+                                        $rdir->dirname(false));
             $res = $manifest->add_resource($resource, null, cc_version11::webcontent);
-            pkg_static_resources::instance()->add($virtual, $res[0], $rdir->dirname(true).$clean_filename, $values[1], $resource);
+            pkg_static_resources::instance()->add($virtual,
+                                                  $res[0],
+                                                  $rdir->dirname(false).$values[7].$clean_filename,
+                                                  $values[1],
+                                                  $resource);
         }
 
         pkg_static_resources::instance()->finished = true;
@@ -214,7 +246,7 @@ abstract class cc_helpers {
         return $result;
     }
 
-    public static function process_linked_files($content, cc_i_manifest &$manifest, $packageroot, $contextid, $outdir) {
+    public static function process_linked_files($content, cc_i_manifest &$manifest, $packageroot, $contextid, $outdir, $webcontent = false) {
         /**
         - detect all embedded files
         - locate their physical counterparts in moodle 2 backup
@@ -233,11 +265,16 @@ abstract class cc_helpers {
                                                  $packageroot,
                                                  $contextid,
                                                  $outdir);
+            $replaceprefix = $webcontent ? '' : '$IMS-CC-FILEBASE$';
             foreach ($lfiles as $lfile) {
                 if (array_key_exists($lfile, $files)) {
-                    $filename = rawurlencode(basename($lfile));
-                    $content = str_replace('@@PLUGINFILE@@/'.$filename,
-                                           '$IMS-CC-FILEBASE$../'.$files[$lfile][1],
+                    $filename = str_replace('%2F', '/',rawurlencode($lfile));
+                    $content = str_replace('@@PLUGINFILE@@'.$filename,
+                                           $replaceprefix.'../'.$files[$lfile][1],
+                                           $content);
+                    //for the legacy stuff
+                    $content = str_replace('$@FILEPHP@$'.str_replace('/','$@SLASH@$',$filename),
+                                           $replaceprefix.'../'.$files[$lfile][1],
                                            $content);
                     $deps[] = $files[$lfile][0];
                 }
index 5992b8e..e01557c 100644 (file)
@@ -14,7 +14,7 @@
 // You should have received a copy of the GNU General Public License
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
-require_once $CFG->dirroot .'/backup/cc/cc_lib/xmlbase.php';
+require_once dirname(__FILE__) .'/../xmlbase.php';
 require_once 'cssparser.php';
 require_once 'pathutils.php';
 
index a3e7050..5552199 100644 (file)
@@ -34,7 +34,7 @@ class XMLGenericDocument {
      * @var DOMXPath
      */
     protected $dxpath = null;
-    private   $filename;
+    protected   $filename;
     private   $filepath;
     private   $isloaded         = false;
     private   $arrayPrefixNS    = array();
@@ -74,7 +74,7 @@ class XMLGenericDocument {
        }
 
        public function viewXML (){
-           return $this->doc->saveXML;
+           return $this->doc->saveXML();
        }
 
        public function registerNS($prefix, $nsuri) {
@@ -402,7 +402,7 @@ class XMLGenericDocument {
                }
        }
 
-       private function processPath(){
+       protected function processPath(){
                $path_parts = pathinfo($this->filename);
                $this->filepath = array_key_exists('dirname',$path_parts) ? $path_parts['dirname']."/" : '';
        }
index 9dfb396..a221d2c 100644 (file)
@@ -25,6 +25,38 @@ defined('MOODLE_INTERNAL') or die('Direct access to this script is forbidden.');
 
 class entities {
 
+    protected function prepare_content($content) {
+        $result = $content;
+        if (empty($result)) {
+            return '';
+        }
+        $encoding = null;
+        $dom = new DOMDocument();
+        try {
+            if ($dom->loadHTML($content)) {
+                $encoding = $dom->xmlEncoding;
+            }
+        } catch(DOMException $e) {
+            //silence the exception if any
+        }
+        if (empty($encoding)) {
+            $encoding = mb_detect_encoding($content, 'auto', true);
+        }
+        if (!empty($encoding) && !mb_check_encoding($content, 'UTF-8')) {
+            $result = mb_convert_encoding($content, 'UTF-8', $encoding);
+        }
+
+        // See if we can strip off body tag and anything outside of it
+        foreach (array('body', 'html') as $tagname) {
+            $regex = str_replace('##', $tagname, "/<##[^>]*>(.+)<\/##>/is");
+            if (preg_match($regex, $result, $matches)) {
+                $result = $matches[1];
+                break;
+            }
+        }
+        return $result;
+    }
+
     public function load_xml_resource ($path_to_file) {
 
         $resource = new DOMDocument();
@@ -173,13 +205,14 @@ class entities {
 
                 cc2moodle::log_action('Copy the file: ' . $source . ' to ' . $destination);
 
-                //echo 'Copy the file: ' . $source . ' to ' . $destination . '<br>';
-
                 if (!file_exists($destination_directory)) {
                     mkdir($destination_directory, $CFG->directorypermissions, true);
                 }
 
-                $copy_success = @copy($source, $destination);
+                $copy_success = true;
+                if (is_file($source)) {
+                    $copy_success = @copy($source, $destination);
+                }
 
                 if (!$copy_success) {
                     notify('WARNING: Cannot copy the file ' . $source . ' to ' . $destination);
@@ -201,6 +234,11 @@ class entities {
 
             if (!empty($files) && ($files->length > 0)) {
                 foreach ($files as $file) {
+                    //omit html files
+                    $ext = strtolower(pathinfo($file->nodeValue, PATHINFO_EXTENSION));
+                    if (in_array($ext, array('html', 'htm', 'xhtml'))) {
+                        continue;
+                    }
                     $all_files[] = $file;
                 }
             }
@@ -208,6 +246,24 @@ class entities {
             unset($files);
         }
 
+        //are there any labels?
+        $xquery = "//imscc:item/imscc:item/imscc:item[imscc:title][not(@identifierref)]";
+        $labels = $xpath->query($xquery);
+        if (!empty($labels) && ($labels->length > 0)) {
+            $tname = 'course_files';
+            $dpath = cc2moodle::$path_to_manifest_folder . DIRECTORY_SEPARATOR . $tname;
+            $fpath = $dpath . DIRECTORY_SEPARATOR . 'folder.gif';
+            $rfpath = $tname.'/folder.gif';
+
+            if (!file_exists($dpath)) {
+                mkdir($dpath);
+            }
+            //copy the folder.gif file
+            $folder_gif = "{$CFG->dirroot}/pix/f/folder.gif";
+            copy($folder_gif, $fpath);
+            $all_files[] = $rfpath;
+        }
+
         $all_files = empty($all_files) ? '' : $all_files;
 
         return $all_files;
index 0c1cd34..96c0873 100644 (file)
@@ -35,6 +35,7 @@ class entities11 extends entities {
     }
 
     protected function get_all_files () {
+        global $CFG;
         $all_files = array();
         $xpath = cc2moodle::newx_path(cc112moodle::$manifest, cc112moodle::$namespaces);
         foreach (cc112moodle::$restypes as $type) {
@@ -44,11 +45,33 @@ class entities11 extends entities {
                 continue;
             }
             foreach ($files as $file) {
+                //omit html files
+                //this is a bit too simplistic
+                $ext = strtolower(pathinfo($file->nodeValue, PATHINFO_EXTENSION));
+                if (in_array($ext, array('html', 'htm', 'xhtml'))) {
+                    continue;
+                }
                 $all_files[] = $file;
             }
             unset($files);
         }
 
+        //are there any labels?
+        $xquery = "//imscc:item/imscc:item/imscc:item[imscc:title][not(@identifierref)]";
+        $labels = $xpath->query($xquery);
+        if (!empty($labels) && ($labels->length > 0)) {
+            $tname = 'course_files';
+            $dpath = cc2moodle::$path_to_manifest_folder . DIRECTORY_SEPARATOR . $tname;
+            $fpath = $dpath . DIRECTORY_SEPARATOR . 'folder.gif';
+            $rfpath = $tname.'/folder.gif';
+            if (!file_exists($dpath)) {
+                mkdir($dpath);
+            }
+            //copy the folder.gif file
+            $folder_gif = "{$CFG->dirroot}/pix/f/folder.gif";
+            copy($folder_gif, $fpath);
+            $all_files[] = $rfpath;
+        }
         $all_files = empty($all_files) ? '' : $all_files;
 
         return $all_files;
index ebb0416..cc43b95 100644 (file)
@@ -42,15 +42,20 @@ class cc_label extends entities {
     }
 
     private function create_node_course_modules_mod_label ($sheet_mod_label, $instance) {
+        if ($instance['deep'] <= ROOT_DEEP) {
+            return '';
+        }
 
         $find_tags = array('[#mod_instance#]',
                            '[#mod_name#]',
                            '[#mod_content#]',
                            '[#date_now#]');
-        //$instance['title']
+
+        $title = isset($instance['title']) && !empty($instance['title']) ? $instance['title'] : 'Untitled';
+        $content = "<img src=\"$@FILEPHP@$$@SLASH@$"."folder.gif\" alt=\"Folder\" title=\"{$title}\" /> {$title}";
         $replace_values = array($instance['instance'],
-                                'Untitled',
-                                'Untitled',
+                                $title,
+                                htmlentities($content),
                                 time());
 
         return str_replace($find_tags, $replace_values, $sheet_mod_label);
index 788503e..7c64b5c 100644 (file)
@@ -45,6 +45,8 @@ class cc_resource extends entities {
     private function create_node_course_modules_mod_resource ($sheet_mod_resource, $instance) {
 
         $link = '';
+        $mod_alltext = '';
+        $mod_summary = '';
         $xpath = cc2moodle::newx_path(cc2moodle::$manifest, cc2moodle::$namespaces);
 
         if ($instance['common_cartriedge_type'] == cc2moodle::CC_TYPE_WEBCONTENT || $instance['common_cartriedge_type'] == cc2moodle::CC_TYPE_ASSOCIATED_CONTENT) {
@@ -89,16 +91,79 @@ class cc_resource extends entities {
                            '[#mod_reference#]',
                            '[#mod_summary#]',
                            '[#mod_alltext#]',
+                           '[#mod_options#]',
                            '[#date_now#]');
 
+        $mod_type      = 'file';
+        $mod_options   = 'objectframe';
+        $mod_reference = $link;
+        //detected if we are dealing with html file
+        if (!empty($link) && ($instance['common_cartriedge_type'] == cc112moodle::CC_TYPE_WEBCONTENT)) {
+            $ext = strtolower(pathinfo($link, PATHINFO_EXTENSION));
+            if (in_array($ext, array('html', 'htm', 'xhtml'))) {
+                $mod_type = 'html';
+                //extract the content of the file
+                $rootpath = realpath(cc112moodle::$path_to_manifest_folder);
+                $htmlpath = realpath($rootpath . DIRECTORY_SEPARATOR . $link);
+                $dirpath  = dirname($htmlpath);
+                if (file_exists($htmlpath)) {
+                    $fcontent = file_get_contents($htmlpath);
+                    $mod_alltext = clean_param($this->prepare_content($fcontent), PARAM_CLEANHTML);
+                    $mod_reference = '';
+                    $mod_options = '';
+                    //TODO: try to handle embedded resources
+                    /**
+                    * images, linked static resources, applets, videos
+                    */
+                    $doc = new DOMDocument();
+                    $cdir = getcwd();
+                    chdir($dirpath);
+                    try {
+                        $doc->loadHTML($mod_alltext);
+                        $xpath = new DOMXPath($doc);
+                        $attributes = array('href', 'src', 'background', 'archive', 'code');
+                        $qtemplate = "//*[@##][not(contains(@##,'://'))]/@##";
+                        $query = '';
+                        foreach ($attributes as $attrname) {
+                            if (!empty($query)) {
+                                $query .= " | ";
+                            }
+                            $query .= str_replace('##', $attrname, $qtemplate);
+                        }
+                        $list = $xpath->query($query);
+                        $searches = array();
+                        $replaces = array();
+                        foreach ($list as $resrc) {
+                            $rpath = $resrc->nodeValue;
+                            $rtp = realpath($rpath);
+                            if (($rtp !== false) && is_file($rtp)) {
+                                //file is there - we are in business
+                                $strip = str_replace("\\", "/", str_ireplace($rootpath, '', $rtp));
+                                $encoded_file = '$@FILEPHP@$'.str_replace('/', '$@SLASH@$', $strip);
+                                $searches[] = $resrc->nodeValue;
+                                $replaces[] = $encoded_file;
+                            }
+                        }
+                        $mod_alltext = str_replace($searches, $replaces, $mod_alltext);
+                    } catch (Exception $e) {
+                        //silence the complaints
+                    }
+                    chdir($cdir);
+                    $mod_alltext = htmlspecialchars($mod_alltext, ENT_COMPAT, 'UTF-8', false);
+                }
+            }
+        }
+
         $replace_values = array($instance['instance'],
-                                htmlentities($instance['title']),
-                                'file',
-                                htmlentities($link),
-                                '',
+                                htmlspecialchars($instance['title']),
+                                $mod_type,
+                                $mod_reference,
                                 '',
+                                $mod_alltext,
+                                $mod_options,
                                 time());
 
+
         return str_replace($find_tags, $replace_values, $sheet_mod_resource);
     }
 }
index 9e1654e..d3cfd72 100644 (file)
@@ -53,14 +53,16 @@ class cc11_lti extends entities11 {
                                '[#mod_basiclti_intro#]'  ,
                                '[#mod_basiclti_timec#]'  ,
                                '[#mod_basiclti_timem#]'  ,
-                               '[#mod_basiclti_toolurl#]'
+                               '[#mod_basiclti_toolurl#]',
+                                          '[#mod_basiclti_icon#]'
                                );
 
             $replace_values = array($instance['instance'],
                                     $topic_data['title'],
                                     $topic_data['description'],
                                     time(),time(),
-                                    $topic_data['launchurl']
+                                    $topic_data['launchurl'],
+                                    $topic_data['icon']
                                     );
 
             $result = str_replace($find_tags, $replace_values, $sheet_mod_basiclti);
@@ -93,12 +95,14 @@ class cc11_lti extends entities11 {
                 $topic_title = $this->getValue($xpath->query('/xmlns:cartridge_basiclti_link/blti:title'),'Untitled');
                 $blti_description = $this->getValue($xpath->query('/xmlns:cartridge_basiclti_link/blti:description'));
                 $launch_url = $this->getValue($xpath->query('/xmlns:cartridge_basiclti_link/blti:launch_url'));
+                $launch_icon = $this->getValue($xpath->query('/xmlns:cartridge_basiclti_link/blti:icon'));
                 $tool_raw = $this->getValue($xpath->query('/xmlns:cartridge_basiclti_link/blti:vendor/lticp:code'),null);
                 $tool_url = $this->getValue($xpath->query('/xmlns:cartridge_basiclti_link/blti:vendor/lticp:url'),null);
                 $tool_desc = $this->getValue($xpath->query('/xmlns:cartridge_basiclti_link/blti:vendor/lticp:description'),null);
                 $topic_data['title'      ] = $topic_title;
                 $topic_data['description'] = $blti_description;
                 $topic_data['launchurl'  ] = $launch_url;
+                $topic_data['icon'       ] = $launch_icon;
                 $topic_data['orgid'      ] = $tool_raw;
                 $topic_data['orgurl'     ] = $tool_url;
                 $topic_data['orgdesc'    ] = $tool_desc;
index b06e6c9..4d9544d 100644 (file)
@@ -45,6 +45,8 @@ class cc11_resource extends entities11 {
     private function create_node_course_modules_mod_resource ($sheet_mod_resource, $instance) {
 
         $link = '';
+        $mod_alltext = '';
+        $mod_summary = '';
         $xpath = cc112moodle::newx_path(cc112moodle::$manifest, cc112moodle::$namespaces);
 
         if ($instance['common_cartriedge_type'] == cc112moodle::CC_TYPE_WEBCONTENT || $instance['common_cartriedge_type'] == cc112moodle::CC_TYPE_ASSOCIATED_CONTENT) {
@@ -97,14 +99,76 @@ class cc11_resource extends entities11 {
                            '[#mod_reference#]',
                            '[#mod_summary#]',
                            '[#mod_alltext#]',
+                           '[#mod_options#]',
                            '[#date_now#]');
 
+        $mod_type      = 'file';
+        $mod_options   = 'objectframe';
+        $mod_reference = $link;
+        //detected if we are dealing with html file
+        if (!empty($link) && ($instance['common_cartriedge_type'] == cc112moodle::CC_TYPE_WEBCONTENT)) {
+            $ext = strtolower(pathinfo($link, PATHINFO_EXTENSION));
+            if (in_array($ext, array('html', 'htm', 'xhtml'))) {
+                $mod_type = 'html';
+                //extract the content of the file
+                $rootpath = realpath(cc112moodle::$path_to_manifest_folder);
+                $htmlpath = realpath($rootpath . DIRECTORY_SEPARATOR . $link);
+                $dirpath  = dirname($htmlpath);
+                if (file_exists($htmlpath)) {
+                    $fcontent = file_get_contents($htmlpath);
+                    $mod_alltext = clean_param($this->prepare_content($fcontent), PARAM_CLEANHTML);
+                    $mod_reference = '';
+                    $mod_options = '';
+                    /**
+                     * try to handle embedded resources
+                     * images, linked static resources, applets, videos
+                     */
+                    $doc = new DOMDocument();
+                    $cdir = getcwd();
+                    chdir($dirpath);
+                    try {
+                        $doc->loadHTML($mod_alltext);
+                        $xpath = new DOMXPath($doc);
+                        $attributes = array('href', 'src', 'background', 'archive', 'code');
+                        $qtemplate = "//*[@##][not(contains(@##,'://'))]/@##";
+                        $query = '';
+                        foreach ($attributes as $attrname) {
+                            if (!empty($query)) {
+                                $query .= " | ";
+                            }
+                            $query .= str_replace('##', $attrname, $qtemplate);
+                        }
+                        $list = $xpath->query($query);
+                        $searches = array();
+                        $replaces = array();
+                        foreach ($list as $resrc) {
+                            $rpath = $resrc->nodeValue;
+                            $rtp = realpath($rpath);
+                            if (($rtp !== false) && is_file($rtp)) {
+                                //file is there - we are in business
+                                $strip = str_replace("\\", "/", str_ireplace($rootpath, '', $rtp));
+                                $encoded_file = '$@FILEPHP@$'.str_replace('/', '$@SLASH@$', $strip);
+                                $searches[] = $resrc->nodeValue;
+                                $replaces[] = $encoded_file;
+                            }
+                        }
+                        $mod_alltext = str_replace($searches, $replaces, $mod_alltext);
+                    } catch (Exception $e) {
+                        //silence the complaints
+                    }
+                    chdir($cdir);
+                    $mod_alltext = htmlspecialchars($mod_alltext, ENT_COMPAT, 'UTF-8', false);
+                }
+            }
+        }
+
         $replace_values = array($instance['instance'],
-                                htmlentities($instance['title']),
-                                'file',
-                                htmlentities($link),
-                                '',
+                                htmlspecialchars($instance['title']),
+                                $mod_type,
+                                $mod_reference,
                                 '',
+                                $mod_alltext,
+                                $mod_options,
                                 time());
 
         return str_replace($find_tags, $replace_values, $sheet_mod_resource);
index 5950b0d..d8da41d 100644 (file)
@@ -15,4 +15,5 @@
            <INSTRUCTORCHOICEALLOWSETTING>$@NULL@$</INSTRUCTORCHOICEALLOWSETTING>
            <GRADE>100.00000</GRADE>
            <INSTRUCTORCUSTOMPARAMETERS>0</INSTRUCTORCUSTOMPARAMETERS>
+        <ICON>[#mod_basiclti_icon#]</ICON>
       </MOD>
\ No newline at end of file
index d9d24fb..3520175 100644 (file)
@@ -7,6 +7,6 @@
         <SUMMARY>[#mod_summary#]</SUMMARY>
         <ALLTEXT>[#mod_alltext#]</ALLTEXT>
         <POPUP></POPUP>
-        <OPTIONS>objectframe</OPTIONS>
+        <OPTIONS>[#mod_options#]</OPTIONS>
         <TIMEMODIFIED>[#date_now#]</TIMEMODIFIED>
       </MOD>
diff --git a/backup/converter/imscc1/backuplib.php b/backup/converter/imscc1/backuplib.php
new file mode 100644 (file)
index 0000000..159b76e
--- /dev/null
@@ -0,0 +1,13 @@
+<?php
+
+require_once($CFG->dirroot . '/backup/converter/convertlib.php');
+
+class imscc1_export_converter extends base_converter {
+    public static function is_available() {
+        return false;
+    }
+
+    protected function execute() {
+
+    }
+}
\ No newline at end of file
diff --git a/backup/converter/imscc1/lib.php b/backup/converter/imscc1/lib.php
new file mode 100644 (file)
index 0000000..86694fc
--- /dev/null
@@ -0,0 +1,124 @@
+<?php
+
+/**
+* Provides Common Cartridge v1 converter class
+*
+* @package    core
+* @subpackage backup-convert
+* @copyright  2011 Darko Miletic <dmiletic@moodlerooms.com>
+* @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+*/
+
+require_once($CFG->dirroot . '/backup/converter/convertlib.php');
+require_once($CFG->dirroot . '/backup/cc/includes/constants.php');
+require_once($CFG->dirroot . '/backup/cc/cc2moodle.php');
+require_once($CFG->dirroot . '/backup/cc/validator.php');
+
+class imscc1_converter extends base_converter {
+
+    /**
+    * Log a message
+    *
+    * @see parent::log()
+    * @param string $message message text
+    * @param int $level message level {@example backup::LOG_WARNING}
+    * @param null|mixed $a additional information
+    * @param null|int $depth the message depth
+    * @param bool $display whether the message should be sent to the output, too
+    */
+    public function log($message, $level, $a = null, $depth = null, $display = false) {
+        parent::log('(imscc1) '.$message, $level, $a, $depth, $display);
+    }
+
+    /**
+     * Detects the Common Cartridge 1.0 format of the backup directory
+     *
+     * @param string $tempdir the name of the backup directory
+     * @return null|string backup::FORMAT_IMSCC1 if the Common Cartridge 1.0 is detected, null otherwise
+     */
+    public static function detect_format($tempdir) {
+        global $CFG;
+
+        $filepath = $CFG->dataroot . '/temp/backup/' . $tempdir;
+        $manifest = cc2moodle::get_manifest($filepath);
+        if (!empty($manifest)) {
+            // looks promising, lets load some information
+            $handle = fopen($manifest, 'r');
+            $xml_snippet = fread($handle, 500);
+            fclose($handle);
+
+            // check if it has the required strings
+
+            $xml_snippet = strtolower($xml_snippet);
+            $xml_snippet = preg_replace('/\s*/m', '', $xml_snippet);
+            $xml_snippet = str_replace("'", '', $xml_snippet);
+            $xml_snippet = str_replace('"', '', $xml_snippet);
+
+            $search_string = "xmlns=http://www.imsglobal.org/xsd/imscc/imscp_v1p1";
+            if (strpos($xml_snippet, $search_string) !== false) {
+                return backup::FORMAT_IMSCC1;
+            }
+        }
+
+        return null;
+    }
+
+
+    /**
+    * Returns the basic information about the converter
+    *
+    * The returned array must contain the following keys:
+    * 'from' - the supported source format, eg. backup::FORMAT_MOODLE1
+    * 'to'   - the supported target format, eg. backup::FORMAT_MOODLE
+    * 'cost' - the cost of the conversion, non-negative non-zero integer
+    */
+    public static function description() {
+
+        return array(
+                'from'  => backup::FORMAT_IMSCC1,
+                'to'    => backup::FORMAT_MOODLE1,
+                'cost'  => 10
+        );
+    }
+
+    protected function execute() {
+        global $CFG;
+
+        $manifest = cc2moodle::get_manifest($this->get_tempdir_path());
+        if (empty($manifest)) {
+            throw new imscc1_convert_exception('No Manifest detected!');
+        }
+
+        $this->log('validating manifest', backup::LOG_DEBUG, null, 1);
+        $validator = new manifest10_validator($CFG->dirroot . '/backup/cc/schemas');
+        if (!$validator->validate($manifest)) {
+            $this->log('validation error(s): '.PHP_EOL.error_messages::instance(), backup::LOG_DEBUG, null, 2);
+            throw new imscc1_convert_exception(error_messages::instance()->to_string(true));
+        }
+
+        $manifestdir = dirname($manifest);
+        $cc2moodle = new cc2moodle($manifest);
+        if ($cc2moodle->is_auth()) {
+            throw new imscc1_convert_exception('Protected cartridge content - Skipping import!');
+        }
+        $status = $cc2moodle->generate_moodle_xml();
+        //Final cleanup
+        $mdoc = new DOMDocument();
+        $mdoc->preserveWhiteSpace = false;
+        $mdoc->formatOutput = true;
+        $mdoc->validateOnParse = false;
+        if ($mdoc->load($manifestdir.'/moodle.xml', LIBXML_NONET)) {
+            $mdoc->save($this->get_workdir_path().'/moodle.xml', LIBXML_NOEMPTYTAG);
+        }
+        //Move the files to the workdir
+        rename($manifestdir.'/course_files', $this->get_workdir_path().'/course_files');
+    }
+
+
+}
+
+/**
+* Exception thrown by this converter
+*/
+class imscc1_convert_exception extends convert_exception {
+}