MDL-33120 book: changes in code based on amended strings
[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(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             $ch->prev = $prev;
79             $ch->next = null;
80             if ($prev) {
81                 $chapters[$prev]->next = $ch->id;
82             }
83             if ($ch->hidden) {
84                 if ($book->numbering == BOOK_NUM_NUMBERS) {
85                     $ch->number = 'x';
86                 } else {
87                     $ch->number = null;
88                 }
89             } else {
90                 $i++;
91                 $ch->number = $i;
92             }
93             $j = 0;
94             $prevsub = null;
95             $hidesub = $ch->hidden;
96             $parent = $ch->id;
97             $ch->parent = null;
98             $ch->subchapters = array();
99         } else {
100             $ch->prev = $prevsub;
101             $ch->next = null;
102             if ($prevsub) {
103                 $chapters[$prevsub]->next = $ch->id;
104             }
105             $ch->parent = $parent;
106             $ch->subchapters = null;
107             $chapters[$parent]->subchapters[$ch->id] = $ch->id;
108             if ($hidesub) {
109                 // all subchapters in hidden chapter must be hidden too
110                 $ch->hidden = 1;
111             }
112             if ($ch->hidden) {
113                 if ($book->numbering == BOOK_NUM_NUMBERS) {
114                     $ch->number = 'x';
115                 } else {
116                     $ch->number = null;
117                 }
118             } else {
119                 $j++;
120                 $ch->number = $j;
121             }
122         }
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);
126         }
127         $chapters[$id] = $ch;
128     }
130     return $chapters;
133 /**
134  * Returns the title for a given chapter
135  *
136  * @param int $chid
137  * @param array $chapters
138  * @param stdClass $book
139  * @param context_module $context
140  * @return string
141  */
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)));
145     $numbers = array();
146     if ($book->numbering == BOOK_NUM_NUMBERS) {
147         if ($ch->parent and $chapters[$ch->parent]->number) {
148             $numbers[] = $chapters[$ch->parent]->number;
149         }
150         if ($ch->number) {
151             $numbers[] = $ch->number;
152         }
153     }
155     if ($numbers) {
156         $title = implode('.', $numbers).' '.$title;
157     }
159     return $title;
162 /**
163  * General logging to table
164  * @param string $str1
165  * @param string $str2
166  * @param int $level
167  * @return void
168  */
169 function book_log($str1, $str2, $level = 0) {
170     switch ($level) {
171         case 1:
172             echo '<tr><td><span class="dimmed_text">'.$str1.'</span></td><td><span class="dimmed_text">'.$str2.'</span></td></tr>';
173             break;
174         case 2:
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>';
176             break;
177         default:
178             echo '<tr><td>'.$str1.'</class></td><td>'.$str2.'</td></tr>';
179             break;
180     }
183 /**
184  * Add the book TOC sticky block to the 1st region available
185  *
186  * @param array $chapters
187  * @param stdClass $chapter
188  * @param stdClass $book
189  * @param stdClass $cm
190  * @param bool $edit
191  */
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';
200     $bc->content = $toc;
202     $regions = $PAGE->blocks->get_regions();
203     $firstregion = reset($regions);
204     $PAGE->blocks->add_fake_block($bc, $firstregion);
207 /**
208  * Generate toc structure
209  *
210  * @param array $chapters
211  * @param stdClass $chapter
212  * @param stdClass $book
213  * @param stdClass $cm
214  * @param bool $edit
215  * @return string
216  */
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
223     $first = 1;
225     $context = context_module::instance($cm->id);
227     switch ($book->numbering) {
228         case BOOK_NUM_NONE:
229             $toc .= '<div class="book_toc_none">';
230             break;
231         case BOOK_NUM_NUMBERS:
232             $toc .= '<div class="book_toc_numbered">';
233             break;
234         case BOOK_NUM_BULLETS:
235             $toc .= '<div class="book_toc_bullets">';
236             break;
237         case BOOK_NUM_INDENTED:
238             $toc .= '<div class="book_toc_indented">';
239             break;
240     }
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.
285                         '&amp;up=1&amp;sesskey='.$USER->sesskey.'"><img src="'.$OUTPUT->pix_url('t/up').'" class="iconsmall" alt="'.get_string('up').'" /></a>';
286             }
287             if ($i != count($chapters)) {
288                 $toc .=  ' <a title="'.get_string('down').'" href="move.php?id='.$cm->id.'&amp;chapterid='.$ch->id.
289                         '&amp;up=0&amp;sesskey='.$USER->sesskey.'"><img src="'.$OUTPUT->pix_url('t/down').'" class="iconsmall" alt="'.get_string('down').'" /></a>';
290             }
291             $toc .=  ' <a title="'.get_string('edit').'" href="edit.php?cmid='.$cm->id.'&amp;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.'&amp;chapterid='.$ch->id.
294                     '&amp;sesskey='.$USER->sesskey.'"><img src="'.$OUTPUT->pix_url('t/delete').'" class="iconsmall" alt="'.get_string('delete').'" /></a>';
295             if ($ch->hidden) {
296                 $toc .= ' <a title="'.get_string('show').'" href="show.php?id='.$cm->id.'&amp;chapterid='.$ch->id.
297                         '&amp;sesskey='.$USER->sesskey.'"><img src="'.$OUTPUT->pix_url('t/show').'" class="iconsmall" alt="'.get_string('show').'" /></a>';
298             } else {
299                 $toc .= ' <a title="'.get_string('hide').'" href="show.php?id='.$cm->id.'&amp;chapterid='.$ch->id.
300                         '&amp;sesskey='.$USER->sesskey.'"><img src="'.$OUTPUT->pix_url('t/hide').'" class="iconsmall" alt="'.get_string('hide').'" /></a>';
301             }
302             $toc .= ' <a title="'.get_string('addafter', 'mod_book').'" href="edit.php?cmid='.$cm->id.
303                     '&amp;pagenum='.$ch->pagenum.'&amp;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>';
307             $first = 0;
308         }
309         $toc .= '</ul></li></ul>';
310     } else { // Normal students view
311         $toc .= '<ul>';
312         foreach ($chapters as $ch) {
313             $title = trim(format_string($ch->title, true, array('context'=>$context)));
314             if (!$ch->hidden) {
315                 if (!$ch->subchapter) {
316                     $nch++;
317                     $ns = 0;
318                     $toc .= ($first) ? '<li>' : '</ul></li><li>';
319                     if ($book->numbering == BOOK_NUM_NUMBERS) {
320                           $title = "$nch $title";
321                     }
322                 } else {
323                     $ns++;
324                     $toc .= ($first) ? '<li><ul><li>' : '<li>';
325                     if ($book->numbering == BOOK_NUM_NUMBERS) {
326                           $title = "$nch.$ns $title";
327                     }
328                 }
329                 if ($ch->id == $chapter->id) {
330                     $toc .= '<strong>'.$title.'</strong>';
331                 } else {
332                     $toc .= '<a title="'.s($title).'" href="view.php?id='.$cm->id.'&amp;chapterid='.$ch->id.'">'.$title.'</a>';
333                 }
334                 $toc .= (!$ch->subchapter) ? '<ul>' : '</li>';
335                 $first = 0;
336             }
337         }
338         $toc .= '</ul></li></ul>';
339     }
341     $toc .= '</div>';
343     $toc = str_replace('<ul></ul>', '', $toc); // Cleanup of invalid structures.
345     return $toc;
349 /**
350  * File browsing support class
351  *
352  * @copyright  2010-2011 Petr Skoda {@link http://skodak.org}
353  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
354  */
355 class book_file_info extends file_info {
356     /** @var stdClass Course object */
357     protected $course;
358     /** @var stdClass Course module object */
359     protected $cm;
360     /** @var array Available file areas */
361     protected $areas;
362     /** @var string File area to browse */
363     protected $filearea;
365     /**
366      * Constructor
367      *
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
374      */
375     public function __construct($browser, $course, $cm, $context, $areas, $filearea) {
376         parent::__construct($browser, $context);
377         $this->course   = $course;
378         $this->cm       = $cm;
379         $this->areas    = $areas;
380         $this->filearea = $filearea;
381     }
383     /**
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
388      */
389     public function get_params() {
390         return array('contextid'=>$this->context->id,
391                      'component'=>'mod_book',
392                      'filearea' =>$this->filearea,
393                      'itemid'   =>null,
394                      'filepath' =>null,
395                      'filename' =>null);
396     }
398     /**
399      * Returns localised visible name.
400      * @return string
401      */
402     public function get_visible_name() {
403         return $this->areas[$this->filearea];
404     }
406     /**
407      * Can I add new files or directories?
408      * @return bool
409      */
410     public function is_writable() {
411         return false;
412     }
414     /**
415      * Is directory?
416      * @return bool
417      */
418     public function is_directory() {
419         return true;
420     }
422     /**
423      * Returns list of children.
424      * @return array of file_info instances
425      */
426     public function get_children() {
427         global $DB;
429         $children = array();
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;
434             }
435         }
436         return $children;
437     }
439     /**
440      * Returns parent file_info instance
441      * @return file_info or null for root
442      */
443     public function get_parent() {
444         return $this->browser->get_file_info($this->context);
445     }