8d0d06eaff9582dc9dd923d81e6348ec2abfe774
[moodle.git] / mod / book / locallib.php
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/>.
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($CFG->dirroot.'/mod/book/lib.php');
28 require_once($CFG->libdir.'/filelib.php');
30 define('BOOK_NUM_NONE',     '0');
31 define('BOOK_NUM_NUMBERS',  '1');
32 define('BOOK_NUM_BULLETS',  '2');
33 define('BOOK_NUM_INDENTED', '3');
35 /**
36  * Preload book chapters and fix toc structure if necessary.
37  *
38  * Returns array of chapters with standard 'pagenum', 'id, pagenum, subchapter, title, hidden'
39  * and extra 'parent, number, subchapters, prev, next'.
40  * Please note the content/text of chapters is not included.
41  *
42  * @param  stdClass $book
43  * @return array of id=>chapter
44  */
45 function book_preload_chapters($book) {
46     global $DB;
47     $chapters = $DB->get_records('book_chapters', array('bookid'=>$book->id), 'pagenum', 'id, pagenum, subchapter, title, hidden');
48     if (!$chapters) {
49         return array();
50     }
52     $prev = null;
53     $prevsub = null;
55     $first = true;
56     $hidesub = true;
57     $parent = null;
58     $pagenum = 0; // chapter sort
59     $i = 0;       // main chapter num
60     $j = 0;       // subchapter num
61     foreach($chapters as $id=>$ch) {
62         $oldch = clone($ch);
63         $pagenum++;
64         $ch->pagenum = $pagenum;
65         if ($first) {
66             // book can not start with a subchapter
67             $ch->subchapter = 0;
68             $first = false;
69         }
70         if (!$ch->subchapter) {
71             $ch->prev = $prev;
72             $ch->next = null;
73             if ($prev) {
74                 $chapters[$prev]->next = $ch->id;
75             }
76             if ($ch->hidden) {
77                 if ($book->numbering == BOOK_NUM_NUMBERS) {
78                     $ch->number = 'x';
79                 } else {
80                     $ch->number = null;
81                 }
82             } else {
83                 $i++;
84                 $ch->number = $i;
85             }
86             $j = 0;
87             $prevsub = null;
88             $hidesub = $ch->hidden;
89             $parent = $ch->id;
90             $ch->parent = null;
91             $ch->subchpaters = array();
92         } else {
93             $ch->prev = $prevsub;
94             $ch->next = null;
95             if ($prevsub) {
96                 $chapters[$prevsub]->next = $ch->id;
97             }
98             $ch->parent = $parent;
99             $ch->subchpaters = null;
100             $chapters[$parent]->subchapters[$ch->id] = $ch->id;
101             if ($hidesub) {
102                 // all subchapters in hidden chapter must be hidden too
103                 $ch->hidden = 1;
104             }
105             if ($ch->hidden) {
106                 if ($book->numbering == BOOK_NUM_NUMBERS) {
107                     $ch->number = 'x';
108                 } else {
109                     $ch->number = null;
110                 }
111             } else {
112                 $j++;
113                 $ch->number = $j;
114             }
115         }
116         if ($oldch->subchapter != $ch->subchapter or $oldch->pagenum != $ch->pagenum or $oldch->hidden != $ch->hidden) {
117             // update only if something changed
118             $DB->update_record('book_chapters', $ch);
119         }
120         $chapters[$id] = $ch;
121     }
123     return $chapters;
126 /**
127  * Returns the title for a given chapter
128  *
129  * @param int $chid
130  * @param array $chapters
131  * @param stdClass $book
132  * @param context_module $context
133  * @return string
134  */
135 function book_get_chapter_title($chid, $chapters, $book, $context) {
136     $ch = $chapters[$chid];
137     $title = trim(format_string($ch->title, true, array('context'=>$context)));
138     $numbers = array();
139     if ($book->numbering == BOOK_NUM_NUMBERS) {
140         if ($ch->parent and $chapters[$ch->parent]->number) {
141             $numbers[] = $chapters[$ch->parent]->number;
142         }
143         if ($ch->number) {
144             $numbers[] = $ch->number;
145         }
146     }
148     if ($numbers) {
149         $title = implode('.', $numbers).' '.$title;
150     }
152     return $title;
155 /**
156  * General logging to table
157  * @param string $str1
158  * @param string $str2
159  * @param int $level
160  * @return void
161  */
162 function book_log($str1, $str2, $level = 0) {
163     switch ($level) {
164         case 1:
165             echo '<tr><td><span class="dimmed_text">'.$str1.'</span></td><td><span class="dimmed_text">'.$str2.'</span></td></tr>';
166             break;
167         case 2:
168             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>';
169             break;
170         default:
171             echo '<tr><td>'.$str1.'</class></td><td>'.$str2.'</td></tr>';
172             break;
173     }
176 /**
177  * Add the book TOC sticky block to the 1st region available
178  *
179  * @param array $chapters
180  * @param stdClass $chapter
181  * @param stdClass $book
182  * @param stdClass $cm
183  * @param bool $edit
184  */
185 function book_add_fake_block($chapters, $chapter, $book, $cm, $edit) {
186     global $OUTPUT, $PAGE;
188     $toc = book_get_toc($chapters, $chapter, $book, $cm, $edit, 0);
190     if ($edit) {
191         $toc .= '<div class="book_faq">';
192         $toc .=  $OUTPUT->help_icon('faq', 'mod_book', get_string('faq', 'mod_book'));
193         $toc .=  '</div>';
194     }
196     $bc = new block_contents();
197     $bc->title = get_string('toc', 'mod_book');
198     $bc->attributes['class'] = 'block';
199     $bc->content = $toc;
201     $regions = $PAGE->blocks->get_regions();
202     $firstregion = reset($regions);
203     $PAGE->blocks->add_fake_block($bc, $firstregion);
206 /**
207  * Generate toc structure
208  *
209  * @param array $chapters
210  * @param stdClass $chapter
211  * @param stdClass $book
212  * @param stdClass $cm
213  * @param bool $edit
214  * @return string
215  */
216 function book_get_toc($chapters, $chapter, $book, $cm, $edit) {
217     global $USER, $OUTPUT;
219     $toc = '';  // Representation of toc (HTML)
220     $nch = 0;   // Chapter number
221     $ns = 0;    // Subchapter number
222     $first = 1;
224     $context = context_module::instance($cm->id);
226     switch ($book->numbering) {
227       case BOOK_NUM_NONE:
228           $toc .= '<div class="book_toc_none">';
229           break;
230       case BOOK_NUM_NUMBERS:
231           $toc .= '<div class="book_toc_numbered">';
232           break;
233       case BOOK_NUM_BULLETS:
234           $toc .= '<div class="book_toc_bullets">';
235           break;
236       case BOOK_NUM_INDENTED:
237           $toc .= '<div class="book_toc_indented">';
238           break;
239     }
242     if ($edit) { // Teacher's TOC
243         $toc .= '<ul>';
244         $i = 0;
245         foreach($chapters as $ch) {
246             $i++;
247             $title = trim(format_string($ch->title, true, array('context'=>$context)));
248             if (!$ch->subchapter) {
249                 $toc .= ($first) ? '<li>' : '</ul></li><li>';
250                 if (!$ch->hidden) {
251                     $nch++;
252                     $ns = 0;
253                     if ($book->numbering == BOOK_NUM_NUMBERS) {
254                         $title = "$nch $title";
255                     }
256                 } else {
257                     if ($book->numbering == BOOK_NUM_NUMBERS) {
258                         $title = "x $title";
259                     }
260                     $title = '<span class="dimmed_text">'.$title.'</span>';
261                 }
262             } else {
263                 $toc .= ($first) ? '<li><ul><li>' : '<li>';
264                 if (!$ch->hidden) {
265                     $ns++;
266                     if ($book->numbering == BOOK_NUM_NUMBERS) {
267                         $title = "$nch.$ns $title";
268                     }
269                 } else {
270                     if ($book->numbering == BOOK_NUM_NUMBERS) {
271                         $title = "x.x $title";
272                     }
273                     $title = '<span class="dimmed_text">'.$title.'</span>';
274                 }
275             }
277             if ($ch->id == $chapter->id) {
278                 $toc .= '<strong>'.$title.'</strong>';
279             } else {
280                 $toc .= '<a title="'.s($title).'" href="view.php?id='.$cm->id.'&amp;chapterid='.$ch->id.'">'.$title.'</a>';
281             }
282             $toc .=  '&nbsp;&nbsp;';
283             if ($i != 1) {
284                 $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>';
285             }
286             if ($i != count($chapters)) {
287                 $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>';
288             }
289             $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>';
290             $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>';
291             if ($ch->hidden) {
292                 $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>';
293             } else {
294                 $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>';
295             }
296             $toc .= ' <a title="'.get_string('addafter', 'mod_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', 'mod_book').'" /></a>';
298             $toc .= (!$ch->subchapter) ? '<ul>' : '</li>';
299             $first = 0;
300         }
301         $toc .= '</ul></li></ul>';
302     } else { // Normal students view
303         $toc .= '<ul>';
304         foreach($chapters as $ch) {
305             $title = trim(format_string($ch->title, true, array('context'=>$context)));
306             if (!$ch->hidden) {
307                 if (!$ch->subchapter) {
308                     $nch++;
309                     $ns = 0;
310                     $toc .= ($first) ? '<li>' : '</ul></li><li>';
311                     if ($book->numbering == BOOK_NUM_NUMBERS) {
312                           $title = "$nch $title";
313                     }
314                 } else {
315                     $ns++;
316                     $toc .= ($first) ? '<li><ul><li>' : '<li>';
317                     if ($book->numbering == BOOK_NUM_NUMBERS) {
318                           $title = "$nch.$ns $title";
319                     }
320                 }
321                 if ($ch->id == $chapter->id) {
322                     $toc .= '<strong>'.$title.'</strong>';
323                 } else {
324                     $toc .= '<a title="'.s($title).'" href="view.php?id='.$cm->id.'&amp;chapterid='.$ch->id.'">'.$title.'</a>';
325                 }
326                 $toc .= (!$ch->subchapter) ? '<ul>' : '</li>';
327                 $first = 0;
328             }
329         }
330         $toc .= '</ul></li></ul>';
331     }
333     $toc .= '</div>';
335     $toc = str_replace('<ul></ul>', '', $toc); // Cleanup of invalid structures.
337     return $toc;
341 /**
342  * File browsing support class
343  *
344  * @copyright  2010-2011 Petr Skoda {@link http://skodak.org}
345  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
346  */
347 class book_file_info extends file_info {
348     /** @var stdClass Course object */
349     protected $course;
350     /** @var stdClass Course module object */
351     protected $cm;
352     /** @var array Available file areas */
353     protected $areas;
354     /** @var string File area to browse */
355     protected $filearea;
357     /**
358      * Constructor
359      *
360      * @param file_browser $browser file_browser instance
361      * @param stdClass $course course object
362      * @param stdClass $cm course module object
363      * @param stdClass $context module context
364      * @param array $areas available file areas
365      * @param string $filearea file area to browse
366      */
367     public function __construct($browser, $course, $cm, $context, $areas, $filearea) {
368         parent::__construct($browser, $context);
369         $this->course   = $course;
370         $this->cm       = $cm;
371         $this->areas    = $areas;
372         $this->filearea = $filearea;
373     }
375     /**
376      * Returns list of standard virtual file/directory identification.
377      * The difference from stored_file parameters is that null values
378      * are allowed in all fields
379      * @return array with keys contextid, filearea, itemid, filepath and filename
380      */
381     public function get_params() {
382         return array('contextid'=>$this->context->id,
383                      'component'=>'mod_book',
384                      'filearea' =>$this->filearea,
385                      'itemid'   =>null,
386                      'filepath' =>null,
387                      'filename' =>null);
388     }
390     /**
391      * Returns localised visible name.
392      * @return string
393      */
394     public function get_visible_name() {
395         return $this->areas[$this->filearea];
396     }
398     /**
399      * Can I add new files or directories?
400      * @return bool
401      */
402     public function is_writable() {
403         return false;
404     }
406     /**
407      * Is directory?
408      * @return bool
409      */
410     public function is_directory() {
411         return true;
412     }
414     /**
415      * Returns list of children.
416      * @return array of file_info instances
417      */
418     public function get_children() {
419         global $DB;
421         $children = array();
422         $chapters = $DB->get_records('book_chapters', array('bookid'=>$this->cm->instance), 'pagenum', 'id, pagenum');
423         foreach ($chapters as $itemid=>$unused) {
424             if ($child = $this->browser->get_file_info($this->context, 'mod_book', $this->filearea, $itemid)) {
425                 $children[] = $child;
426             }
427         }
428         return $children;
429     }
431     /**
432      * Returns parent file_info instance
433      * @return file_info or null for root
434      */
435     public function get_parent() {
436         return $this->browser->get_file_info($this->context);
437     }