MDL-33121 Book Module: removing prev/next value from book_preload_chapters and more...
[moodle.git] / mod / book / locallib.php
1 <?php
2 // This file is part of 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/>.
17 /**
18  * Book module local lib functions
19  *
20  * @package    mod_book
21  * @copyright  2010-2011 Petr Skoda {@link http://skodak.org}
22  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23  */
25 defined('MOODLE_INTERNAL') || die;
27 require_once(dirname(__FILE__).'/lib.php');
28 require_once($CFG->libdir.'/filelib.php');
30 /**
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
36  */
37 define('BOOK_NUM_NONE',     '0');
38 define('BOOK_NUM_NUMBERS',  '1');
39 define('BOOK_NUM_BULLETS',  '2');
40 define('BOOK_NUM_INDENTED', '3');
42 /**
43  * Preload book chapters and fix toc structure if necessary.
44  *
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.
48  *
49  * @param  stdClass $book
50  * @return array of id=>chapter
51  */
52 function book_preload_chapters($book) {
53     global $DB;
54     $chapters = $DB->get_records('book_chapters', array('bookid'=>$book->id), 'pagenum', 'id, pagenum, subchapter, title, hidden');
55     if (!$chapters) {
56         return array();
57     }
59     $prev = null;
60     $prevsub = null;
62     $first = true;
63     $hidesub = true;
64     $parent = null;
65     $pagenum = 0; // chapter sort
66     $i = 0;       // main chapter num
67     $j = 0;       // subchapter num
68     foreach ($chapters as $id => $ch) {
69         $oldch = clone($ch);
70         $pagenum++;
71         $ch->pagenum = $pagenum;
72         if ($first) {
73             // book can not start with a subchapter
74             $ch->subchapter = 0;
75             $first = false;
76         }
77         if (!$ch->subchapter) {
78             if ($ch->hidden) {
79                 if ($book->numbering == BOOK_NUM_NUMBERS) {
80                     $ch->number = 'x';
81                 } else {
82                     $ch->number = null;
83                 }
84             } else {
85                 $i++;
86                 $ch->number = $i;
87             }
88             $j = 0;
89             $prevsub = null;
90             $hidesub = $ch->hidden;
91             $parent = $ch->id;
92             $ch->parent = null;
93             $ch->subchapters = array();
94         } else {
95             $ch->parent = $parent;
96             $ch->subchapters = null;
97             $chapters[$parent]->subchapters[$ch->id] = $ch->id;
98             if ($hidesub) {
99                 // all subchapters in hidden chapter must be hidden too
100                 $ch->hidden = 1;
101             }
102             if ($ch->hidden) {
103                 if ($book->numbering == BOOK_NUM_NUMBERS) {
104                     $ch->number = 'x';
105                 } else {
106                     $ch->number = null;
107                 }
108             } else {
109                 $j++;
110                 $ch->number = $j;
111             }
112         }
114         if ($oldch->subchapter != $ch->subchapter or $oldch->pagenum != $ch->pagenum or $oldch->hidden != $ch->hidden) {
115             // update only if something changed
116             $DB->update_record('book_chapters', $ch);
117         }
118         $chapters[$id] = $ch;
119     }
121     return $chapters;
124 /**
125  * Returns the title for a given chapter
126  *
127  * @param int $chid
128  * @param array $chapters
129  * @param stdClass $book
130  * @param context_module $context
131  * @return string
132  */
133 function book_get_chapter_title($chid, $chapters, $book, $context) {
134     $ch = $chapters[$chid];
135     $title = trim(format_string($ch->title, true, array('context'=>$context)));
136     $numbers = array();
137     if ($book->numbering == BOOK_NUM_NUMBERS) {
138         if ($ch->parent and $chapters[$ch->parent]->number) {
139             $numbers[] = $chapters[$ch->parent]->number;
140         }
141         if ($ch->number) {
142             $numbers[] = $ch->number;
143         }
144     }
146     if ($numbers) {
147         $title = implode('.', $numbers).' '.$title;
148     }
150     return $title;
153 /**
154  * Add the book TOC sticky block to the 1st region available
155  *
156  * @param array $chapters
157  * @param stdClass $chapter
158  * @param stdClass $book
159  * @param stdClass $cm
160  * @param bool $edit
161  */
162 function book_add_fake_block($chapters, $chapter, $book, $cm, $edit) {
163     global $OUTPUT, $PAGE;
165     $toc = book_get_toc($chapters, $chapter, $book, $cm, $edit, 0);
167     $bc = new block_contents();
168     $bc->title = get_string('toc', 'mod_book');
169     $bc->attributes['class'] = 'block';
170     $bc->content = $toc;
172     $regions = $PAGE->blocks->get_regions();
173     $firstregion = reset($regions);
174     $PAGE->blocks->add_fake_block($bc, $firstregion);
177 /**
178  * Generate toc structure
179  *
180  * @param array $chapters
181  * @param stdClass $chapter
182  * @param stdClass $book
183  * @param stdClass $cm
184  * @param bool $edit
185  * @return string
186  */
187 function book_get_toc($chapters, $chapter, $book, $cm, $edit) {
188     global $USER, $OUTPUT;
190     $toc = '';
191     $nch = 0;   // Chapter number
192     $ns = 0;    // Subchapter number
193     $first = 1;
195     $context = context_module::instance($cm->id);
197     switch ($book->numbering) {
198         case BOOK_NUM_NONE:
199             $toc .= html_writer::start_tag('div', array('class' => 'book_toc_none'));
200             break;
201         case BOOK_NUM_NUMBERS:
202             $toc .= html_writer::start_tag('div', array('class' => 'book_toc_numbered'));
203             break;
204         case BOOK_NUM_BULLETS:
205             $toc .= html_writer::start_tag('div', array('class' => 'book_toc_bullets'));
206             break;
207         case BOOK_NUM_INDENTED:
208             $toc .= html_writer::start_tag('div', array('class' => 'book_toc_indented'));
209             break;
210     }
212     if ($edit) { // Teacher's TOC
213         $toc .= html_writer::start_tag('ul');
214         $i = 0;
215         foreach ($chapters as $ch) {
216             $i++;
217             $title = trim(format_string($ch->title, true, array('context'=>$context)));
218             if (!$ch->subchapter) {
220                 if ($first) {
221                     $toc .= html_writer::start_tag('li');
222                 } else {
223                     $toc .= html_writer::end_tag('ul');
224                     $toc .= html_writer::end_tag('li');
225                     $toc .= html_writer::start_tag('li');
226                 }
228                 if (!$ch->hidden) {
229                     $nch++;
230                     $ns = 0;
231                     if ($book->numbering == BOOK_NUM_NUMBERS) {
232                         $title = "$nch $title";
233                     }
234                 } else {
235                     if ($book->numbering == BOOK_NUM_NUMBERS) {
236                         $title = "x $title";
237                     }
238                     $title = html_writer::tag('span', $title, array('class' => 'dimmed_text'));
239                 }
240             } else {
242                 if ($first) {
243                     $toc .= html_writer::start_tag('li');
244                     $toc .= html_writer::start_tag('ul');
245                     $toc .= html_writer::start_tag('li');
246                 } else {
247                     $toc .= html_writer::start_tag('li');
248                 }
250                 if (!$ch->hidden) {
251                     $ns++;
252                     if ($book->numbering == BOOK_NUM_NUMBERS) {
253                         $title = "$nch.$ns $title";
254                     }
255                 } else {
256                     if ($book->numbering == BOOK_NUM_NUMBERS) {
257                         $title = "x.x $title";
258                     }
259                     $title = html_writer::tag('span', $title, array('class' => 'dimmed_text'));
260                 }
261             }
263             if ($ch->id == $chapter->id) {
264                 $toc .= html_writer::tag('strong', $title);
265             } else {
266                 $toc .= html_writer::link(new moodle_url('view.php', array('id' => $cm->id, 'chapterid' => $ch->id)), $title, array('title' => s($title)));
267             }
268             $toc .=  '&nbsp;&nbsp;';
269             if ($i != 1) {
270                 $toc .= html_writer::link(new moodle_url('move.php', array('id' => $cm->id, 'chapterid' => $ch->id, 'up' => '1', 'sesskey' => $USER->sesskey)),
271                                             $OUTPUT->pix_icon('t/up', get_string('up')), array('title' => get_string('up')));
272             }
273             if ($i != count($chapters)) {
274                 $toc .= html_writer::link(new moodle_url('move.php', array('id' => $cm->id, 'chapterid' => $ch->id, 'up' => '0', 'sesskey' => $USER->sesskey)),
275                                             $OUTPUT->pix_icon('t/down', get_string('down')), array('title' => get_string('down')));
276             }
277             $toc .= html_writer::link(new moodle_url('edit.php', array('cmid' => $cm->id, 'id' => $ch->id)),
278                                         $OUTPUT->pix_icon('t/edit', get_string('edit')), array('title' => get_string('edit')));
279             $toc .= html_writer::link(new moodle_url('delete.php', array('id' => $cm->id, 'chapterid' => $ch->id, 'sesskey' => $USER->sesskey)),
280                                         $OUTPUT->pix_icon('t/delete', get_string('delete')), array('title' => get_string('delete')));
281             if ($ch->hidden) {
282                 $toc .= html_writer::link(new moodle_url('show.php', array('id' => $cm->id, 'chapterid' => $ch->id, 'sesskey' => $USER->sesskey)),
283                                             $OUTPUT->pix_icon('t/show', get_string('show')), array('title' => get_string('show')));
284             } else {
285                 $toc .= html_writer::link(new moodle_url('show.php', array('id' => $cm->id, 'chapterid' => $ch->id, 'sesskey' => $USER->sesskey)),
286                                             $OUTPUT->pix_icon('t/hide', get_string('hide')), array('title' => get_string('hide')));
287             }
288             $toc .= html_writer::link(new moodle_url('edit.php', array('cmid' => $cm->id, 'pagenum' => $ch->pagenum, 'subchapter' => $ch->subchapter)),
289                                             $OUTPUT->pix_icon('add', get_string('addafter', 'mod_book'), 'mod_book'), array('title' => get_string('addafter', 'mod_book')));
292             if (!$ch->subchapter) {
293                 $toc .= html_writer::start_tag('ul');
294             } else {
295                 $toc .= html_writer::end_tag('li');
296             }
297             $first = 0;
298         }
300         $toc .= html_writer::end_tag('ul');
301         $toc .= html_writer::end_tag('li');
302         $toc .= html_writer::end_tag('ul');
304     } else { // Normal students view
305         $toc .= html_writer::start_tag('ul');
306         foreach ($chapters as $ch) {
307             $title = trim(format_string($ch->title, true, array('context'=>$context)));
308             if (!$ch->hidden) {
309                 if (!$ch->subchapter) {
310                     $nch++;
311                     $ns = 0;
313                     if ($first) {
314                         $toc .= html_writer::start_tag('li');
315                     } else {
316                         $toc .= html_writer::end_tag('ul');
317                         $toc .= html_writer::end_tag('li');
318                         $toc .= html_writer::start_tag('li');
319                     }
321                     if ($book->numbering == BOOK_NUM_NUMBERS) {
322                           $title = "$nch $title";
323                     }
324                 } else {
325                     $ns++;
327                     if ($first) {
328                         $toc .= html_writer::start_tag('li');
329                         $toc .= html_writer::start_tag('ul');
330                         $toc .= html_writer::start_tag('li');
331                     } else {
332                         $toc .= html_writer::start_tag('li');
333                     }
335                     if ($book->numbering == BOOK_NUM_NUMBERS) {
336                           $title = "$nch.$ns $title";
337                     }
338                 }
339                 if ($ch->id == $chapter->id) {
340                     $toc .= html_writer::tag('strong', $title);
341                 } else {
342                     $toc .= html_writer::link(new moodle_url('view.php', array('id' => $cm->id, 'chapterid' => $ch->id)), $title, array('title' => s($title)));
343                 }
345                 if (!$ch->subchapter) {
346                     $toc .= html_writer::start_tag('ul');
347                 } else {
348                     $toc .= html_writer::end_tag('li');
349                 }
351                 $first = 0;
352             }
353         }
355         $toc .= html_writer::end_tag('ul');
356         $toc .= html_writer::end_tag('li');
357         $toc .= html_writer::end_tag('ul');
359     }
361     $toc .= html_writer::end_tag('div');
363     $toc = str_replace('<ul></ul>', '', $toc); // Cleanup of invalid structures.
365     return $toc;
369 /**
370  * File browsing support class
371  *
372  * @copyright  2010-2011 Petr Skoda {@link http://skodak.org}
373  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
374  */
375 class book_file_info extends file_info {
376     /** @var stdClass Course object */
377     protected $course;
378     /** @var stdClass Course module object */
379     protected $cm;
380     /** @var array Available file areas */
381     protected $areas;
382     /** @var string File area to browse */
383     protected $filearea;
385     /**
386      * Constructor
387      *
388      * @param file_browser $browser file_browser instance
389      * @param stdClass $course course object
390      * @param stdClass $cm course module object
391      * @param stdClass $context module context
392      * @param array $areas available file areas
393      * @param string $filearea file area to browse
394      */
395     public function __construct($browser, $course, $cm, $context, $areas, $filearea) {
396         parent::__construct($browser, $context);
397         $this->course   = $course;
398         $this->cm       = $cm;
399         $this->areas    = $areas;
400         $this->filearea = $filearea;
401     }
403     /**
404      * Returns list of standard virtual file/directory identification.
405      * The difference from stored_file parameters is that null values
406      * are allowed in all fields
407      * @return array with keys contextid, filearea, itemid, filepath and filename
408      */
409     public function get_params() {
410         return array('contextid'=>$this->context->id,
411                      'component'=>'mod_book',
412                      'filearea' =>$this->filearea,
413                      'itemid'   =>null,
414                      'filepath' =>null,
415                      'filename' =>null);
416     }
418     /**
419      * Returns localised visible name.
420      * @return string
421      */
422     public function get_visible_name() {
423         return $this->areas[$this->filearea];
424     }
426     /**
427      * Can I add new files or directories?
428      * @return bool
429      */
430     public function is_writable() {
431         return false;
432     }
434     /**
435      * Is directory?
436      * @return bool
437      */
438     public function is_directory() {
439         return true;
440     }
442     /**
443      * Returns list of children.
444      * @return array of file_info instances
445      */
446     public function get_children() {
447         global $DB;
449         $children = array();
450         $chapters = $DB->get_records('book_chapters', array('bookid'=>$this->cm->instance), 'pagenum', 'id, pagenum');
451         foreach ($chapters as $itemid => $unused) {
452             if ($child = $this->browser->get_file_info($this->context, 'mod_book', $this->filearea, $itemid)) {
453                 $children[] = $child;
454             }
455         }
456         return $children;
457     }
459     /**
460      * Returns parent file_info instance
461      * @return file_info or null for root
462      */
463     public function get_parent() {
464         return $this->browser->get_file_info($this->context);
465     }