MDL-20067 new IMS Content package module, includes migrate from old mod/resource...
authorskodak <skodak>
Thu, 13 Aug 2009 20:48:32 +0000 (20:48 +0000)
committerskodak <skodak>
Thu, 13 Aug 2009 20:48:32 +0000 (20:48 +0000)
17 files changed:
lang/en_utf8/imscp.php [new file with mode: 0644]
mod/imscp/db/access.php [new file with mode: 0644]
mod/imscp/db/install.php [new file with mode: 0644]
mod/imscp/db/install.xml [new file with mode: 0644]
mod/imscp/db/upgrade.php [new file with mode: 0644]
mod/imscp/db/upgradelib.php [new file with mode: 0644]
mod/imscp/functions.js [new file with mode: 0644]
mod/imscp/icon.gif [new file with mode: 0644]
mod/imscp/index.php [new file with mode: 0644]
mod/imscp/lib.php [new file with mode: 0644]
mod/imscp/locallib.php [new file with mode: 0644]
mod/imscp/mod_form.php [new file with mode: 0644]
mod/imscp/readme.txt [new file with mode: 0644]
mod/imscp/settings.php [new file with mode: 0644]
mod/imscp/style.css [new file with mode: 0644]
mod/imscp/version.php [new file with mode: 0644]
mod/imscp/view.php [new file with mode: 0644]

diff --git a/lang/en_utf8/imscp.php b/lang/en_utf8/imscp.php
new file mode 100644 (file)
index 0000000..5d71ccb
--- /dev/null
@@ -0,0 +1,13 @@
+<?php
+
+$string['areabackup'] = 'Package archive';
+$string['areacontent'] = 'Content';
+$string['contentheader'] = 'Content';
+$string['deploymenterror'] = 'Content package error!';
+$string['keepold'] = 'Archive packages';
+$string['keepoldexplain'] = 'How many packages should be archived?';
+$string['modulename'] = 'IMS Content Package';
+$string['modulenameplural'] = 'IMS Content packages';
+$string['navigation'] = 'Navigation';
+$string['packagefile'] = 'Package file';
+$string['toc'] = 'TOC';
diff --git a/mod/imscp/db/access.php b/mod/imscp/db/access.php
new file mode 100644 (file)
index 0000000..d2e6d95
--- /dev/null
@@ -0,0 +1,40 @@
+<?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/>.
+
+/**
+ * Capability definitions for the imscp module.
+ *
+ * @package   mod-imscp
+ * @copyright 2009 Petr Skoda (http://skodak.org)
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$mod_imscp_capabilities = array(
+
+/* TODO: review public portfolio API first!
+    'mod/imscp:portfolioexport' => array(
+
+        'captype' => 'read',
+        'contextlevel' => CONTEXT_MODULE,
+        'legacy' => array(
+            'teacher' => CAP_ALLOW,
+            'editingteacher' => CAP_ALLOW,
+        )
+    ),*/
+
+);
+
diff --git a/mod/imscp/db/install.php b/mod/imscp/db/install.php
new file mode 100644 (file)
index 0000000..90f1e81
--- /dev/null
@@ -0,0 +1,51 @@
+<?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/>.
+
+/**
+ * Post installation and migration code.
+ *
+ * This file replaces:
+ *   - STATEMENTS section in db/install.xml
+ *   - lib.php/modulename_install() post installation hook
+ *   - partially defaults.php
+ *
+ * @package   mod-imscp
+ * @copyright 2009 Petr Skoda (http://skodak.org)
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+function xmldb_imscp_install() {
+    global $CFG;
+
+    // Install logging support
+    update_log_display_entry('imscp', 'view', 'imscp', 'name');
+    update_log_display_entry('imscp', 'view all', 'imscp', 'name');
+    update_log_display_entry('imscp', 'update', 'imscp', 'name');
+    update_log_display_entry('imscp', 'add', 'imscp', 'name');
+
+    // Upgrade from old resource module type if needed
+    require_once("$CFG->dirroot/mod/imscp/db/upgradelib.php");
+    imscp_20_migrate();
+}
+
+function xmldb_imscp_install_recovery() {
+    global $CFG;
+
+    // Upgrade from old resource module type if needed
+    require_once("$CFG->dirroot/mod/imscp/db/upgradelib.php");
+    imscp_20_migrate();
+}
diff --git a/mod/imscp/db/install.xml b/mod/imscp/db/install.xml
new file mode 100644 (file)
index 0000000..0ca9832
--- /dev/null
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<XMLDB PATH="mod/imscp/db" VERSION="20090729" COMMENT="XMLDB file for IMS Content Package module "
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:noNamespaceSchemaLocation="../../../lib/xmldb/xmldb.xsd"
+>
+  <TABLES>
+    <TABLE NAME="imscp" COMMENT="each record is one imscp resource">
+      <FIELDS>
+        <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="course"/>
+        <FIELD NAME="course" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="id" NEXT="name"/>
+        <FIELD NAME="name" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" PREVIOUS="course" NEXT="intro"/>
+        <FIELD NAME="intro" TYPE="text" LENGTH="small" NOTNULL="false" SEQUENCE="false" PREVIOUS="name" NEXT="introformat"/>
+        <FIELD NAME="introformat" TYPE="int" LENGTH="4" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="intro" NEXT="revision"/>
+        <FIELD NAME="revision" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="incremented when after each file changes, solves browser caching issues" PREVIOUS="introformat" NEXT="keepold"/>
+        <FIELD NAME="keepold" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="false" DEFAULT="-1" SEQUENCE="false" COMMENT="incremented when after each file changes, solves browser caching issues" PREVIOUS="revision" NEXT="structure"/>
+        <FIELD NAME="structure" TYPE="text" LENGTH="medium" NOTNULL="false" SEQUENCE="false" PREVIOUS="keepold" NEXT="timemodified"/>
+        <FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="structure"/>
+      </FIELDS>
+      <KEYS>
+        <KEY NAME="primary" TYPE="primary" FIELDS="id"/>
+      </KEYS>
+      <INDEXES>
+        <INDEX NAME="course" UNIQUE="false" FIELDS="course"/>
+      </INDEXES>
+    </TABLE>
+  </TABLES>
+</XMLDB>
\ No newline at end of file
diff --git a/mod/imscp/db/upgrade.php b/mod/imscp/db/upgrade.php
new file mode 100644 (file)
index 0000000..e542ca4
--- /dev/null
@@ -0,0 +1,34 @@
+<?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/>.
+
+/**
+ * IMS CP module upgrade code
+ *
+ * @package   mod-imscp
+ * @copyright 2009 Petr Skoda (http://skodak.org)
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+function xmldb_imscp_upgrade($oldversion) {
+    global $CFG, $DB;
+
+    $dbman = $DB->get_manager();
+    $result = true;
+
+
+    return $result;
+}
diff --git a/mod/imscp/db/upgradelib.php b/mod/imscp/db/upgradelib.php
new file mode 100644 (file)
index 0000000..1319c41
--- /dev/null
@@ -0,0 +1,147 @@
+<?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/>.
+
+/**
+ * IMS CP module upgrade related helper functions
+ *
+ * @package   mod-imscp
+ * @copyright 2009 Petr Skoda (http://skodak.org)
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * Migrate imscp module data from 1.9 resource_old table to new imscp table
+ * @return void
+ */
+function imscp_20_migrate() {
+    global $CFG, $DB;
+    require_once("$CFG->libdir/filelib.php");
+    require_once("$CFG->dirroot/course/lib.php");
+    require_once("$CFG->dirroot/mod/imscp/locallib.php");
+
+    if (!file_exists("$CFG->dirroot/mod/resource/db/upgradelib.php")) {
+        // bad luck, somebody deleted resource module
+        return;
+    }
+
+    require_once("$CFG->dirroot/mod/resource/db/upgradelib.php");
+
+    // create resource_old table and copy resource table there if needed
+    if (!resource_20_prepare_migration()) {
+        // no modules or fresh install
+        return;
+    }
+
+    if (!$candidates = $DB->get_recordset('resource_old', array('type'=>'ims', 'migrated'=>0))) {
+        return;
+    }
+
+    $fs = get_file_storage();
+
+    foreach ($candidates as $candidate) {
+        upgrade_set_timeout(60);
+
+        $imscp = new object();
+        $imscp->course       = $candidate->course;
+        $imscp->name         = $candidate->name;
+        $imscp->intro        = $candidate->intro;
+        $imscp->introformat  = $candidate->introformat;
+        $imscp->revision     = 1;
+        $imscp->keepold      = 1;
+        $imscp->timemodified = time();
+
+        if (!$imscp = resource_migrate_to_module('imscp', $candidate, $imscp)) {
+            continue;
+        }
+
+        $context = get_context_instance(CONTEXT_MODULE, $candidate->cmid);
+        $root = "$CFG->dataroot/$candidate->course/$CFG->moddata/resource/$candidate->oldid";
+
+        // migrate package backup file
+        if ($candidate->reference) {
+            $package = basename($candidate->reference);
+            $fullpath = $root.'/'.$package;
+            if (file_exists($fullpath)) {
+                $file_record = array('contextid' => $context->id,
+                                     'filearea'  => 'imscp_backup',
+                                     'itemid'    => 1,
+                                     'filepath'  => '/',
+                                     'filename'  => $package);
+                $fs->create_file_from_pathname($file_record, $fullpath);
+            }
+        }
+
+        // migrate extracted package data
+        $files = imsc_migrate_get_old_files($root, '');
+        $file_record = array('contextid'=>$context->id, 'filearea'=>'imscp_content', 'itemid'=>1);
+        $error = false;
+        foreach ($files as $relname=>$fullpath) {
+            $parts = explode('/', $relname);
+            $file_record['filename'] = array_pop($parts);
+            $parts[] = ''; // keep trailing slash
+            $file_record['filepath'] = implode('/', $parts);
+
+            try {
+                $fs->create_file_from_pathname($file_record, $fullpath);
+            } catch (Exception $e) {
+                //continue on error, we can not recover anyway
+                $error = true;
+                notify("IMSCP: failed migrating file: $relname");
+            }
+        }
+        unset($files);
+
+        // parse manifest
+        $structure = imscp_parse_structure($imscp, $context);
+        $imscp->structure = is_array($structure) ? serialize($structure) : null;
+        $DB->update_record('imscp', $imscp);
+
+        // remove old moddata dir only if no error and manifest ok
+        if (!$error and is_array($structure)) {
+            fulldelete($root);
+        }
+    }
+
+    $candidates->close();
+
+    // clear all course modinfo caches
+    rebuild_course_cache(0, true);
+}
+
+/**
+ * Private function returning all extracted IMS content package file
+ */
+function imsc_migrate_get_old_files($path, $relative) {
+    $result = array();
+    $items = new DirectoryIterator($path);
+    foreach ($items as $item) {
+        if ($item->isDot() or $item->isLink()) {
+            // symbolik links could create infinite loops or cause unintended file migration, sorry
+            continue;
+        }
+        $pathname = $item->getPathname();
+        $relname  = $relative.'/'.$item->getFilename();
+        if ($item->isFile()) {
+            $result[$relname] = $pathname;
+        } else if ($item->isDir()) {
+            $result = array_merge($result, imsc_migrate_get_old_files($pathname, $relname));
+        }
+        unset($item);
+    }
+    unset($items);
+    return $result;
+}
\ No newline at end of file
diff --git a/mod/imscp/functions.js b/mod/imscp/functions.js
new file mode 100644 (file)
index 0000000..2439a5e
--- /dev/null
@@ -0,0 +1,279 @@
+// 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/>.
+
+/**
+ * Javascript helper function for IMS Content Package module including
+ * dummy SCORM API.
+ *
+ * @package   mod-imscp
+ * @copyright 2009 Petr Skoda (http://skodak.org)
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/** Dummy SCORM API adapter */
+var API = new function () {
+    this.LMSCommit         = function (parameter) {return "true";};
+    this.LMSFinish         = function (parameter) {return "true";};
+    this.LMSGetDiagnostic  = function (errorCode) {return "n/a";};
+    this.LMSGetErrorString = function (errorCode) {return "n/a";};
+    this.LMSGetLastError   = function () {return "0";};
+    this.LMSGetValue       = function (element) {return "";};
+    this.LMSInitialize     = function (parameter) {return "true";};
+    this.LMSSetValue       = function (element, value) {return "true";};
+};
+
+var imscp_layout_widget;
+var imscp_current_node;
+var imscp_buttons = [];
+var imscp_bloody_labelclick = false;
+var imscp_panel;
+
+function imscp_init() {
+    YAHOO.util.Event.onDOMReady(function () {
+        imscp_setup();
+    });
+}
+
+function imscp_setup() {
+    // layout
+    YAHOO.widget.LayoutUnit.prototype.STR_COLLAPSE = mstr.moodle.hide;
+    YAHOO.widget.LayoutUnit.prototype.STR_EXPAND = mstr.moodle.show;
+
+    imscp_layout_widget = new YAHOO.widget.Layout('imscp_layout', {
+        minWidth: 600,
+        minHeight: 400,
+        units: [
+            { position: 'left', body: 'imscp_toc', header: mstr.imscp.toc, width: 250, resize: true, gutter: '2px 5px 5px 2px', collapse: true, minWidth:150},
+            { position: 'center', body: '<div id="imscp_content"></div>', gutter: '2px 5px 5px 2px', scroll: true}
+        ]
+    });
+    imscp_layout_widget.render();
+    var left = imscp_layout_widget.getUnitByPosition('left');
+    left.on('collapse', function() {
+        imscp_resize_frame();
+    });
+    left.on('expand', function() {
+        imscp_resize_frame();
+    });
+
+    // ugly resizing hack that works around problems with resizing of iframes and objects
+    left._resize.on('startResize', function() {
+        var obj = YAHOO.util.Dom.get('imscp_object');
+        obj.style.display = 'none';
+    });
+    left._resize.on('endResize', function() {
+        var obj = YAHOO.util.Dom.get('imscp_object');
+        obj.style.display = 'block';
+        imscp_resize_frame();
+    });
+
+    // TOC tree
+    var tree = new YAHOO.widget.TreeView('imscp_tree');
+    tree.singleNodeHighlight = true;
+    tree.subscribe('labelClick', function(node) {
+        imscp_activate_item(node);
+        if (node.children.length) {
+            imscp_bloody_labelclick = true;
+        }
+    });
+    tree.subscribe('collapse', function(node) {
+        if (imscp_bloody_labelclick) {
+            imscp_bloody_labelclick = false;
+            return false;
+        }
+    });
+    tree.subscribe('expand', function(node) {
+        if (imscp_bloody_labelclick) {
+            imscp_bloody_labelclick = false;
+            return false;
+        }
+    });
+    tree.expandAll();
+    tree.render();
+
+    // navigation
+    imscp_panel = new YAHOO.widget.Panel('imscp_navpanel', { visible:true, draggable:true, close:false,
+                                                           context: ['page', 'bl', 'bl', ["windowScroll", "textResize", "windowResize"]], constraintoviewport:true} );
+    imscp_panel.setHeader(mstr.imscp.navigation);
+    //TODO: make some better&accessible buttons
+    imscp_panel.setBody('<span id="imscp_nav"><button id="nav_skipprev">&lt;&lt;</button><button id="nav_prev">&lt;</button><button id="nav_up">^</button><button id="nav_next">&gt;</button><button id="nav_skipnext">&gt;&gt;</button></span>');
+    imscp_panel.render();
+    imscp_buttons[0] = new YAHOO.widget.Button('nav_skipprev');
+    imscp_buttons[1] = new YAHOO.widget.Button('nav_prev');
+    imscp_buttons[2] = new YAHOO.widget.Button('nav_up');
+    imscp_buttons[3] = new YAHOO.widget.Button('nav_next');
+    imscp_buttons[4] = new YAHOO.widget.Button('nav_skipnext');
+    imscp_buttons[0].on('click', function(ev) {
+        imscp_activate_item(imscp_skipprev(imscp_current_node));
+    });
+    imscp_buttons[1].on('click', function(ev) {
+        imscp_activate_item(imscp_prev(imscp_current_node));
+    });
+    imscp_buttons[2].on('click', function(ev) {
+        imscp_activate_item(imscp_up(imscp_current_node));
+    });
+    imscp_buttons[3].on('click', function(ev) {
+        imscp_activate_item(imscp_next(imscp_current_node));
+    });
+    imscp_buttons[4].on('click', function(ev) {
+        imscp_activate_item(imscp_skipnext(imscp_current_node));
+    });
+    imscp_panel.render();
+
+    // finally activate the first item
+    imscp_activate_item(tree.getRoot().children[0]);
+
+    // resizing
+    imscp_resize_layout(false);
+
+    // fix layout if window resized
+    window.onresize = function() {
+        imscp_resize_layout(true);
+    };
+}
+
+
+function imscp_activate_item(node) {
+    if (!node) {
+        return;
+    }
+    imscp_current_node = node;
+    imscp_current_node.highlight();
+
+    var content = new YAHOO.util.Element('imscp_content');
+    try {
+        // first try IE way - it can not set name attribute later
+        // and also it has some restrictions on DOM access from object tag
+        var obj = document.createElement('<iframe id="imscp_object" src="'+node.href+'">');
+    } catch (e) {
+        var obj = document.createElement('object');
+        obj.setAttribute('id', 'imscp_object');
+        obj.setAttribute('type', 'text/html');
+        obj.setAttribute('data', node.href);
+    }
+    var old = YAHOO.util.Dom.get('imscp_object');
+    if (old) {
+        content.replaceChild(obj, old);
+    } else {
+        content.appendChild(obj);
+    }
+    imscp_resize_frame();
+
+    imscp_current_node.focus();
+    imscp_fixnav();
+}
+
+/**
+ * Enables/disables navigation buttons as needed.
+ * @return void
+ */
+function imscp_fixnav() {
+    imscp_buttons[0].set('disabled', (imscp_skipprev(imscp_current_node) == null));
+    imscp_buttons[1].set('disabled', (imscp_prev(imscp_current_node) == null));
+    imscp_buttons[2].set('disabled', (imscp_up(imscp_current_node) == null));
+    imscp_buttons[3].set('disabled', (imscp_next(imscp_current_node) == null));
+    imscp_buttons[4].set('disabled', (imscp_skipnext(imscp_current_node) == null));
+}
+
+
+function imscp_resize_layout(alsowidth) {
+    if (alsowidth) {
+        var layout = YAHOO.util.Dom.get('imscp_layout');
+        layout.style.width = '500px';
+        var newwidth = imscp_get_htmlelement_size('content', 'width');
+        if (newwidth > 500) {
+            layout.style.width = newwidth+'px';
+        }
+    }
+    var pageheight = imscp_get_htmlelement_size('page', 'height');
+    var layoutheight = imscp_get_htmlelement_size(imscp_layout_widget, 'height');
+    var newheight = layoutheight + parseInt(YAHOO.util.Dom.getViewportHeight()) - pageheight - 20;
+    if (newheight > 400) {
+        imscp_layout_widget.setStyle('height', newheight+'px');
+    }
+    imscp_layout_widget.render();
+    imscp_resize_frame();
+
+    imscp_panel.align('bl', 'bl');
+}
+
+function imscp_get_htmlelement_size(el, prop) {
+    var val = YAHOO.util.Dom.getStyle(el, prop);
+    if (val == 'auto') {
+        if (el.get) {
+            el = el.get('element'); // get real HTMLElement from YUI element
+        }
+        val = YAHOO.util.Dom.getComputedStyle(YAHOO.util.Dom.get(el), prop);
+    }
+    return parseInt(val);
+}
+
+function imscp_resize_frame() {
+    var obj = YAHOO.util.Dom.get('imscp_object');
+    if (obj) {
+        var content = imscp_layout_widget.getUnitByPosition('center').get('wrap');
+        obj.style.width = (content.offsetWidth - 6)+'px';
+        obj.style.height = (content.offsetHeight - 10)+'px';
+    }
+}
+
+
+function imscp_up(node) {
+    if (node.depth > 0) {
+        return node.parent;
+    }
+    return null;
+}
+
+function imscp_lastchild(node) {
+    if (node.children.length) {
+        return imscp_lastchild(node.children[node.children.length-1]);
+    } else {
+        return node;
+    }
+}
+
+function imscp_prev(node) {
+    if (node.previousSibling && node.previousSibling.children.length) {
+        return imscp_lastchild(node.previousSibling);
+    }
+    return imscp_skipprev(node);
+}
+
+function imscp_skipprev(node) {
+    if (node.previousSibling) {
+        return node.previousSibling;
+    } else if (node.depth > 0) {
+        return node.parent;
+    }
+    return null;
+}
+
+function imscp_next(node) {
+    if (node.children.length) {
+        return node.children[0];
+    }
+    return imscp_skipnext(node);
+}
+
+function imscp_skipnext(node) {
+    if (node.nextSibling) {
+        return node.nextSibling;
+    } else if (node.depth > 0) {
+        return imscp_skipnext(node.parent);
+    }
+    return null;
+}
+
diff --git a/mod/imscp/icon.gif b/mod/imscp/icon.gif
new file mode 100644 (file)
index 0000000..fc60d4e
Binary files /dev/null and b/mod/imscp/icon.gif differ
diff --git a/mod/imscp/index.php b/mod/imscp/index.php
new file mode 100644 (file)
index 0000000..088d8dd
--- /dev/null
@@ -0,0 +1,97 @@
+<?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/>.
+
+/**
+ * List of file imscps in course
+ *
+ * @package   mod-imscp
+ * @copyright 2009 onwards Martin Dougiamas (http://dougiamas.com)
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require('../../config.php');
+
+$id = required_param('id', PARAM_INT); // course id
+
+$course = $DB->get_record('course', array('id'=>$id), '*', MUST_EXIST);
+
+require_course_login($course, true);
+
+add_to_log($course->id, 'imscp', 'view all', "index.php?id=$course->id", '');
+
+$strimscp       = get_string('modulename', 'imscp');
+$strimscps      = get_string('modulenameplural', 'imscp');
+$strweek         = get_string('week');
+$strtopic        = get_string('topic');
+$strname         = get_string('name');
+$strintro        = get_string('moduleintro');
+$strlastmodified = get_string('lastmodified');
+
+$PAGE->set_url('mod/imscp/index.php', array('id' => $course->id));
+$PAGE->set_title($course->shortname.': '.$strimscps);
+$PAGE->set_heading($course->fullname);
+$navlinks = array(array('name' => $strimscps, 'link' => '', 'type' => 'activityinstance'));
+echo $OUTPUT->header(build_navigation($navlinks), navmenu($course));
+
+if (!$imscps = get_all_instances_in_course('imscp', $course)) {
+    notice(get_string('thereareno', 'moodle', $strimscps), "$CFG->wwwroot/course/view.php?id=$course->id");
+    exit;
+}
+
+$table = new html_table();
+$table->set_classes(array('generaltable', 'mod_index'));
+
+if ($course->format == 'weeks') {
+    $table->head  = array ($strweek, $strname, $strintro);
+    $table->align = array ('center', 'left', 'left');
+} else if ($course->format == 'topics') {
+    $table->head  = array ($strtopic, $strname, $strintro);
+    $table->align = array ('center', 'left', 'left');
+} else {
+    $table->head  = array ($strlastmodified, $strname, $strintro);
+    $table->align = array ('left', 'left', 'left');
+}
+
+$modinfo = get_fast_modinfo($course);
+$currentsection = '';
+foreach ($imscps as $imscp) {
+    $cm = $modinfo->cms[$imscp->coursemodule];
+    if ($course->format == 'weeks' or $course->format == 'topics') {
+        $printsection = '';
+        if ($imscp->section !== $currentsection) {
+            if ($imscp->section) {
+                $printsection = $imscp->section;
+            }
+            if ($currentsection !== '') {
+                $table->data[] = 'hr';
+            }
+            $currentsection = $imscp->section;
+        }
+    } else {
+        $printsection = '<span class="smallinfo">'.userdate($imscp->timemodified)."</span>";
+    }
+
+    $class = $imscp->visible ? '' : 'class="dimmed"'; // hidden modules are dimmed
+    $table->data[] = array (
+        $printsection,
+        "<a $class href=\"view.php?id=$cm->id\">".format_string($imscp->name)."</a>",
+        format_module_intro('imscp', $imscp, $cm->id));
+}
+
+echo $OUTPUT->table($table);
+
+echo $OUTPUT->footer();
diff --git a/mod/imscp/lib.php b/mod/imscp/lib.php
new file mode 100644 (file)
index 0000000..1ab3519
--- /dev/null
@@ -0,0 +1,375 @@
+<?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/>.
+
+/**
+ * Mandatory public API of imscp module
+ *
+ * @package   mod-imscp
+ * @copyright 2009 Petr Skoda (http://skodak.org)
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * List of features supported in IMS CP module
+ * @param string $feature FEATURE_xx constant for requested feature
+ * @return mixed True if module supports feature, false if not, null if doesn't know
+ */
+function imscp_supports($feature) {
+    switch($feature) {
+        case FEATURE_MOD_ARCHETYPE:           return MOD_ARCHETYPE_RESOURCE;
+        case FEATURE_GROUPS:                  return false;
+        case FEATURE_GROUPINGS:               return false;
+        case FEATURE_GROUPMEMBERSONLY:        return true;
+        case FEATURE_MOD_INTRO:               return true;
+        case FEATURE_COMPLETION_TRACKS_VIEWS: return true;
+        case FEATURE_GRADE_HAS_GRADE:         return false;
+        case FEATURE_GRADE_OUTCOMES:          return false;
+
+        default: return null;
+    }
+}
+
+/**
+ * Returns all other caps used in module
+ * @return array
+ */
+function imscp_get_extra_capabilities() {
+    return array('moodle/site:accessallgroups');
+}
+
+/**
+ * This function is used by the reset_course_userdata function in moodlelib.
+ * @param $data the data submitted from the reset course.
+ * @return array status array
+ */
+function imscp_reset_userdata($data) {
+    return array();
+}
+
+/**
+ * List of view style log actions
+ * @return array
+ */
+function imscp_get_view_actions() {
+    return array('view', 'view all');
+}
+
+/**
+ * List of update style log actions
+ * @return array
+ */
+function imscp_get_post_actions() {
+    return array('update', 'add');
+}
+
+/**
+ * Add imscp instance.
+ * @param object $data
+ * @param object $mform
+ * @return int new imscp instance id
+ */
+function imscp_add_instance($data, $mform) {
+    global $CFG, $DB;
+    require_once("$CFG->dirroot/mod/imscp/locallib.php");
+
+    $cmid = $data->coursemodule;
+
+    $data->timemodified = time();
+    $data->revision     = 1;
+    $data->structure    = null;
+
+    $data->id = $DB->insert_record('imscp', $data);
+
+    // we need to use context now, so we need to make sure all needed info is already in db
+    $DB->set_field('course_modules', 'instance', $data->id, array('id'=>$cmid));
+    $context = get_context_instance(CONTEXT_MODULE, $cmid);
+    $imscp = $DB->get_record('imscp', array('id'=>$data->id), '*', MUST_EXIST);
+
+    if ($filename = $mform->get_new_filename('package')) {
+        if ($package = $mform->save_stored_file('package', $context->id, 'imscp_backup', 1, '/', $filename)) {
+            // extract package content
+            $packer = get_file_packer('application/zip');
+            $package->extract_to_storage($packer, $context->id, 'imscp_content', 1, '/');
+            $structure = imscp_parse_structure($imscp, $context);
+            $imscp->structure = is_array($structure) ? serialize($structure) : null;
+            $DB->update_record('imscp', $imscp);
+        }
+    }
+
+    return $data->id;
+}
+
+/**
+ * Update imscp instance.
+ * @param object $data
+ * @param object $mform
+ * @return bool true
+ */
+function imscp_update_instance($data, $mform) {
+    global $CFG, $DB;
+    require_once("$CFG->dirroot/mod/imscp/locallib.php");
+
+    $cmid = $data->coursemodule;
+
+    $data->timemodified = time();
+    $data->id           = $data->instance;
+    $imscp->structure   = null; // better reparse structure after each update
+
+    $DB->update_record('imscp', $data);
+
+    $context = get_context_instance(CONTEXT_MODULE, $cmid);
+    $imscp = $DB->get_record('imscp', array('id'=>$data->id), '*', MUST_EXIST);
+
+    if ($filename = $mform->get_new_filename('package')) {
+        $fs = get_file_storage();
+
+        $imscp->revision++;
+        $DB->update_record('imscp', $imscp);
+
+        // get a list of existing packages before adding new package
+        if ($imscp->keepold > -1) {
+            $packages = $fs->get_area_files($context->id, 'imscp_backup', false, "itemid ASC", false);
+        } else {
+            $packages = array();
+        }
+
+        $package = $mform->save_stored_file('package', $context->id, 'imscp_backup', $imscp->revision, '/', $filename);
+
+        // purge all extracted content
+        $fs->delete_area_files($context->id, 'imscp_content');
+
+        // extract package content
+        if ($package) {
+            $packer = get_file_packer('application/zip');
+            $package->extract_to_storage($packer, $context->id, 'imscp_content', $imscp->revision, '/');
+        }
+
+        // cleanup old package files, keep current + keepold
+        while ($packages and (count($packages) > $imscp->keepold)) {
+            $package = array_shift($packages);
+            $fs->delete_area_files($context->id, 'imscp_backup', $package->get_itemid());
+        }
+    }
+
+    $structure = imscp_parse_structure($imscp, $context);
+    $imscp->structure = is_array($structure) ? serialize($structure) : null;
+    $DB->update_record('imscp', $imscp);
+
+    return true;
+}
+
+/**
+ * Delete imscp instance.
+ * @param int $id
+ * @return bool true
+ */
+function imscp_delete_instance($id) {
+    global $DB;
+
+    if (!$imscp = $DB->get_record('imscp', array('id'=>$id))) {
+        return false;
+    }
+
+    // note: all context files are deleted automatically
+
+    $DB->delete_records('imscp', array('id'=>$imscp->id));
+
+    return true;
+}
+
+/**
+ * Return use outline
+ * @param object $course
+ * @param object $user
+ * @param object $mod
+ * @param object $imscp
+ * @return object|null
+ */
+function imscp_user_outline($course, $user, $mod, $imscp) {
+    global $DB;
+
+    if ($logs = $DB->get_records('log', array('userid'=>$user->id, 'module'=>'imscp',
+                                              'action'=>'view', 'info'=>$imscp->id), 'time ASC')) {
+
+        $numviews = count($logs);
+        $lastlog = array_pop($logs);
+
+        $result = new object();
+        $result->info = get_string('numviews', '', $numviews);
+        $result->time = $lastlog->time;
+
+        return $result;
+    }
+    return NULL;
+}
+
+/**
+ * Return use complete
+ * @param object $course
+ * @param object $user
+ * @param object $mod
+ * @param object $imscp
+ */
+function imscp_user_complete($course, $user, $mod, $imscp) {
+    global $CFG, $DB;
+
+    if ($logs = $DB->get_records('log', array('userid'=>$user->id, 'module'=>'imscp',
+                                              'action'=>'view', 'info'=>$imscp->id), 'time ASC')) {
+        $numviews = count($logs);
+        $lastlog = array_pop($logs);
+
+        $strmostrecently = get_string('mostrecently');
+        $strnumviews = get_string('numviews', '', $numviews);
+
+        echo "$strnumviews - $strmostrecently ".userdate($lastlog->time);
+
+    } else {
+        print_string('neverseen', 'imscp');
+    }
+}
+
+/**
+ * Returns the users with data in one imscp
+ *
+ * @param int $imscpid
+ * @return bool false
+ */
+function imscp_get_participants($imscpid) {
+    return false;
+}
+
+/**
+ * Lists all browsable file areas
+ * @param object $course
+ * @param object $cm
+ * @param object $context
+ * @return array
+ */
+function imscp_get_file_areas($course, $cm, $context) {
+    $areas = array();
+    if (has_capability('moodle/course:managefiles', $context)) {
+        $areas['imscp_content'] = get_string('areacontent', 'imscp');
+        $areas['imscp_backup']  = get_string('areabackup', 'imscp');
+    }
+    return $areas;
+}
+
+/**
+ * File browsing support for imscp module ontent area.
+ * @param object $browser
+ * @param object $areas
+ * @param object $course
+ * @param object $cm
+ * @param object $context
+ * @param string $filearea
+ * @param int $itemid
+ * @param string $filepath
+ * @param string $filename
+ * @return object file_info instance or null if not found
+ */
+function imscp_get_file_info($browser, $areas, $course, $cm, $context, $filearea, $itemid, $filepath, $filename) {
+    global $CFG, $DB;
+    require_once("$CFG->dirroot/mod/imscp/locallib.php");
+
+    // note: imscp_intro handled in file_browser automatically
+
+    if (!has_capability('moodle/course:managefiles', $context)) {
+        // no peaking here, sorry
+        return null;
+    }
+
+    if ($filearea !== 'imscp_content' and $filearea !== 'imscp_backup') {
+        return null;
+    }
+    if (is_null($itemid)) {
+        return new imscp_file_info($browser, $course, $cm, $context, $areas, $filearea, $itemid);
+    }
+
+    $fs = get_file_storage();
+    $filepath = is_null($filepath) ? '/' : $filepath;
+    $filename = is_null($filename) ? '.' : $filename;
+    if (!$storedfile = $fs->get_file($context->id, $filearea, $itemid, $filepath, $filename)) {
+        return null;
+    }
+
+    // do not allow manual modification of any files!
+    $urlbase = $CFG->wwwroot.'/pluginfile.php';
+    return new file_info_stored($browser, $context, $storedfile, $urlbase, $itemid, true, true, false, false);
+}
+
+/**
+ * Serves the imscp files.
+ *
+ * @param object $course
+ * @param object $cminfo
+ * @param object $context
+ * @param string $filearea
+ * @param array $args
+ * @param bool $forcedownload
+ * @return bool false if file not found, does not return if found - justsend the file
+ */
+function imscp_pluginfile($course, $cminfo, $context, $filearea, $args, $forcedownload) {
+    global $CFG, $DB;
+
+    if (!$cminfo->uservisible) {
+        return false;
+    }
+
+    if (!$cm = get_coursemodule_from_instance('imscp', $cminfo->instance, $course->id)) {
+        return false;
+    }
+
+    if ($filearea === 'imscp_content') {
+        require_course_login($course, true, $cm);
+        $revision = array_shift($args);
+        $fs = get_file_storage();
+        $relativepath = '/'.implode('/', $args);
+        if ($relativepath === '/imsmanifest.xml') {
+            if (!has_capability('moodle/course:managefiles', $context)) {
+                // no stealing of detailed package info ;-)
+                return false;
+            }
+        }
+        $fullpath = $context->id.$filearea.$revision.$relativepath;
+        if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
+            return false;
+        }
+
+        // finally send the file
+        send_stored_file($file, 86400, 0, $forcedownload);
+
+    } else if ($filearea === 'imscp_backup') {
+        require_login($course, false, $cm);
+        if (!has_capability('moodle/course:managefiles', $context)) {
+            // no stealing of package backups
+            return false;
+        }
+        $revision = array_shift($args);
+        $fs = get_file_storage();
+        $relativepath = '/'.implode('/', $args);
+        $fullpath = $context->id.$filearea.$revision.$relativepath;
+        if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
+            return false;
+        }
+
+        // finally send the file
+        send_stored_file($file, 86400, 0, $forcedownload);
+
+    } else {
+        return false;
+    }
+}
diff --git a/mod/imscp/locallib.php b/mod/imscp/locallib.php
new file mode 100644 (file)
index 0000000..e0e32af
--- /dev/null
@@ -0,0 +1,269 @@
+<?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/>.
+
+/**
+ * Private imscp module utility functions
+ *
+ * @package   mod-imscp
+ * @copyright 2009 Petr Skoda (http://skodak.org)
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once("$CFG->dirroot/mod/imscp/lib.php");
+require_once("$CFG->libdir/file/file_browser.php");
+require_once("$CFG->libdir/filelib.php");
+require_once("$CFG->libdir/resourcelib.php");
+
+function imscp_print_content($imscp, $cm, $course) {
+    global $PAGE, $CFG;
+
+    $items = unserialize($imscp->structure);
+    $first = reset($items);
+    $context = get_context_instance(CONTEXT_MODULE, $cm->id);
+    $urlbase = "$CFG->wwwroot/pluginfile.php";
+    $path = '/'.$context->id.'/imscp_content/'.$imscp->revision.'/'.$first['href'];
+    $firsturl = file_encode_url($urlbase, $path, false);
+
+    echo '<div id="imscp_layout">';
+    echo '<div id="imscp_toc">';
+    echo '<div id="imscp_tree"><ul>';
+    foreach ($items as $item) {
+        echo imscp_htmllize_item($item, $imscp, $cm);
+    }
+    echo '</ul></div></div>';
+    echo '</div>';
+    echo '<div id="imscp_navpanel"></div>';
+
+    echo $PAGE->requires->js_function_call('imscp_init')->now();
+    return;
+}
+
+/**
+ * Internal function - creates htmls structure suitable for YUI tree.
+ */
+function imscp_htmllize_item($item, $imscp, $cm) {
+    global $CFG;
+
+    $context = get_context_instance(CONTEXT_MODULE, $cm->id);
+    $urlbase = "$CFG->wwwroot/pluginfile.php";
+    $path = '/'.$context->id.'/imscp_content/'.$imscp->revision.'/'.$item['href'];
+    $url = file_encode_url($urlbase, $path, false);
+    $result = "<li><a href=\"$url\">".$item['title'].'</a>';
+    if ($item['subitems']) {
+        $result .= '<ul>';
+        foreach ($item['subitems'] as $subitem) {
+            $result .= imscp_htmllize_item($subitem, $imscp, $cm);
+        }
+        $result .= '</ul>';
+    }
+    $result .= '</li>';
+
+    return $result;
+}
+
+function imscp_parse_structure($imscp, $context) {
+    $fs = get_file_storage();
+
+    if (!$manifestfile = $fs->get_file($context->id, 'imscp_content', $imscp->revision, '/', 'imsmanifest.xml')) {
+        return null;
+    }
+
+
+    $doc = new DOMDocument();
+    if (!$doc->loadXML($manifestfile->get_content(), LIBXML_NONET)) {
+        return null;
+    }
+
+    // we put this fake URL as base in order to detect path changes caused by xml:base attributes
+    $doc->documentURI = 'http://grrr/';
+
+    $xmlorganizations = $doc->getElementsByTagName('organizations');
+    if (empty($xmlorganizations->length)) {
+        return null;
+    }
+    $default = null;
+    if ($xmlorganizations->item(0)->attributes->getNamedItem('default')) {
+        $default = $xmlorganizations->item(0)->attributes->getNamedItem('default')->nodeValue;
+    }
+    $xmlorganization = $doc->getElementsByTagName('organization');
+    if (empty($xmlorganization->length)) {
+        return null;
+    }
+    $organization = null;
+    foreach ($xmlorganization as $org) {
+        if (is_null($organization)) {
+            // use first if default nor found
+            $organization = $org;
+        }
+        if (!$org->attributes->getNamedItem('identifier')) {
+            continue;
+        }
+        if ($default === $org->attributes->getNamedItem('identifier')->nodeValue) {
+            // found default - use it
+            $organization = $org;
+            break;
+        }
+    }
+
+    // load all resources
+    $resources = array();
+
+    $xmlresources = $doc->getElementsByTagName('resource');
+    foreach ($xmlresources as $res) {
+        if (!$identifier = $res->attributes->getNamedItem('identifier')) {
+            continue;
+        }
+        $identifier = $identifier->nodeValue;
+        if ($xmlbase = $res->baseURI) {
+            // undo the fake URL, we are interested in relative links only
+            $xmlbase = str_replace('http://grrr/', '/', $xmlbase);
+            $xmlbase = rtrim($xmlbase, '/').'/';
+        } else {
+            $xmlbase = '';
+        }
+        if (!$href = $res->attributes->getNamedItem('href')) {
+            continue;
+        }
+        $href = $href->nodeValue;
+        if (strpos($href, 'http://') !== 0) {
+            $href = $xmlbase.$href;
+        }
+        // href cleanup - Some packages are poorly done and use \ in urls
+        $href = ltrim(strtr($href, "\\", '/'), '/');
+        $resources[$identifier] = $href;
+    }
+
+    $items = array();
+    foreach ($organization->childNodes as $child) {
+        if ($child->nodeName === 'item') {
+            if (!$item = imscp_recursive_item($child, 0, $resources)) {
+                continue;
+            }
+            $items[] = $item;
+        }
+    }
+
+    return $items;
+}
+
+function imscp_recursive_item($xmlitem, $level, $resources) {
+    $identifierref = '';
+    if ($identifierref = $xmlitem->attributes->getNamedItem('identifierref')) {
+        $identifierref = $identifierref->nodeValue;
+    }
+
+    $title = '?';
+    $subitems = array();
+
+    foreach ($xmlitem->childNodes as $child) {
+        if ($child->nodeName === 'title') {
+            $title = $child->textContent;
+
+        } else if ($child->nodeName === 'item') {
+            if ($subitem = imscp_recursive_item($child, $level+1, $resources)) {
+                $subitems[] = $subitem;
+            }
+        }
+    }
+
+    return array('href'     => isset($resources[$identifierref]) ? $resources[$identifierref] : '',
+                 'title'    => $title,
+                 'level'    => $level,
+                 'subitems' => $subitems,
+                );
+}
+
+/**
+ * File browsing support class
+ */
+class imscp_file_info extends file_info {
+    protected $course;
+    protected $cm;
+    protected $areas;
+    protected $filearea;
+
+    public function __construct($browser, $course, $cm, $context, $areas, $filearea) {
+        parent::__construct($browser, $context);
+        $this->course   = $course;
+        $this->cm       = $cm;
+        $this->areas    = $areas;
+        $this->filearea = $filearea;
+    }
+
+    /**
+     * Returns list of standard virtual file/directory identification.
+     * The difference from stored_file parameters is that null values
+     * are allowed in all fields
+     * @return array with keys contextid, filearea, itemid, filepath and filename
+     */
+    public function get_params() {
+        return array('contextid'=>$this->context->id,
+                     'filearea' =>$this->filearea,
+                     'itemid'   =>null,
+                     'filepath' =>null,
+                     'filename' =>null);
+    }
+
+    /**
+     * Returns localised visible name.
+     * @return string
+     */
+    public function get_visible_name() {
+        return $this->areas[$this->filearea];
+    }
+
+    /**
+     * Can I add new files or directories?
+     * @return bool
+     */
+    public function is_writable() {
+        return false;
+    }
+
+    /**
+     * Is directory?
+     * @return bool
+     */
+    public function is_directory() {
+        return true;
+    }
+
+    /**
+     * Returns list of children.
+     * @return array of file_info instances
+     */
+    public function get_children() {
+        global $DB;
+
+        $children = array();
+        $itemids = $DB->get_records('files', array('contextid'=>$this->context->id, 'filearea'=>$this->filearea), 'itemid', "DISTINCT itemid");
+        foreach ($itemids as $itemid=>$unused) {
+            if ($child = $this->browser->get_file_info($this->context, $this->filearea, $itemid)) {
+                $children[] = $child;
+            }
+        }
+        return $children;
+    }
+
+    /**
+     * Returns parent file_info instance
+     * @return file_info or null for root
+     */
+    public function get_parent() {
+        return $this->browser->get_file_info($this->context);
+    }
+}
diff --git a/mod/imscp/mod_form.php b/mod/imscp/mod_form.php
new file mode 100644 (file)
index 0000000..7f4c5a6
--- /dev/null
@@ -0,0 +1,102 @@
+<?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/>.
+
+/**
+ * IMS CP configuration form
+ *
+ * @package   mod-imscp
+ * @copyright 2009 Petr Skoda (http://skodak.org)
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once ($CFG->dirroot.'/course/moodleform_mod.php');
+require_once($CFG->dirroot.'/mod/resource/locallib.php');
+require_once($CFG->libdir.'/filelib.php');
+
+class mod_imscp_mod_form extends moodleform_mod {
+    function definition() {
+        global $CFG, $DB;
+        $mform = $this->_form;
+
+        $config = get_config('imscp');
+
+        //-------------------------------------------------------
+        $mform->addElement('header', 'general', get_string('general', 'form'));
+        $mform->addElement('text', 'name', get_string('name'), array('size'=>'48'));
+        if (!empty($CFG->formatstringstriptags)) {
+            $mform->setType('name', PARAM_TEXT);
+        } else {
+            $mform->setType('name', PARAM_CLEAN);
+        }
+        $mform->addRule('name', null, 'required', null, 'client');
+        $this->add_intro_editor($config->requiremodintro);
+
+        //-------------------------------------------------------
+        $mform->addElement('header', 'content', get_string('contentheader', 'imscp'));
+        $mform->addElement('static', 'note', '', '<i>(TODO: Filepicker is borked, sorry.)</i>');
+//        $mform->addElement('filepicker', 'package', get_string('packagefile', 'imscp'));
+        $mform->setMaxFileSize(10000000);
+        $mform->addElement('file', 'package', get_string('packagefile', 'imscp'));
+
+        $options = array('-1'=>get_string('all'), '0'=>get_string('no'), '1'=>'1', '2'=>'2', '5'=>'5', '10'=>'10', '20'=>'20');
+        $mform->addElement('select', 'keepold', get_string('keepold', 'imscp'), $options);
+        $mform->setDefault('keepold', $config->keepold);
+        $mform->setAdvanced('keepold', $config->keepold_adv);
+
+        //-------------------------------------------------------
+        $this->standard_coursemodule_elements();
+
+        //-------------------------------------------------------
+        $this->add_action_buttons();
+    }
+
+    function validation($data, $files) {
+        global $USER;
+
+        if ($errors = parent::validation($data, $files)) {
+            return $errors;
+        }
+
+        $usercontext = get_context_instance(CONTEXT_USER, $USER->id);
+        $fs = get_file_storage();
+        /*
+        if (!$files = $fs->get_area_files($usercontext->id, 'user_draft', $data['package'], 'id', false)) {
+            if (!$this->current->instance) {
+                $errors['package'] = get_string('required');
+                return $errors;
+            }
+        }
+
+        $files = reset($file);
+
+        if ($file->get_mimetype() != 'application/zip') {
+            $errors['package'] = get_string('invalidfiletype', 'error', '', $file);
+            // better delete current file, it is not usable anyway
+            $fs->delete_area_files($usercontext->id, 'user_draft', $data['package']);
+        }*/
+
+        // TODO: filepicker borked
+        if (empty($files)) {
+            if (!$this->current->instance) {
+                $errors['package'] = get_string('required');
+                return $errors;
+            }
+        }
+
+        return $errors;
+    }
+}
diff --git a/mod/imscp/readme.txt b/mod/imscp/readme.txt
new file mode 100644 (file)
index 0000000..99badfa
--- /dev/null
@@ -0,0 +1,29 @@
+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/>.
+
+copyright 2009 Petr Skoda (http://skodak.org)
+license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+
+
+IMS CP module
+=============
+
+IMS CP module is a successor to original 'ims' type plugin of Resource module.
+
+TODO:
+ * implement portfolio support
+ * new backup/restore (Eloy)
+ * old restore support (Eloy)
+ * reimplement some repository support (Eloy)
\ No newline at end of file
diff --git a/mod/imscp/settings.php b/mod/imscp/settings.php
new file mode 100644 (file)
index 0000000..d28b52b
--- /dev/null
@@ -0,0 +1,38 @@
+<?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/>.
+
+/**
+ * IMS CP module admin settings and defaults
+ *
+ * @package   mod-imscp
+ * @copyright 2009 Petr Skoda (http://skodak.org)
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+if ($ADMIN->fulltree) {
+
+    //--- general settings -----------------------------------------------------------------------------------
+    $settings->add(new admin_setting_configcheckbox('imscp/requiremodintro', get_string('requiremodintro', 'admin'),
+                                        get_string('configrequiremodintro', 'admin'), 1));
+
+    //--- modedit defaults -----------------------------------------------------------------------------------
+    $settings->add(new admin_setting_heading('imscpmodeditdefaults', get_string('modeditdefaults', 'admin'), get_string('condifmodeditdefaults', 'admin')));
+    $options = array('-1'=>get_string('all'), '0'=>get_string('no'), '1'=>'1', '2'=>'2', '5'=>'5', '10'=>'10', '20'=>'20');
+    $settings->add(new admin_setting_configselect_with_advanced('imscp/keepold',
+        get_string('keepold', 'imscp'), get_string('keepoldexplain', 'imscp'),
+        array('value'=>1, 'adv'=>false), $options));
+}
diff --git a/mod/imscp/style.css b/mod/imscp/style.css
new file mode 100644 (file)
index 0000000..aae22d6
--- /dev/null
@@ -0,0 +1,28 @@
+/*** Modules: IMS CP ***/
+
+/* TODO: yui styling has to go here because the order of CSS loading is not solved yet */
+
+#mod-imscp-view #imscp_nav {
+ text-align: center;
+ margin-bottom: 5px;
+ margin-top: 5px;
+}
+
+#mod-imscp-view #imscp_toc .ygtv-highlight1 {
+  font-weight: bold;
+}
+
+#mod-imscp-view #footer {
+ display:none;
+}
+
+/* this is a temporary hack until we solve theYUI CSS trouble */
+#mod-imscp-view .yui-layout-hd {
+ background-image: none;
+ height:3em;
+ background-color: #DDDDDD;
+}
+
+#mod-imscp-view .yui-layout-hd h2 {
+ color: black;
+}
\ No newline at end of file
diff --git a/mod/imscp/version.php b/mod/imscp/version.php
new file mode 100644 (file)
index 0000000..d7fa865
--- /dev/null
@@ -0,0 +1,29 @@
+<?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/>.
+
+/**
+ * IMS CP module version information
+ *
+ * @package   mod-imscp
+ * @copyright 2009 Petr Skoda (http://skodak.org)
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$module->version  = 2009080500;
+$module->requires = 2009073101;  // Requires this Moodle version
+$module->cron     = 0;
+
diff --git a/mod/imscp/view.php b/mod/imscp/view.php
new file mode 100644 (file)
index 0000000..dcc95aa
--- /dev/null
@@ -0,0 +1,83 @@
+<?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/>.
+
+/**
+ * IMS CP module main user interface
+ *
+ * @package   mod-imscp
+ * @copyright 2009 Petr Skoda (http://skodak.org)
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require('../../config.php');
+require_once("$CFG->dirroot/mod/imscp/locallib.php");
+
+$id = optional_param('id', 0, PARAM_INT);  // Course module ID
+$i  = optional_param('i', 0, PARAM_INT);   // IMS CP instance id
+
+if ($i) {  // Two ways to specify the module
+    $imscp = $DB->get_record('imscp', array('id'=>$i), '*', MUST_EXIST);
+    $cm = get_coursemodule_from_instance('imscp', $imscp->id, $imscp->course, false, MUST_EXIST);
+
+} else {
+    $cm = get_coursemodule_from_id('imscp', $id, 0, false, MUST_EXIST);
+    $imscp = $DB->get_record('imscp', array('id'=>$cm->instance), '*', MUST_EXIST);
+}
+
+$course = $DB->get_record('course', array('id'=>$cm->course), '*', MUST_EXIST);
+
+require_course_login($course, true, $cm);
+$context = get_context_instance(CONTEXT_MODULE, $cm->id);
+
+add_to_log($course->id, 'imscp', 'view', 'view.php?id='.$cm->id, $imscp->id, $cm->id);
+
+$PAGE->set_url('mod/imscp/view.php', array('id' => $cm->id));
+$PAGE->requires->yui_lib('json')->in_head();
+$PAGE->requires->yui_lib('event')->in_head();
+$PAGE->requires->yui_lib('treeview')->in_head();
+$PAGE->requires->yui_lib('layout')->in_head();
+$PAGE->requires->yui_lib('button')->in_head();
+$PAGE->requires->yui_lib('container')->in_head();
+$PAGE->requires->yui_lib('dragdrop')->in_head();
+$PAGE->requires->yui_lib('resize')->in_head();
+$PAGE->requires->js('mod/imscp/functions.js')->in_head();
+$PAGE->requires->css('mod/imscp/style.css');
+
+$PAGE->requires->string_for_js('navigation', 'imscp');
+$PAGE->requires->string_for_js('toc', 'imscp');
+$PAGE->requires->string_for_js('hide', 'moodle');
+$PAGE->requires->string_for_js('show', 'moodle');
+
+// TODO: ugly hack for a page layout without footer and blocks
+$PAGE->set_generaltype('topframe');
+
+$PAGE->set_title($course->shortname.': '.$imscp->name);
+$PAGE->set_heading($course->fullname);
+$PAGE->set_activity_record($imscp);
+$PAGE->set_button(update_module_button($cm->id, '', get_string('modulename', 'imscp')));
+echo $OUTPUT->header(build_navigation('', $cm), navmenu($course, $cm));
+
+// verify imsmanifest was parsed properly
+if (!$imscp->structure) {
+    echo $OUTPUT->notification(get_string('deploymenterror', 'imscp'), 'notifyproblem');
+    echo $OUTPUT->footer();
+    die;
+}
+
+imscp_print_content($imscp, $cm, $course);
+
+echo $OUTPUT->footer();