fix extra height attribute
[moodle.git] / locallib.php
CommitLineData
e355240d
PS
1<?php
2// This file is part of Book module for Moodle - http://moodle.org/
3//
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.
8//
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.
13//
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/>.
16
17/**
18 * Book module local lib functions
19 *
20 * @package mod
21 * @subpackage book
22 * @copyright 2010 Petr Skoda {@link http://skodak.org}
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 */
25
26defined('MOODLE_INTERNAL') || die;
27
28define('BOOK_NUM_NONE', '0');
29define('BOOK_NUM_NUMBERS', '1');
30define('BOOK_NUM_BULLETS', '2');
31define('BOOK_NUM_INDENTED', '3');
32
33require_once($CFG->dirroot.'/mod/book/lib.php');
f4e1849e 34require_once($CFG->libdir.'/filelib.php');
e355240d
PS
35
36function book_get_numbering_types() {
37 return array (BOOK_NUM_NONE => get_string('numbering0', 'mod_book'),
38 BOOK_NUM_NUMBERS => get_string('numbering1', 'mod_book'),
39 BOOK_NUM_BULLETS => get_string('numbering2', 'mod_book'),
40 BOOK_NUM_INDENTED => get_string('numbering3', 'mod_book') );
41}
42
43
44//////////////////////////////////////////////////////////////////////////////////////
45/// Any other book functions go here. Each of them must have a name that
46/// starts with book_
47
2c1e98e6
PS
48/**
49 * check chapter ordering and make sure subchapter is not first
50 * in book hidden chapter must have all subchapters hidden too
51 * @param int $id
52 * @return void
53 */
e355240d 54function book_check_structure($bookid) {
2c1e98e6
PS
55 global $DB;
56
57 if ($chapters = $DB->get_records('book_chapters', array('bookid'=>$bookid), 'pagenum', 'id, pagenum, subchapter, hidden')) {
e355240d
PS
58 $first = true;
59 $hidesub = true;
60 $i = 1;
61 foreach($chapters as $ch) {
62 if ($first and $ch->subchapter) {
63 $ch->subchapter = 0;
64 }
65 $first = false;
66 if (!$ch->subchapter) {
67 $hidesub = $ch->hidden;
68 } else {
69 $ch->hidden = $hidesub ? true : $ch->hidden;
70 }
71 $ch->pagenum = $i;
2c1e98e6 72 $DB->update_record('book_chapters', $ch);
e355240d
PS
73 $i++;
74 }
75 }
76}
77
e355240d
PS
78/// general function for logging to table
79function book_log($str1, $str2, $level = 0) {
80 switch ($level) {
81 case 1:
82 echo '<tr><td><span class="dimmed_text">'.$str1.'</span></td><td><span class="dimmed_text">'.$str2.'</span></td></tr>';
83 break;
84 case 2:
85 echo '<tr><td><span style="color: rgb(255, 0, 0);">'.$str1.'</span></td><td><span style="color: rgb(255, 0, 0);">'.$str2.'</span></td></tr>';
86 break;
87 default:
88 echo '<tr><td>'.$str1.'</class></td><td>'.$str2.'</td></tr>';
89 break;
90 }
91}
92
93//=================================================
94// import functions
95//=================================================
96
97/// normalize relative links (= remove ..)
98function book_prepare_link($ref) {
99 if ($ref == '') {
100 return '';
101 }
102 $ref = str_replace('\\','/',$ref); //anti MS hack
103 $cnt = substr_count($ref, '..');
104 for($i=0; $i<$cnt; $i++) {
105 $ref = ereg_replace('[^/]+/\.\./', '', $ref);
106 }
107 //still any '..' left?? == error! error!
108 if (substr_count($ref, '..') > 0) {
109 return '';
110 }
111 if (ereg('[\|\`]', $ref)) { // check for other bad characters
112 return '';
113 }
114 return $ref;
115}
116
117/// read chapter content from file
118function book_read_chapter($base, $ref) {
119 $file = $base.'/'.$ref;
120 if (filesize($file) <= 0 or !is_readable($file)) {
121 book_log($ref, get_string('error'), 2);
122 return;
123 }
124 //first read data
125 $handle = fopen($file, "rb");
126 $contents = fread($handle, filesize($file));
127 fclose($handle);
128 //extract title
129 $chapter = new object();
130 if (preg_match('/<title>([^<]+)<\/title>/i', $contents, $matches)) {
131 $chapter->title = $matches[1];
132 } else {
133 $chapter->title = $ref;
134 }
135 //extract page body
136 if (preg_match('/<body[^>]*>(.+)<\/body>/is', $contents, $matches)) {
137 $chapter->content = $matches[1];
138 } else {
139 book_log($ref, get_string('error'), 2);
140 return;
141 }
142 book_log($ref, get_string('ok'));
143 $chapter->importsrc = $ref;
144 //extract page head
145 if (preg_match('/<head[^>]*>(.+)<\/head>/is', $contents, $matches)) {
146 $head = $matches[1];
147 if (preg_match('/charset=([^"]+)/is', $head, $matches)) {
148 $enc = $matches[1];
149 $textlib = textlib_get_instance();
150 $chapter->content = $textlib->convert($chapter->content, $enc, current_charset());
151 $chapter->title = $textlib->convert($chapter->title, $enc, current_charset());
152 }
153 if (preg_match_all('/<link[^>]+rel="stylesheet"[^>]*>/i', $head, $matches)) { //dlnsk extract links to css
154 for($i=0; $i<count($matches[0]); $i++){
155 $chapter->content = $matches[0][$i]."\n".$chapter->content;
156 }
157 }
158 }
159 return $chapter;
160}
161
162///relink images and relative links
163function book_relink($id, $bookid, $courseid) {
2c1e98e6
PS
164 global $CFG, $DB;
165
e355240d
PS
166 if ($CFG->slasharguments) {
167 $coursebase = $CFG->wwwroot.'/file.php/'.$courseid;
168 } else {
169 $coursebase = $CFG->wwwroot.'/file.php?file=/'.$courseid;
170 }
2c1e98e6 171 $chapters = $DB->get_records('book_chapters', array('bookid'=>$bookid), 'pagenum', 'id, pagenum, title, content, importsrc');
e355240d
PS
172 $originals = array();
173 foreach($chapters as $ch) {
174 $originals[$ch->importsrc] = $ch;
175 }
176 foreach($chapters as $ch) {
177 $rel = substr($ch->importsrc, 0, strrpos($ch->importsrc, '/')+1);
178 $base = $coursebase.strtr(urlencode($rel), array("%2F" => "/")); //for better internationalization (dlnsk)
179 $modified = false;
180 //image relinking
181 if ($ch->importsrc && preg_match_all('/(<img[^>]+src=")([^"]+)("[^>]*>)/i', $ch->content, $images)) {
182 for($i = 0; $i<count($images[0]); $i++) {
183 if (!preg_match('/[a-z]+:/i', $images[2][$i])) { // not absolute link
184 $link = book_prepare_link($base.$images[2][$i]);
185 if ($link == '') {
186 continue;
187 }
188 $origtag = $images[0][$i];
189 $newtag = $images[1][$i].$link.$images[3][$i];
190 $ch->content = str_replace($origtag, $newtag, $ch->content);
191 $modified = true;
192 book_log($ch->title, $images[2][$i].' --> '.$link);
193 }
194 }
195 }
196 //css relinking (dlnsk)
197 if ($ch->importsrc && preg_match_all('/(<link[^>]+href=")([^"]+)("[^>]*>)/i', $ch->content, $csslinks)) {
198 for($i = 0; $i<count($csslinks[0]); $i++) {
199 if (!preg_match('/[a-z]+:/i', $csslinks[2][$i])) { // not absolute link
200 $link = book_prepare_link($base.$csslinks[2][$i]);
201 if ($link == '') {
202 continue;
203 }
204 $origtag = $csslinks[0][$i];
205 $newtag = $csslinks[1][$i].$link.$csslinks[3][$i];
206 $ch->content = str_replace($origtag, $newtag, $ch->content);
207 $modified = true;
208 book_log($ch->title, $csslinks[2][$i].' --> '.$link);
209 }
210 }
211 }
212 //general embed relinking - flash and others??
213 if ($ch->importsrc && preg_match_all('/(<embed[^>]+src=")([^"]+)("[^>]*>)/i', $ch->content, $embeds)) {
214 for($i = 0; $i<count($embeds[0]); $i++) {
215 if (!preg_match('/[a-z]+:/i', $embeds[2][$i])) { // not absolute link
216 $link = book_prepare_link($base.$embeds[2][$i]);
217 if ($link == '') {
218 continue;
219 }
220 $origtag = $embeds[0][$i];
221 $newtag = $embeds[1][$i].$link.$embeds[3][$i];
222 $ch->content = str_replace($origtag, $newtag, $ch->content);
223 $modified = true;
224 book_log($ch->title, $embeds[2][$i].' --> '.$link);
225 }
226 }
227 }
228 //flash in IE <param name=movie value="something" - I do hate IE!
229 if ($ch->importsrc && preg_match_all('/<param[^>]+name\s*=\s*"?movie"?[^>]*>/i', $ch->content, $params)) {
230 for($i = 0; $i<count($params[0]); $i++) {
231 if (preg_match('/(value=\s*")([^"]+)(")/i', $params[0][$i], $values)) {
232 if (!preg_match('/[a-z]+:/i', $values[2])) { // not absolute link
233 $link = book_prepare_link($base.$values[2]);
234 if ($link == '') {
235 continue;
236 }
237 $newvalue = $values[1].$link.$values[3];
238 $newparam = str_replace($values[0], $newvalue, $params[0][$i]);
239 $ch->content = str_replace($params[0][$i], $newparam, $ch->content);
240 $modified = true;
241 book_log($ch->title, $values[2].' --> '.$link);
242 }
243 }
244 }
245 }
246 //java applet - add code bases if not present!!!!
247 if ($ch->importsrc && preg_match_all('/<applet[^>]*>/i', $ch->content, $applets)) {
248 for($i = 0; $i<count($applets[0]); $i++) {
249 if (!stripos($applets[0][$i], 'codebase')) {
250 $newapplet = str_ireplace('<applet', '<applet codebase="."', $applets[0][$i]);
251 $ch->content = str_replace($applets[0][$i], $newapplet, $ch->content);
252 $modified = true;
253 }
254 }
255 }
256 //relink java applet code bases
257 if ($ch->importsrc && preg_match_all('/(<applet[^>]+codebase=")([^"]+)("[^>]*>)/i', $ch->content, $codebases)) {
258 for($i = 0; $i<count($codebases[0]); $i++) {
259 if (!preg_match('/[a-z]+:/i', $codebases[2][$i])) { // not absolute link
260 $link = book_prepare_link($base.$codebases[2][$i]);
261 if ($link == '') {
262 continue;
263 }
264 $origtag = $codebases[0][$i];
265 $newtag = $codebases[1][$i].$link.$codebases[3][$i];
266 $ch->content = str_replace($origtag, $newtag, $ch->content);
267 $modified = true;
268 book_log($ch->title, $codebases[2][$i].' --> '.$link);
269 }
270 }
271 }
272 //relative link conversion
273 if ($ch->importsrc && preg_match_all('/(<a\s[^>]*href=")([^"^#]*)(#[^"]*)?("[^>]*>)/i', $ch->content, $links)) {
274 for($i = 0; $i<count($links[0]); $i++) {
275 if ($links[2][$i] != '' //check for inner anchor links
276 && !preg_match('/[a-z]+:/i', $links[2][$i])) { //not absolute link
277 $origtag = $links[0][$i];
278 $target = book_prepare_link($rel.$links[2][$i]); //target chapter
279 if ($target != '' && array_key_exists($target, $originals)) {
280 $o = $originals[$target];
281 $newtag = $links[1][$i].$CFG->wwwroot.'/mod/book/view.php?id='.$id.'&chapterid='.$o->id.$links[3][$i].$links[4][$i];
282 $newtag = preg_replace('/target=[^\s>]/i','', $newtag);
283 $ch->content = str_replace($origtag, $newtag, $ch->content);
284 $modified = true;
285 book_log($ch->title, $links[2][$i].$links[3][$i].' --> '.$CFG->wwwroot.'/mod/book/view.php?id='.$id.'&chapterid='.$o->id.$links[3][$i]);
286 } else if ($target!='' && (!preg_match('/\.html$|\.htm$/i', $links[2][$i]))) { // other relative non html links converted to download links
287 $target = book_prepare_link($base.$links[2][$i]);
288 $origtag = $links[0][$i];
289 $newtag = $links[1][$i].$target.$links[4][$i];
290 $ch->content = str_replace($origtag, $newtag, $ch->content);
291 $modified = true;
292 book_log($ch->title, $links[2][$i].' --> '.$target);
293 }
294 }
295 }
296 }
297 if ($modified) {
2c1e98e6 298 $DB->update_record('book_chapters', $ch);
e355240d
PS
299 }
300 }
301}
4171f20d
PS
302
303function book_get_toc($chapters, $chapter, $book, $cm, $edit, $print) {
304 global $USER, $OUTPUT;
305
306 /// included from mod/book/view.php and print.php
307 ///
308 /// uses:
309 /// $chapters - all book chapters
310 /// $chapter - may be false
311 /// $cm - course module
312 /// $book - book
313 /// $edit - force editing view
314
315
316 /// fills:
317 /// $toc
318 /// $title (not for print)
319
320 $currtitle = ''; //active chapter title (plain text)
321 $currsubtitle = ''; //active subchapter if any
322 $prevtitle = '&nbsp;';
323 $toc = ''; //representation of toc (HTML)
324
325 $nch = 0; //chapter number
326 $ns = 0; //subchapter number
327 $first = 1;
328 $titles = array();
329
330 switch ($book->numbering) {
331 case BOOK_NUM_NONE:
332 $toc .= '<div class="book_toc_none">';
333 break;
334 case BOOK_NUM_NUMBERS:
335 $toc .= '<div class="book_toc_numbered">';
336 break;
337 case BOOK_NUM_BULLETS:
338 $toc .= '<div class="book_toc_bullets">';
339 break;
340 case BOOK_NUM_INDENTED:
341 $toc .= '<div class="book_toc_indented">';
342 break;
343 }
344
345
346 if ($print) { ///TOC for printing
347 $toc .= '<a name="toc"></a>';
348 if ($book->customtitles) {
349 $toc .= '<h1>'.get_string('toc', 'book').'</h1>';
350 } else {
351 $toc .= '<p class="book_chapter_title">'.get_string('toc', 'book').'</p>';
352 }
353 $toc .= '<ul>';
354 foreach($chapters as $ch) {
355 $title = trim(strip_tags($ch->title));
356 if (!$ch->hidden) {
357 if (!$ch->subchapter) {
358 $nch++;
359 $ns = 0;
360 $toc .= ($first) ? '<li>' : '</ul></li><li>';
361 if ($book->numbering == BOOK_NUM_NUMBERS) {
362 $title = "$nch $title";
363 }
364 } else {
365 $ns++;
366 $toc .= ($first) ? '<li><ul><li>' : '<li>';
367 if ($book->numbering == BOOK_NUM_NUMBERS) {
368 $title = "$nch.$ns $title";
369 }
370 }
371 $titles[$ch->id] = $title;
372 $toc .= '<a title="'.s($title).'" href="#ch'.$ch->id.'">'.$title.'</a>';
373 $toc .= (!$ch->subchapter) ? '<ul>' : '</li>';
374 $first = 0;
375 }
376 }
377 $toc .= '</ul></li></ul>';
378 } else if ($edit) { ///teacher's TOC
379 $toc .= '<ul>';
380 $i = 0;
381 foreach($chapters as $ch) {
382 $i++;
383 $title = trim(strip_tags($ch->title));
384 if (!$ch->subchapter) {
385 $toc .= ($first) ? '<li>' : '</ul></li><li>';
386 if (!$ch->hidden) {
387 $nch++;
388 $ns = 0;
389 if ($book->numbering == BOOK_NUM_NUMBERS) {
390 $title = "$nch $title";
391 }
392 } else {
393 if ($book->numbering == BOOK_NUM_NUMBERS) {
394 $title = "x $title";
395 }
396 $title = '<span class="dimmed_text">'.$title.'</span>';
397 }
398 $prevtitle = $title;
399 } else {
400 $toc .= ($first) ? '<li><ul><li>' : '<li>';
401 if (!$ch->hidden) {
402 $ns++;
403 if ($book->numbering == BOOK_NUM_NUMBERS) {
404 $title = "$nch.$ns $title";
405 }
406 } else {
407 if ($book->numbering == BOOK_NUM_NUMBERS) {
408 $title = "x.x $title";
409 }
410 $title = '<span class="dimmed_text">'.$title.'</span>';
411 }
412 }
413
414 if ($ch->id == $chapter->id) {
415 $toc .= '<strong>'.$title.'</strong>';
416 if ($ch->subchapter) {
417 $currtitle = $prevtitle;
418 $currsubtitle = $title;
419 } else {
420 $currtitle = $title;
421 $currsubtitle = '&nbsp;';
422 }
423 } else {
424 $toc .= '<a title="'.s($title).'" href="view.php?id='.$cm->id.'&amp;chapterid='.$ch->id.'">'.$title.'</a>';
425 }
426 $toc .= '&nbsp;&nbsp;';
427 if ($i != 1) {
60882692 428 $toc .= ' <a title="'.get_string('up').'" href="move.php?id='.$cm->id.'&amp;chapterid='.$ch->id.'&amp;up=1&amp;sesskey='.$USER->sesskey.'"><img src="'.$OUTPUT->pix_url('t/up').'" class="iconsmall" alt="'.get_string('up').'" /></a>';
4171f20d
PS
429 }
430 if ($i != count($chapters)) {
60882692 431 $toc .= ' <a title="'.get_string('down').'" href="move.php?id='.$cm->id.'&amp;chapterid='.$ch->id.'&amp;up=0&amp;sesskey='.$USER->sesskey.'"><img src="'.$OUTPUT->pix_url('t/down').'" class="iconsmall" alt="'.get_string('down').'" /></a>';
4171f20d 432 }
60882692
PS
433 $toc .= ' <a title="'.get_string('edit').'" href="edit.php?cmid='.$cm->id.'&amp;id='.$ch->id.'"><img src="'.$OUTPUT->pix_url('t/edit').'" class="iconsmall" alt="'.get_string('edit').'" /></a>';
434 $toc .= ' <a title="'.get_string('delete').'" href="delete.php?id='.$cm->id.'&amp;chapterid='.$ch->id.'&amp;sesskey='.$USER->sesskey.'"><img src="'.$OUTPUT->pix_url('t/delete').'" class="iconsmall" alt="'.get_string('delete').'" /></a>';
4171f20d 435 if ($ch->hidden) {
60882692 436 $toc .= ' <a title="'.get_string('show').'" href="show.php?id='.$cm->id.'&amp;chapterid='.$ch->id.'&amp;sesskey='.$USER->sesskey.'"><img src="'.$OUTPUT->pix_url('t/show').'" class="iconsmall" alt="'.get_string('show').'" /></a>';
4171f20d 437 } else {
60882692 438 $toc .= ' <a title="'.get_string('hide').'" href="show.php?id='.$cm->id.'&amp;chapterid='.$ch->id.'&amp;sesskey='.$USER->sesskey.'"><img src="'.$OUTPUT->pix_url('t/hide').'" class="iconsmall" alt="'.get_string('hide').'" /></a>';
4171f20d 439 }
60882692 440 $toc .= ' <a title="'.get_string('addafter', 'book').'" href="edit.php?cmid='.$cm->id.'&amp;pagenum='.$ch->pagenum.'&amp;subchapter='.$ch->subchapter.'"><img src="'.$OUTPUT->pix_url('add', 'mod_book').'" class="iconsmall" alt="'.get_string('addafter', 'book').'" /></a>';
4171f20d
PS
441
442 $toc .= (!$ch->subchapter) ? '<ul>' : '</li>';
443 $first = 0;
444 }
445 $toc .= '</ul></li></ul>';
446 } else { //normal students view
447 $toc .= '<ul>';
448 foreach($chapters as $ch) {
449 $title = trim(strip_tags($ch->title));
450 if (!$ch->hidden) {
451 if (!$ch->subchapter) {
452 $nch++;
453 $ns = 0;
454 $toc .= ($first) ? '<li>' : '</ul></li><li>';
455 if ($book->numbering == BOOK_NUM_NUMBERS) {
456 $title = "$nch $title";
457 }
458 $prevtitle = $title;
459 } else {
460 $ns++;
461 $toc .= ($first) ? '<li><ul><li>' : '<li>';
462 if ($book->numbering == BOOK_NUM_NUMBERS) {
463 $title = "$nch.$ns $title";
464 }
465 }
466 if ($ch->id == $chapter->id) {
467 $toc .= '<strong>'.$title.'</strong>';
468 if ($ch->subchapter) {
469 $currtitle = $prevtitle;
470 $currsubtitle = $title;
471 } else {
472 $currtitle = $title;
473 $currsubtitle = '&nbsp;';
474 }
475 } else {
476 $toc .= '<a title="'.s($title).'" href="view.php?id='.$cm->id.'&amp;chapterid='.$ch->id.'">'.$title.'</a>';
477 }
478 $toc .= (!$ch->subchapter) ? '<ul>' : '</li>';
479 $first = 0;
480 }
481 }
482 $toc .= '</ul></li></ul>';
483 }
484
485 $toc .= '</div>';
486
487 $toc = str_replace('<ul></ul>', '', $toc); //cleanup of invalid structures
488
489 return array($toc, $currtitle, $currsubtitle, $titles);
490}