Merge branch 'MDL-33121' of git://github.com/rwijaya/moodle
[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                         if (empty($chapters[$ch->parent]->hidden)) {
258                             $title = "$nch.x $title";
259                         } else {
260                             $title = "x.x $title";
261                         }
262                     }
263                     $title = html_writer::tag('span', $title, array('class' => 'dimmed_text'));
264                 }
265             }
267             if ($ch->id == $chapter->id) {
268                 $toc .= html_writer::tag('strong', $title);
269             } else {
270                 $toc .= html_writer::link(new moodle_url('view.php', array('id' => $cm->id, 'chapterid' => $ch->id)), $title, array('title' => s($title)));
271             }
272             $toc .=  '&nbsp;&nbsp;';
273             if ($i != 1) {
274                 $toc .= html_writer::link(new moodle_url('move.php', array('id' => $cm->id, 'chapterid' => $ch->id, 'up' => '1', 'sesskey' => $USER->sesskey)),
275                                             $OUTPUT->pix_icon('t/up', get_string('up')), array('title' => get_string('up')));
276             }
277             if ($i != count($chapters)) {
278                 $toc .= html_writer::link(new moodle_url('move.php', array('id' => $cm->id, 'chapterid' => $ch->id, 'up' => '0', 'sesskey' => $USER->sesskey)),
279                                             $OUTPUT->pix_icon('t/down', get_string('down')), array('title' => get_string('down')));
280             }
281             $toc .= html_writer::link(new moodle_url('edit.php', array('cmid' => $cm->id, 'id' => $ch->id)),
282                                         $OUTPUT->pix_icon('t/edit', get_string('edit')), array('title' => get_string('edit')));
283             $toc .= html_writer::link(new moodle_url('delete.php', array('id' => $cm->id, 'chapterid' => $ch->id, 'sesskey' => $USER->sesskey)),
284                                         $OUTPUT->pix_icon('t/delete', get_string('delete')), array('title' => get_string('delete')));
285             if ($ch->hidden) {
286                 $toc .= html_writer::link(new moodle_url('show.php', array('id' => $cm->id, 'chapterid' => $ch->id, 'sesskey' => $USER->sesskey)),
287                                             $OUTPUT->pix_icon('t/show', get_string('show')), array('title' => get_string('show')));
288             } else {
289                 $toc .= html_writer::link(new moodle_url('show.php', array('id' => $cm->id, 'chapterid' => $ch->id, 'sesskey' => $USER->sesskey)),
290                                             $OUTPUT->pix_icon('t/hide', get_string('hide')), array('title' => get_string('hide')));
291             }
292             $toc .= html_writer::link(new moodle_url('edit.php', array('cmid' => $cm->id, 'pagenum' => $ch->pagenum, 'subchapter' => $ch->subchapter)),
293                                             $OUTPUT->pix_icon('add', get_string('addafter', 'mod_book'), 'mod_book'), array('title' => get_string('addafter', 'mod_book')));
296             if (!$ch->subchapter) {
297                 $toc .= html_writer::start_tag('ul');
298             } else {
299                 $toc .= html_writer::end_tag('li');
300             }
301             $first = 0;
302         }
304         $toc .= html_writer::end_tag('ul');
305         $toc .= html_writer::end_tag('li');
306         $toc .= html_writer::end_tag('ul');
308     } else { // Normal students view
309         $toc .= html_writer::start_tag('ul');
310         foreach ($chapters as $ch) {
311             $title = trim(format_string($ch->title, true, array('context'=>$context)));
312             if (!$ch->hidden) {
313                 if (!$ch->subchapter) {
314                     $nch++;
315                     $ns = 0;
317                     if ($first) {
318                         $toc .= html_writer::start_tag('li');
319                     } else {
320                         $toc .= html_writer::end_tag('ul');
321                         $toc .= html_writer::end_tag('li');
322                         $toc .= html_writer::start_tag('li');
323                     }
325                     if ($book->numbering == BOOK_NUM_NUMBERS) {
326                           $title = "$nch $title";
327                     }
328                 } else {
329                     $ns++;
331                     if ($first) {
332                         $toc .= html_writer::start_tag('li');
333                         $toc .= html_writer::start_tag('ul');
334                         $toc .= html_writer::start_tag('li');
335                     } else {
336                         $toc .= html_writer::start_tag('li');
337                     }
339                     if ($book->numbering == BOOK_NUM_NUMBERS) {
340                           $title = "$nch.$ns $title";
341                     }
342                 }
343                 if ($ch->id == $chapter->id) {
344                     $toc .= html_writer::tag('strong', $title);
345                 } else {
346                     $toc .= html_writer::link(new moodle_url('view.php', array('id' => $cm->id, 'chapterid' => $ch->id)), $title, array('title' => s($title)));
347                 }
349                 if (!$ch->subchapter) {
350                     $toc .= html_writer::start_tag('ul');
351                 } else {
352                     $toc .= html_writer::end_tag('li');
353                 }
355                 $first = 0;
356             }
357         }
359         $toc .= html_writer::end_tag('ul');
360         $toc .= html_writer::end_tag('li');
361         $toc .= html_writer::end_tag('ul');
363     }
365     $toc .= html_writer::end_tag('div');
367     $toc = str_replace('<ul></ul>', '', $toc); // Cleanup of invalid structures.
369     return $toc;
373 /**
374  * File browsing support class
375  *
376  * @copyright  2010-2011 Petr Skoda {@link http://skodak.org}
377  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
378  */
379 class book_file_info extends file_info {
380     /** @var stdClass Course object */
381     protected $course;
382     /** @var stdClass Course module object */
383     protected $cm;
384     /** @var array Available file areas */
385     protected $areas;
386     /** @var string File area to browse */
387     protected $filearea;
389     /**
390      * Constructor
391      *
392      * @param file_browser $browser file_browser instance
393      * @param stdClass $course course object
394      * @param stdClass $cm course module object
395      * @param stdClass $context module context
396      * @param array $areas available file areas
397      * @param string $filearea file area to browse
398      */
399     public function __construct($browser, $course, $cm, $context, $areas, $filearea) {
400         parent::__construct($browser, $context);
401         $this->course   = $course;
402         $this->cm       = $cm;
403         $this->areas    = $areas;
404         $this->filearea = $filearea;
405     }
407     /**
408      * Returns list of standard virtual file/directory identification.
409      * The difference from stored_file parameters is that null values
410      * are allowed in all fields
411      * @return array with keys contextid, filearea, itemid, filepath and filename
412      */
413     public function get_params() {
414         return array('contextid'=>$this->context->id,
415                      'component'=>'mod_book',
416                      'filearea' =>$this->filearea,
417                      'itemid'   =>null,
418                      'filepath' =>null,
419                      'filename' =>null);
420     }
422     /**
423      * Returns localised visible name.
424      * @return string
425      */
426     public function get_visible_name() {
427         return $this->areas[$this->filearea];
428     }
430     /**
431      * Can I add new files or directories?
432      * @return bool
433      */
434     public function is_writable() {
435         return false;
436     }
438     /**
439      * Is directory?
440      * @return bool
441      */
442     public function is_directory() {
443         return true;
444     }
446     /**
447      * Returns list of children.
448      * @return array of file_info instances
449      */
450     public function get_children() {
451         global $DB;
453         $children = array();
454         $chapters = $DB->get_records('book_chapters', array('bookid'=>$this->cm->instance), 'pagenum', 'id, pagenum');
455         foreach ($chapters as $itemid => $unused) {
456             if ($child = $this->browser->get_file_info($this->context, 'mod_book', $this->filearea, $itemid)) {
457                 $children[] = $child;
458             }
459         }
460         return $children;
461     }
463     /**
464      * Returns parent file_info instance
465      * @return file_info or null for root
466      */
467     public function get_parent() {
468         return $this->browser->get_file_info($this->context);
469     }