2 // This file is part of Book module for 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 module local lib functions
21 * @copyright 2010-2011 Petr Skoda {@link http://skodak.org}
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25 defined('MOODLE_INTERNAL') || die;
27 require_once(dirname(__FILE__).'/lib.php');
28 require_once($CFG->libdir.'/filelib.php');
31 * The following defines are used to define how the chapters and subchapters of a book should be displayed in that table of contents.
32 * BOOK_NUM_NONE No special styling will applied and the editor will be able to do what ever thay want in the title
33 * BOOK_NUM_NUMBERS Chapters and subchapters are numbered (1, 1.1, 1.2, 2, ...)
34 * BOOK_NUM_BULLETS Subchapters are indented and displayed with bullets
35 * BOOK_NUM_INDENTED Subchapters are indented
37 define('BOOK_NUM_NONE', '0');
38 define('BOOK_NUM_NUMBERS', '1');
39 define('BOOK_NUM_BULLETS', '2');
40 define('BOOK_NUM_INDENTED', '3');
43 * Preload book chapters and fix toc structure if necessary.
45 * Returns array of chapters with standard 'pagenum', 'id, pagenum, subchapter, title, hidden'
46 * and extra 'parent, number, subchapters, prev, next'.
47 * Please note the content/text of chapters is not included.
49 * @param stdClass $book
50 * @return array of id=>chapter
52 function book_preload_chapters($book) {
54 $chapters = $DB->get_records('book_chapters', array('bookid'=>$book->id), 'pagenum', 'id, pagenum, subchapter, title, hidden');
65 $pagenum = 0; // chapter sort
66 $i = 0; // main chapter num
67 $j = 0; // subchapter num
68 foreach ($chapters as $id => $ch) {
71 $ch->pagenum = $pagenum;
73 // book can not start with a subchapter
77 if (!$ch->subchapter) {
81 $chapters[$prev]->next = $ch->id;
84 if ($book->numbering == BOOK_NUM_NUMBERS) {
95 $hidesub = $ch->hidden;
98 $ch->subchapters = array();
100 $ch->prev = $prevsub;
103 $chapters[$prevsub]->next = $ch->id;
105 $ch->parent = $parent;
106 $ch->subchapters = null;
107 $chapters[$parent]->subchapters[$ch->id] = $ch->id;
109 // all subchapters in hidden chapter must be hidden too
113 if ($book->numbering == BOOK_NUM_NUMBERS) {
123 if ($oldch->subchapter != $ch->subchapter or $oldch->pagenum != $ch->pagenum or $oldch->hidden != $ch->hidden) {
124 // update only if something changed
125 $DB->update_record('book_chapters', $ch);
127 $chapters[$id] = $ch;
134 * Returns the title for a given chapter
137 * @param array $chapters
138 * @param stdClass $book
139 * @param context_module $context
142 function book_get_chapter_title($chid, $chapters, $book, $context) {
143 $ch = $chapters[$chid];
144 $title = trim(format_string($ch->title, true, array('context'=>$context)));
146 if ($book->numbering == BOOK_NUM_NUMBERS) {
147 if ($ch->parent and $chapters[$ch->parent]->number) {
148 $numbers[] = $chapters[$ch->parent]->number;
151 $numbers[] = $ch->number;
156 $title = implode('.', $numbers).' '.$title;
163 * General logging to table
164 * @param string $str1
165 * @param string $str2
169 function book_log($str1, $str2, $level = 0) {
172 echo '<tr><td><span class="dimmed_text">'.$str1.'</span></td><td><span class="dimmed_text">'.$str2.'</span></td></tr>';
175 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>';
178 echo '<tr><td>'.$str1.'</class></td><td>'.$str2.'</td></tr>';
184 * Add the book TOC sticky block to the 1st region available
186 * @param array $chapters
187 * @param stdClass $chapter
188 * @param stdClass $book
189 * @param stdClass $cm
192 function book_add_fake_block($chapters, $chapter, $book, $cm, $edit) {
193 global $OUTPUT, $PAGE;
195 $toc = book_get_toc($chapters, $chapter, $book, $cm, $edit, 0);
197 $bc = new block_contents();
198 $bc->title = get_string('toc', 'mod_book');
199 $bc->attributes['class'] = 'block';
202 $regions = $PAGE->blocks->get_regions();
203 $firstregion = reset($regions);
204 $PAGE->blocks->add_fake_block($bc, $firstregion);
208 * Generate toc structure
210 * @param array $chapters
211 * @param stdClass $chapter
212 * @param stdClass $book
213 * @param stdClass $cm
217 function book_get_toc($chapters, $chapter, $book, $cm, $edit) {
218 global $USER, $OUTPUT;
220 $toc = ''; // Representation of toc (HTML)
221 $nch = 0; // Chapter number
222 $ns = 0; // Subchapter number
225 $context = context_module::instance($cm->id);
227 switch ($book->numbering) {
229 $toc .= '<div class="book_toc_none">';
231 case BOOK_NUM_NUMBERS:
232 $toc .= '<div class="book_toc_numbered">';
234 case BOOK_NUM_BULLETS:
235 $toc .= '<div class="book_toc_bullets">';
237 case BOOK_NUM_INDENTED:
238 $toc .= '<div class="book_toc_indented">';
242 if ($edit) { // Teacher's TOC
245 foreach ($chapters as $ch) {
247 $title = trim(format_string($ch->title, true, array('context'=>$context)));
248 if (!$ch->subchapter) {
249 $toc .= ($first) ? '<li>' : '</ul></li><li>';
253 if ($book->numbering == BOOK_NUM_NUMBERS) {
254 $title = "$nch $title";
257 if ($book->numbering == BOOK_NUM_NUMBERS) {
260 $title = '<span class="dimmed_text">'.$title.'</span>';
263 $toc .= ($first) ? '<li><ul><li>' : '<li>';
266 if ($book->numbering == BOOK_NUM_NUMBERS) {
267 $title = "$nch.$ns $title";
270 if ($book->numbering == BOOK_NUM_NUMBERS) {
271 $title = "x.x $title";
273 $title = '<span class="dimmed_text">'.$title.'</span>';
277 if ($ch->id == $chapter->id) {
278 $toc .= '<strong>'.$title.'</strong>';
280 $toc .= '<a title="'.s($title).'" href="view.php?id='.$cm->id.'&chapterid='.$ch->id.'">'.$title.'</a>';
282 $toc .= ' ';
284 $toc .= ' <a title="'.get_string('up').'" href="move.php?id='.$cm->id.'&chapterid='.$ch->id.
285 '&up=1&sesskey='.$USER->sesskey.'"><img src="'.$OUTPUT->pix_url('t/up').'" class="iconsmall" alt="'.get_string('up').'" /></a>';
287 if ($i != count($chapters)) {
288 $toc .= ' <a title="'.get_string('down').'" href="move.php?id='.$cm->id.'&chapterid='.$ch->id.
289 '&up=0&sesskey='.$USER->sesskey.'"><img src="'.$OUTPUT->pix_url('t/down').'" class="iconsmall" alt="'.get_string('down').'" /></a>';
291 $toc .= ' <a title="'.get_string('edit').'" href="edit.php?cmid='.$cm->id.'&id='.$ch->id.'"><img src="'.
292 $OUTPUT->pix_url('t/edit').'" class="iconsmall" alt="'.get_string('edit').'" /></a>';
293 $toc .= ' <a title="'.get_string('delete').'" href="delete.php?id='.$cm->id.'&chapterid='.$ch->id.
294 '&sesskey='.$USER->sesskey.'"><img src="'.$OUTPUT->pix_url('t/delete').'" class="iconsmall" alt="'.get_string('delete').'" /></a>';
296 $toc .= ' <a title="'.get_string('show').'" href="show.php?id='.$cm->id.'&chapterid='.$ch->id.
297 '&sesskey='.$USER->sesskey.'"><img src="'.$OUTPUT->pix_url('t/show').'" class="iconsmall" alt="'.get_string('show').'" /></a>';
299 $toc .= ' <a title="'.get_string('hide').'" href="show.php?id='.$cm->id.'&chapterid='.$ch->id.
300 '&sesskey='.$USER->sesskey.'"><img src="'.$OUTPUT->pix_url('t/hide').'" class="iconsmall" alt="'.get_string('hide').'" /></a>';
302 $toc .= ' <a title="'.get_string('addafter', 'mod_book').'" href="edit.php?cmid='.$cm->id.
303 '&pagenum='.$ch->pagenum.'&subchapter='.$ch->subchapter.'"><img src="'.
304 $OUTPUT->pix_url('add', 'mod_book').'" class="iconsmall" alt="'.get_string('addafter', 'mod_book').'" /></a>';
306 $toc .= (!$ch->subchapter) ? '<ul>' : '</li>';
309 $toc .= '</ul></li></ul>';
310 } else { // Normal students view
312 foreach ($chapters as $ch) {
313 $title = trim(format_string($ch->title, true, array('context'=>$context)));
315 if (!$ch->subchapter) {
318 $toc .= ($first) ? '<li>' : '</ul></li><li>';
319 if ($book->numbering == BOOK_NUM_NUMBERS) {
320 $title = "$nch $title";
324 $toc .= ($first) ? '<li><ul><li>' : '<li>';
325 if ($book->numbering == BOOK_NUM_NUMBERS) {
326 $title = "$nch.$ns $title";
329 if ($ch->id == $chapter->id) {
330 $toc .= '<strong>'.$title.'</strong>';
332 $toc .= '<a title="'.s($title).'" href="view.php?id='.$cm->id.'&chapterid='.$ch->id.'">'.$title.'</a>';
334 $toc .= (!$ch->subchapter) ? '<ul>' : '</li>';
338 $toc .= '</ul></li></ul>';
343 $toc = str_replace('<ul></ul>', '', $toc); // Cleanup of invalid structures.
350 * File browsing support class
352 * @copyright 2010-2011 Petr Skoda {@link http://skodak.org}
353 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
355 class book_file_info extends file_info {
356 /** @var stdClass Course object */
358 /** @var stdClass Course module object */
360 /** @var array Available file areas */
362 /** @var string File area to browse */
368 * @param file_browser $browser file_browser instance
369 * @param stdClass $course course object
370 * @param stdClass $cm course module object
371 * @param stdClass $context module context
372 * @param array $areas available file areas
373 * @param string $filearea file area to browse
375 public function __construct($browser, $course, $cm, $context, $areas, $filearea) {
376 parent::__construct($browser, $context);
377 $this->course = $course;
379 $this->areas = $areas;
380 $this->filearea = $filearea;
384 * Returns list of standard virtual file/directory identification.
385 * The difference from stored_file parameters is that null values
386 * are allowed in all fields
387 * @return array with keys contextid, filearea, itemid, filepath and filename
389 public function get_params() {
390 return array('contextid'=>$this->context->id,
391 'component'=>'mod_book',
392 'filearea' =>$this->filearea,
399 * Returns localised visible name.
402 public function get_visible_name() {
403 return $this->areas[$this->filearea];
407 * Can I add new files or directories?
410 public function is_writable() {
418 public function is_directory() {
423 * Returns list of children.
424 * @return array of file_info instances
426 public function get_children() {
430 $chapters = $DB->get_records('book_chapters', array('bookid'=>$this->cm->instance), 'pagenum', 'id, pagenum');
431 foreach ($chapters as $itemid => $unused) {
432 if ($child = $this->browser->get_file_info($this->context, 'mod_book', $this->filearea, $itemid)) {
433 $children[] = $child;
440 * Returns parent file_info instance
441 * @return file_info or null for root
443 public function get_parent() {
444 return $this->browser->get_file_info($this->context);