2 // This file is part of Moodle - http://moodle.org/
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
18 * Book imscp export lib
20 * @package booktool_exportimscp
21 * @copyright 2001-3001 Antonio Vicent {@link http://ludens.es}
22 * @copyright 2001-3001 Eloy Lafuente (stronk7) {@link http://stronk7.com}
23 * @copyright 2011 Petr Skoda {@link http://skodak.org}
24 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
27 defined('MOODLE_INTERNAL') || die;
29 require_once(dirname(__FILE__).'/lib.php');
30 require_once($CFG->dirroot.'/mod/book/locallib.php');
33 * Export one book as IMSCP package
35 * @param stdClass $book book instance
36 * @param context_module $context
37 * @return bool|stored_file
39 function booktool_exportimscp_build_package($book, $context) {
42 $fs = get_file_storage();
44 if ($packagefile = $fs->get_file($context->id, 'booktool_exportimscp', 'package', $book->revision, '/', 'imscp.zip')) {
48 // fix structure and test if chapters present
49 if (!book_preload_chapters($book)) {
50 print_error('nochapters', 'booktool_exportimscp');
53 // prepare temp area with package contents
54 booktool_exportimscp_prepare_files($book, $context);
56 $packer = get_file_packer('application/zip');
57 $areafiles = $fs->get_area_files($context->id, 'booktool_exportimscp', 'temp', $book->revision, "sortorder, itemid, filepath, filename", false);
59 foreach ($areafiles as $file) {
60 $path = $file->get_filepath().$file->get_filename();
61 $path = ltrim($path, '/');
62 $files[$path] = $file;
65 $packagefile = $packer->archive_to_storage($files, $context->id, 'booktool_exportimscp', 'package', $book->revision, '/', 'imscp.zip');
68 $fs->delete_area_files($context->id, 'booktool_exportimscp', 'temp', $book->revision);
70 // delete older versions
71 $sql = "SELECT DISTINCT itemid
73 WHERE contextid = :contextid AND component = 'booktool_exportimscp' AND itemid < :revision";
74 $params = array('contextid'=>$context->id, 'revision'=>$book->revision);
75 $revisions = $DB->get_records_sql($sql, $params);
76 foreach ($revisions as $rev => $unused) {
77 $fs->delete_area_files($context->id, 'booktool_exportimscp', 'temp', $rev);
78 $fs->delete_area_files($context->id, 'booktool_exportimscp', 'package', $rev);
85 * Prepare temp area with the files used by book html contents
87 * @param stdClass $book book instance
88 * @param context_module $context
90 function booktool_exportimscp_prepare_files($book, $context) {
93 $fs = get_file_storage();
95 $temp_file_record = array('contextid'=>$context->id, 'component'=>'booktool_exportimscp', 'filearea'=>'temp', 'itemid'=>$book->revision);
96 $chapters = $DB->get_records('book_chapters', array('bookid'=>$book->id), 'pagenum');
97 $chapterresources = array();
98 foreach ($chapters as $chapter) {
99 $chapterresources[$chapter->id] = array();
100 $files = $fs->get_area_files($context->id, 'mod_book', 'chapter', $chapter->id, "sortorder, itemid, filepath, filename", false);
101 foreach ($files as $file) {
102 $temp_file_record['filepath'] = '/'.$chapter->pagenum.$file->get_filepath();
103 $fs->create_file_from_storedfile($temp_file_record, $file);
104 $chapterresources[$chapter->id][] = $chapter->pagenum.$file->get_filepath().$file->get_filename();
106 if ($file = $fs->get_file($context->id, 'booktool_exportimscp', 'temp', $book->revision, "/$chapter->pagenum/", 'index.html')) {
107 // this should not exist
110 $content = booktool_exportimscp_chapter_content($chapter, $context);
111 $index_file_record = array('contextid'=>$context->id, 'component'=>'booktool_exportimscp', 'filearea'=>'temp',
112 'itemid'=>$book->revision, 'filepath'=>"/$chapter->pagenum/", 'filename'=>'index.html');
113 $fs->create_file_from_string($index_file_record, $content);
116 $css_file_record = array('contextid'=>$context->id, 'component'=>'booktool_exportimscp', 'filearea'=>'temp',
117 'itemid'=>$book->revision, 'filepath'=>"/css/", 'filename'=>'styles.css');
118 $fs->create_file_from_pathname($css_file_record, dirname(__FILE__).'/imscp.css');
120 // Init imsmanifest and others
125 // Moodle and Book version
126 $moodle_release = $CFG->release;
127 $moodle_version = $CFG->version;
128 $book_version = get_config('mod_book', 'version');
129 $bookname = format_string($book->name, true, array('context'=>$context));
131 // Load manifest header
132 $imsmanifest .= '<?xml version="1.0" encoding="UTF-8"?>
133 <!-- This package has been created with Moodle ' . $moodle_release . ' (' . $moodle_version . ') http://moodle.org/, Book module version ' . $book_version . ' - https://github.com/skodak/moodle-mod_book -->
134 <!-- One idea and implementation by Eloy Lafuente (stronk7) and Antonio Vicent (C) 2001-3001 -->
135 <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">
136 <organizations default="MOODLE-' . $book->course . '-' . $book->id . '">
137 <organization identifier="MOODLE-' . $book->course . '-' . $book->id . '" structure="hierarchical">
138 <title>' . htmlspecialchars($bookname) . '</title>';
140 // To store the prev level (book only have 0 and 1)
143 foreach ($chapters as $chapter) {
144 // Calculate current level ((book only have 0 and 1)
145 $currlevel = empty($chapter->subchapter) ? 0 : 1;
146 // Based upon prevlevel and current one, decide what to close
147 if ($prevlevel !== null) {
148 // Calculate the number of spaces (for visual xml-text formating)
149 $prevspaces = substr(' ', 0, $currlevel * 2);
151 // Same level, simply close the item
152 if ($prevlevel == $currlevel) {
153 $imsitems .= $prevspaces . ' </item>' . "\n";
155 // Bigger currlevel, nothing to close
156 // Smaller currlevel, close both the current item and the parent one
157 if ($prevlevel > $currlevel) {
158 $imsitems .= ' </item>' . "\n";
159 $imsitems .= ' </item>' . "\n";
163 $prevlevel = $currlevel;
165 // Calculate the number of spaces (for visual xml-text formatting)
166 $currspaces = substr(' ', 0, $currlevel * 2);
168 $chaptertitle = format_string($chapter->title, true, array('context'=>$context));
171 $imsitems .= $currspaces .' <item identifier="ITEM-' . $book->course . '-' . $book->id . '-' . $chapter->pagenum .'" isvisible="true" identifierref="RES-' .
172 $book->course . '-' . $book->id . '-' . $chapter->pagenum . "\">\n" .
173 $currspaces . ' <title>' . htmlspecialchars($chaptertitle) . '</title>' . "\n";
175 // Add the imsresources
176 // First, check if we have localfiles
177 $localfiles = array();
178 foreach ($chapterresources[$chapter->id] as $localfile) {
179 $localfiles[] = "\n" . ' <file href="' . $localfile . '" />';
181 // Now add the dependency to css
182 $cssdependency = "\n" . ' <dependency identifierref="RES-' . $book->course . '-' . $book->id . '-css" />';
183 // Now build the resources section
184 $imsresources .= ' <resource identifier="RES-' . $book->course . '-' . $book->id . '-' . $chapter->pagenum . '" type="webcontent" xml:base="' .
185 $chapter->pagenum . '/" href="index.html">' . "\n" .
186 ' <file href="' . $chapter->pagenum . '/index.html" />' . implode($localfiles) . $cssdependency . "\n".
187 ' </resource>' . "\n";
190 // Close items (the latest chapter)
192 if ($currlevel == 0) {
193 $imsitems .= ' </item>' . "\n";
196 if ($currlevel == 1) {
197 $imsitems .= ' </item>' . "\n";
198 $imsitems .= ' </item>' . "\n";
201 // Define the css common resource
202 $cssresource = ' <resource identifier="RES-' . $book->course . '-' . $book->id . '-css" type="webcontent" xml:base="css/" href="styles.css">
203 <file href="css/styles.css" />
206 // Add imsitems to manifest
207 $imsmanifest .= "\n" . $imsitems;
208 // Close the organization
209 $imsmanifest .= " </organization>
211 // Add resources to manifest
212 $imsmanifest .= "\n <resources>\n" . $imsresources . $cssresource . " </resources>";
214 $imsmanifest .= "\n</manifest>\n";
216 $manifest_file_record = array('contextid'=>$context->id, 'component'=>'booktool_exportimscp', 'filearea'=>'temp',
217 'itemid'=>$book->revision, 'filepath'=>"/", 'filename'=>'imsmanifest.xml');
218 $fs->create_file_from_string($manifest_file_record, $imsmanifest);
222 * Returns the html contents of one book's chapter to be exported as IMSCP
224 * @param stdClass $chapter the chapter to be exported
225 * @param context_module $context context the chapter belongs to
226 * @return string the contents of the chapter
228 function booktool_exportimscp_chapter_content($chapter, $context) {
230 $options = new stdClass();
231 $options->noclean = true;
232 $options->context = $context;
234 $chaptercontent = str_replace('@@PLUGINFILE@@/', '', $chapter->content);
235 $chaptercontent = format_text($chaptercontent, $chapter->contentformat, $options);
237 $chaptertitle = format_string($chapter->title, true, array('context'=>$context));
240 $content .= '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">' . "\n";
241 $content .= '<html>' . "\n";
242 $content .= '<head>' . "\n";
243 $content .= '<meta http-equiv="content-type" content="text/html; charset=utf-8" />' . "\n";
244 $content .= '<link rel="stylesheet" type="text/css" href="../css/styles.css" />' . "\n";
245 $content .= '<title>' . $chaptertitle . '</title>' . "\n";
246 $content .= '</head>' . "\n";
247 $content .= '<body>' . "\n";
248 $content .= '<h1 id="header">' . $chaptertitle . '</h1>' ."\n";
249 $content .= $chaptercontent . "\n";
250 $content .= '</body>' . "\n";
251 $content .= '</html>' . "\n";