Adding IMS-CP export functionality to the book module.
[moodle.git] / generateimscp.php
CommitLineData
fc9479b8
EL
1<?PHP // $Id: generateimscp.php,v 1.1 2007/02/11 12:18:23 stronk7 Exp $
2
3///////////////////////////////////////////////////////////////////////////
4// //
5// NOTICE OF COPYRIGHT //
6// //
7// Moodle - Modular Object-Oriented Dynamic Learning Environment //
8// http://moodle.com //
9// //
10// Copyright (C) 2001-3001 Antonio Vicent http://ludens.es //
11// (C) 2001-3001 Eloy Lafuente (stronk7) http://contiento.com //
12// //
13// This program is free software; you can redistribute it and/or modify //
14// it under the terms of the GNU General Public License as published by //
15// the Free Software Foundation; either version 2 of the License, or //
16// (at your option) any later version. //
17// //
18// This program is distributed in the hope that it will be useful, //
19// but WITHOUT ANY WARRANTY; without even the implied warranty of //
20// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
21// GNU General Public License for more details: //
22// //
23// http://www.gnu.org/copyleft/gpl.html //
24// //
25///////////////////////////////////////////////////////////////////////////
26
27require_once('../../config.php');
28require_once('lib.php');
29require_once($CFG->dirroot . '/backup/lib.php');
30require_once($CFG->dirroot . '/lib/filelib.php');
31
32$id = required_param('id', PARAM_INT); // Course Module ID
33
34if ($CFG->forcelogin) {
35 require_login();
36}
37
38if (!$cm = get_record('course_modules', 'id', $id)) {
39 error('Course Module ID was incorrect');
40}
41
42if (!$course = get_record('course', 'id', $cm->course)) {
43 error('Course is misconfigured');
44}
45
46if ($course->category) {
47 require_login($course->id);
48}
49
50if (!$cm->visible and !isteacher($course->id)) {
51 notice(get_string('activityiscurrentlyhidden'));
52}
53
54if (!$book = get_record('book', 'id', $cm->instance)) {
55 error('Course module is incorrect');
56}
57
58if (!isteacher($course->id)) {
59 error('Only teachers are allowed to generate IMS CP packages');
60}
61
62$strbooks = get_string('modulenameplural', 'book');
63$strbook = get_string('modulename', 'book');
64$strtop = get_string('top', 'book');
65
66add_to_log($course->id, 'book', 'generateimscp', 'generateimscp.php?id='.$cm->id, $book->id, $cm->id);
67
68/// Get all the chapters
69 $chapters = get_records('book_chapters', 'bookid', $book->id, 'pagenum');
70
71/// Generate the manifest and all the contents
72 chapters2imsmanifest($chapters, $book, $cm);
73
74/// Now zip everything
75 make_upload_directory('temp');
76 $zipfile = $CFG->dataroot . "/temp/". time() . '.zip';
77 $files = get_directory_list($CFG->dataroot . "/$cm->course/moddata/book/$book->id", basename($zipfile), false, true, true);
78 foreach ($files as $key => $value) {
79 $files[$key] = $CFG->dataroot . "/$cm->course/moddata/book/$book->id/" . $value;
80 }
81 zip_files($files, $zipfile);
82/// Now delete all the temp dirs
83 delete_dir_contents($CFG->dataroot . "/$cm->course/moddata/book/$book->id");
84/// Now serve the file
85 send_file($zipfile, clean_filename($book->name) . '.zip', 86400, 0, false, true);
86
87/**
88 * This function will create the default imsmanifest plus contents for the book chapters passed as array
89 * Everything will be created under the book moddata file area *
90 */
91function chapters2imsmanifest ($chapters, $book, $cm) {
92
93 global $CFG;
94
95/// Init imsmanifest and others
96 $imsmanifest = '';
97 $imsitems = '';
98 $imsresources = '';
99
100/// Moodle and Book version
101 $moodle_release = $CFG->release;
102 $moodle_version = $CFG->version;
103 $book_version = get_field('modules', 'version', 'name', 'book');
104
105/// Load manifest header
106 $imsmanifest .= '<?xml version="1.0" encoding="UTF-8"?>
107<!-- This package has been created with Moodle ' . $moodle_release . ' (' . $moodle_version . '), Book module version ' . $book_version . ' - http://moodle.org -->
108<!-- One idea and implementation by Eloy Lafuente (stronk7) and Antonio Vicent (C) 2001-3001 -->
109<manifest xmlns="http://www.imsglobal.org/xsd/imscp_v1p1" xmlns:imsmd="http://www.imsglobal.org/xsd/imsmd_v1p2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" identifier="MANIFEST-' . md5($CFG->wwwroot . '-' . $book->course . '-' . $book->id) . '" xsi:schemaLocation="http://www.imsglobal.org/xsd/imscp_v1p1 imscp_v1p1.xsd http://www.imsglobal.org/xsd/imsmd_v1p2 imsmd_v1p2p2.xsd">
110 <organizations default="MOODLE-' . $book->course . '-' . $book->id . '">
111 <organization identifier="MOODLE-' . $book->course . '-' . $book->id . '" structure="hierarchical">
112 <title>' . htmlspecialchars($book->name) . '</title>';
113
114/// Create the temp directory
115 $moddir = "$cm->course/moddata/book/$book->id";
116 make_upload_directory($moddir);
117
118/// For each chapter, create the corresponding directory and save contents there
119
120/// To store the prev level (book only have 0 and 1)
121 $prevlevel = null;
122 foreach ($chapters as $chapter) {
123 /// Calculate current level ((book only have 0 and 1)
124 $currlevel = empty($chapter->subchapter) ? 0 : 1;
125 /// Based upon prevlevel and current one, decide what to close
126 if ($prevlevel !== null) {
127 /// Calculate the number of spaces (for visual xml-text formating)
128 $prevspaces = substr(' ', 0, $currlevel * 2);
129
130 /// Same level, simply close the item
131 if ($prevlevel == $currlevel) {
132 $imsitems .= $prevspaces . ' </item>' . "\n";
133 }
134 /// Bigger currlevel, nothing to close
135 /// Smaller currlevel, close both the current item and the parent one
136 if ($prevlevel > $currlevel) {
137 $imsitems .= ' </item>' . "\n";
138 $imsitems .= ' </item>' . "\n";
139 }
140 }
141 /// Update prevlevel
142 $prevlevel = $currlevel;
143
144 /// Calculate the number of spaces (for visual xml-text formating)
145 $currspaces = substr(' ', 0, $currlevel * 2);
146
147 /// Create the static html file + local attachments (images...)
148 $chapterdir = "$moddir/$chapter->pagenum";
149 make_upload_directory($chapterdir);
150 $chaptercontent = chapter2html($chapter, $book->course, $book->id);
151 file_put_contents($CFG->dataroot . "/" . $chapterdir . "/index.html", $chaptercontent->content);
152 /// Add the imsitems
153 $imsitems .= $currspaces .' <item identifier="ITEM-' . $book->course . '-' . $book->id . '-' . $chapter->pagenum .'" isvisible="true" identifierref="RES-' . $book->course . '-' . $book->id . '-' . $chapter->pagenum . '">
154 ' . $currspaces . ' <title>' . htmlspecialchars($chapter->title) . '</title>' . "\n";
155
156 /// Add the imsresources
157 /// First, check if we have localfiles
158 $localfiles = '';
159 if ($chaptercontent->localfiles) {
160 foreach ($chaptercontent->localfiles as $localfile) {
161 $localfiles .= "\n" . ' <file href="' . $chapter->pagenum . '/' . $localfile . '" />';
162 }
163 }
164 /// Now add the dependency to css
165 $cssdependency = "\n" . ' <dependency identifierref="RES-' . $book->course . '-' . $book->id . '-css" />';
166 /// Now build the resources section
167 $imsresources .= ' <resource identifier="RES-' . $book->course . '-' . $book->id . '-' . $chapter->pagenum . '" type="webcontent" xml:base="' . $chapter->pagenum . '/" href="index.html">
168 <file href="' . $chapter->pagenum . '/index.html" />' . $localfiles . $cssdependency . '
169 </resource>' . "\n";
170 }
171
172/// Close items (the latest chapter)
173/// Level 1, close 1
174 if ($currlevel == 0) {
175 $imsitems .= ' </item>' . "\n";
176 }
177/// Level 2, close 2
178 if ($currlevel == 1) {
179 $imsitems .= ' </item>' . "\n";
180 $imsitems .= ' </item>' . "\n";
181 }
182
183/// Define the css common resource
184$cssresource = ' <resource identifier="RES-' . $book->course . '-' . $book->id . '-css" type="webcontent" xml:base="css/" href="styles.css">
185 <file href="css/styles.css" />
186 </resource>' . "\n";
187
188/// Add imsitems to manifest
189 $imsmanifest .= "\n" . $imsitems;
190/// Close the organization
191 $imsmanifest .= " </organization>
192 </organizations>";
193/// Add resources to manifest
194 $imsmanifest .= "\n <resources>\n" . $imsresources . $cssresource . " </resources>";
195/// Close manifest
196 $imsmanifest .= "\n</manifest>\n";
197
198 file_put_contents($CFG->dataroot . "/" . $moddir . '/imsmanifest.xml', $imsmanifest );
199
200/// Now send the css resource
201 make_upload_directory("$moddir/css");
202 file_put_contents($CFG->dataroot . "/" . $moddir . "/css/styles.css", file_get_contents("$CFG->dirroot/mod/book/generateimscp.css"));
203}
204
205/**
206 * This function will create one chaptercontent object, with the contents converted to html and
207 * one array of local images to be included
208 */
209function chapter2html($chapter, $courseid, $bookid) {
210
211 global $CFG;
212
213 $content = '';
214 $content .= '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">' . "\n";
215 $content .= '<html>' . "\n";
216 $content .= '<head>' . "\n";
217 $content .= '<meta http-equiv="content-type" content="text/html; charset=utf-8" />' . "\n";
218 $content .= '<link rel="stylesheet" type="text/css" href="../css/styles.css" />' . "\n";
219 $content .= '<title>' . $chapter->title . '</title>' . "\n";
220 $content .= '</head>' . "\n";
221 $content .= '<body>' . "\n";
222 $content .= '<h1 id="header">' . $chapter->title . '</h1>' ."\n";
223 $options->noclean = true;
224 $content .= format_text($chapter->content, '', $options, $courseid) . "\n";
225 $content .= '</body>' . "\n";
226 $content .= '</html>' . "\n";
227
228/// Now look for course-files in contents
229 $search = array($CFG->wwwroot.'/file.php/'.$courseid,
230 $CFG->wwwroot.'/file.php?file=/'.$courseid);
231 $replace = array('$@FILEPHP@$','$@FILEPHP@$');
232 $content = str_replace($search, $replace, $content);
233
234 $regexp = '/\$@FILEPHP@\$(.*?)"/is';
235 $localfiles = array();
236 $basefiles = array();
237 preg_match_all($regexp, $content, $list);
238
239 if ($list) {
240 /// Build the array of local files
241 foreach (array_unique($list[1]) as $key => $value) {
242 $localfiles['<#'. $key . '#>'] = $value;
243 $basefiles['<#'. $key . '#>'] = basename($value);
244 /// Copy files to current chapter directory
245 if (file_exists($CFG->dataroot . '/' . $courseid . '/' . $value)) {
246 copy($CFG->dataroot . '/' . $courseid . '/' . $value, $CFG->dataroot . '/' . $courseid . '/moddata/book/' . $bookid . '/' . $chapter->pagenum . '/' . basename ($value));
247 }
248 }
249 /// Replace contents by keys
250 $content = str_replace($localfiles, array_keys($localfiles), $content);
251 /// Replace keys by basefiles
252 $content = str_replace(array_keys($basefiles), $basefiles, $content);
253 /// Delete $@FILEPHP@$
254 $content = str_replace('$@FILEPHP@$', '', $content);
255 }
256
257/// Build the final object needed to have all the info in order to create the manifest
258 $object = new stdClass;
259 $object->content = $content;
260 $object->localfiles = $basefiles;
261
262 return $object;
263}
264
265?>