MDL-51423 clilib: Introduce cli_write() and cli_writeln() functions
[moodle.git] / mod / book / lib.php
blob41dddfed4a51e750a8e1c8c8e1da197d8fcb207b
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.
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 core interaction API
20 * @package mod_book
21 * @copyright 2004-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 /**
28 * Returns list of available numbering types
29 * @return array
31 function book_get_numbering_types() {
32 global $CFG; // required for the include
34 require_once(dirname(__FILE__).'/locallib.php');
36 return array (
37 BOOK_NUM_NONE => get_string('numbering0', 'mod_book'),
38 BOOK_NUM_NUMBERS => get_string('numbering1', 'mod_book'),
39 BOOK_NUM_BULLETS => get_string('numbering2', 'mod_book'),
40 BOOK_NUM_INDENTED => get_string('numbering3', 'mod_book')
44 /**
45 * Returns list of available navigation link types.
46 * @return array
48 function book_get_nav_types() {
49 require_once(dirname(__FILE__).'/locallib.php');
51 return array (
52 BOOK_LINK_TOCONLY => get_string('navtoc', 'mod_book'),
53 BOOK_LINK_IMAGE => get_string('navimages', 'mod_book'),
54 BOOK_LINK_TEXT => get_string('navtext', 'mod_book'),
58 /**
59 * Returns list of available navigation link CSS classes.
60 * @return array
62 function book_get_nav_classes() {
63 return array ('navtoc', 'navimages', 'navtext');
66 /**
67 * Returns all other caps used in module
68 * @return array
70 function book_get_extra_capabilities() {
71 // used for group-members-only
72 return array('moodle/site:accessallgroups');
75 /**
76 * Add book instance.
78 * @param stdClass $data
79 * @param stdClass $mform
80 * @return int new book instance id
82 function book_add_instance($data, $mform) {
83 global $DB;
85 $data->timecreated = time();
86 $data->timemodified = $data->timecreated;
87 if (!isset($data->customtitles)) {
88 $data->customtitles = 0;
91 return $DB->insert_record('book', $data);
94 /**
95 * Update book instance.
97 * @param stdClass $data
98 * @param stdClass $mform
99 * @return bool true
101 function book_update_instance($data, $mform) {
102 global $DB;
104 $data->timemodified = time();
105 $data->id = $data->instance;
106 if (!isset($data->customtitles)) {
107 $data->customtitles = 0;
110 $DB->update_record('book', $data);
112 $book = $DB->get_record('book', array('id'=>$data->id));
113 $DB->set_field('book', 'revision', $book->revision+1, array('id'=>$book->id));
115 return true;
119 * Delete book instance by activity id
121 * @param int $id
122 * @return bool success
124 function book_delete_instance($id) {
125 global $DB;
127 if (!$book = $DB->get_record('book', array('id'=>$id))) {
128 return false;
131 $DB->delete_records('book_chapters', array('bookid'=>$book->id));
132 $DB->delete_records('book', array('id'=>$book->id));
134 return true;
138 * Given a course and a time, this module should find recent activity
139 * that has occurred in book activities and print it out.
141 * @param stdClass $course
142 * @param bool $viewfullnames
143 * @param int $timestart
144 * @return bool true if there was output, or false is there was none
146 function book_print_recent_activity($course, $viewfullnames, $timestart) {
147 return false; // True if anything was printed, otherwise false
151 * This function is used by the reset_course_userdata function in moodlelib.
152 * @param $data the data submitted from the reset course.
153 * @return array status array
155 function book_reset_userdata($data) {
156 return array();
160 * No cron in book.
162 * @return bool
164 function book_cron () {
165 return true;
169 * No grading in book.
171 * @param int $bookid
172 * @return null
174 function book_grades($bookid) {
175 return null;
179 * This function returns if a scale is being used by one book
180 * it it has support for grading and scales. Commented code should be
181 * modified if necessary. See book, glossary or journal modules
182 * as reference.
184 * @param int $bookid
185 * @param int $scaleid
186 * @return boolean True if the scale is used by any journal
188 function book_scale_used($bookid, $scaleid) {
189 return false;
193 * Checks if scale is being used by any instance of book
195 * This is used to find out if scale used anywhere
197 * @param int $scaleid
198 * @return bool true if the scale is used by any book
200 function book_scale_used_anywhere($scaleid) {
201 return false;
205 * Return read actions.
207 * Note: This is not used by new logging system. Event with
208 * crud = 'r' and edulevel = LEVEL_PARTICIPATING will
209 * be considered as view action.
211 * @return array
213 function book_get_view_actions() {
214 global $CFG; // necessary for includes
216 $return = array('view', 'view all');
218 $plugins = core_component::get_plugin_list('booktool');
219 foreach ($plugins as $plugin => $dir) {
220 if (file_exists("$dir/lib.php")) {
221 require_once("$dir/lib.php");
223 $function = 'booktool_'.$plugin.'_get_view_actions';
224 if (function_exists($function)) {
225 if ($actions = $function()) {
226 $return = array_merge($return, $actions);
231 return $return;
235 * Return write actions.
237 * Note: This is not used by new logging system. Event with
238 * crud = ('c' || 'u' || 'd') and edulevel = LEVEL_PARTICIPATING
239 * will be considered as post action.
241 * @return array
243 function book_get_post_actions() {
244 global $CFG; // necessary for includes
246 $return = array('update');
248 $plugins = core_component::get_plugin_list('booktool');
249 foreach ($plugins as $plugin => $dir) {
250 if (file_exists("$dir/lib.php")) {
251 require_once("$dir/lib.php");
253 $function = 'booktool_'.$plugin.'_get_post_actions';
254 if (function_exists($function)) {
255 if ($actions = $function()) {
256 $return = array_merge($return, $actions);
261 return $return;
265 * Supported features
267 * @param string $feature FEATURE_xx constant for requested feature
268 * @return mixed True if module supports feature, false if not, null if doesn't know
270 function book_supports($feature) {
271 switch($feature) {
272 case FEATURE_MOD_ARCHETYPE: return MOD_ARCHETYPE_RESOURCE;
273 case FEATURE_GROUPS: return false;
274 case FEATURE_GROUPINGS: return false;
275 case FEATURE_MOD_INTRO: return true;
276 case FEATURE_COMPLETION_TRACKS_VIEWS: return true;
277 case FEATURE_GRADE_HAS_GRADE: return false;
278 case FEATURE_GRADE_OUTCOMES: return false;
279 case FEATURE_BACKUP_MOODLE2: return true;
280 case FEATURE_SHOW_DESCRIPTION: return true;
282 default: return null;
287 * Adds module specific settings to the settings block
289 * @param settings_navigation $settingsnav The settings navigation object
290 * @param navigation_node $booknode The node to add module settings to
291 * @return void
293 function book_extend_settings_navigation(settings_navigation $settingsnav, navigation_node $booknode) {
294 global $USER, $PAGE;
296 $plugins = core_component::get_plugin_list('booktool');
297 foreach ($plugins as $plugin => $dir) {
298 if (file_exists("$dir/lib.php")) {
299 require_once("$dir/lib.php");
301 $function = 'booktool_'.$plugin.'_extend_settings_navigation';
302 if (function_exists($function)) {
303 $function($settingsnav, $booknode);
307 $params = $PAGE->url->params();
309 if (!empty($params['id']) and !empty($params['chapterid']) and has_capability('mod/book:edit', $PAGE->cm->context)) {
310 if (!empty($USER->editing)) {
311 $string = get_string("turneditingoff");
312 $edit = '0';
313 } else {
314 $string = get_string("turneditingon");
315 $edit = '1';
317 $url = new moodle_url('/mod/book/view.php', array('id'=>$params['id'], 'chapterid'=>$params['chapterid'], 'edit'=>$edit, 'sesskey'=>sesskey()));
318 $booknode->add($string, $url, navigation_node::TYPE_SETTING);
324 * Lists all browsable file areas
325 * @param object $course
326 * @param object $cm
327 * @param object $context
328 * @return array
330 function book_get_file_areas($course, $cm, $context) {
331 $areas = array();
332 $areas['chapter'] = get_string('chapters', 'mod_book');
333 return $areas;
337 * File browsing support for book module chapter area.
338 * @param object $browser
339 * @param object $areas
340 * @param object $course
341 * @param object $cm
342 * @param object $context
343 * @param string $filearea
344 * @param int $itemid
345 * @param string $filepath
346 * @param string $filename
347 * @return object file_info instance or null if not found
349 function book_get_file_info($browser, $areas, $course, $cm, $context, $filearea, $itemid, $filepath, $filename) {
350 global $CFG, $DB;
352 // note: 'intro' area is handled in file_browser automatically
354 if (!has_capability('mod/book:read', $context)) {
355 return null;
358 if ($filearea !== 'chapter') {
359 return null;
362 require_once(dirname(__FILE__).'/locallib.php');
364 if (is_null($itemid)) {
365 return new book_file_info($browser, $course, $cm, $context, $areas, $filearea);
368 $fs = get_file_storage();
369 $filepath = is_null($filepath) ? '/' : $filepath;
370 $filename = is_null($filename) ? '.' : $filename;
371 if (!$storedfile = $fs->get_file($context->id, 'mod_book', $filearea, $itemid, $filepath, $filename)) {
372 return null;
375 // modifications may be tricky - may cause caching problems
376 $canwrite = has_capability('mod/book:edit', $context);
378 $chaptername = $DB->get_field('book_chapters', 'title', array('bookid'=>$cm->instance, 'id'=>$itemid));
379 $chaptername = format_string($chaptername, true, array('context'=>$context));
381 $urlbase = $CFG->wwwroot.'/pluginfile.php';
382 return new file_info_stored($browser, $context, $storedfile, $urlbase, $chaptername, true, true, $canwrite, false);
386 * Serves the book attachments. Implements needed access control ;-)
388 * @param stdClass $course course object
389 * @param cm_info $cm course module object
390 * @param context $context context object
391 * @param string $filearea file area
392 * @param array $args extra arguments
393 * @param bool $forcedownload whether or not force download
394 * @param array $options additional options affecting the file serving
395 * @return bool false if file not found, does not return if found - just send the file
397 function book_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload, array $options=array()) {
398 global $CFG, $DB;
400 if ($context->contextlevel != CONTEXT_MODULE) {
401 return false;
404 require_course_login($course, true, $cm);
406 if ($filearea !== 'chapter') {
407 return false;
410 if (!has_capability('mod/book:read', $context)) {
411 return false;
414 $chid = (int)array_shift($args);
416 if (!$book = $DB->get_record('book', array('id'=>$cm->instance))) {
417 return false;
420 if (!$chapter = $DB->get_record('book_chapters', array('id'=>$chid, 'bookid'=>$book->id))) {
421 return false;
424 if ($chapter->hidden and !has_capability('mod/book:viewhiddenchapters', $context)) {
425 return false;
428 // Download the contents of a chapter as an html file.
429 if ($args[0] == 'index.html') {
430 $filename = "index.html";
432 // Remove @@PLUGINFILE@@/.
433 $content = str_replace('@@PLUGINFILE@@/', '', $chapter->content);
435 $titles = "";
436 // Format the chapter titles.
437 if (!$book->customtitles) {
438 require_once(dirname(__FILE__).'/locallib.php');
439 $chapters = book_preload_chapters($book);
441 if (!$chapter->subchapter) {
442 $currtitle = book_get_chapter_title($chapter->id, $chapters, $book, $context);
443 // Note that we can't use the $OUTPUT->heading() in WS_SERVER mode.
444 $titles = "<h3>$currtitle</h3>";
445 } else {
446 $currtitle = book_get_chapter_title($chapters[$chapter->id]->parent, $chapters, $book, $context);
447 $currsubtitle = book_get_chapter_title($chapter->id, $chapters, $book, $context);
448 // Note that we can't use the $OUTPUT->heading() in WS_SERVER mode.
449 $titles = "<h3>$currtitle</h3>";
450 $titles .= "<h4>$currsubtitle</h4>";
454 $formatoptions = new stdClass;
455 $formatoptions->noclean = true;
456 $formatoptions->overflowdiv = true;
457 $formatoptions->context = $context;
459 $content = $titles . format_text($content, $chapter->contentformat, $formatoptions);
461 send_file($content, $filename, 0, 0, true, true);
462 } else {
463 $fs = get_file_storage();
464 $relativepath = implode('/', $args);
465 $fullpath = "/$context->id/mod_book/chapter/$chid/$relativepath";
466 if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
467 return false;
470 // Nasty hack because we do not have file revisions in book yet.
471 $lifetime = $CFG->filelifetime;
472 if ($lifetime > 60 * 10) {
473 $lifetime = 60 * 10;
476 // Finally send the file.
477 send_stored_file($file, $lifetime, 0, $forcedownload, $options);
482 * Return a list of page types
484 * @param string $pagetype current page type
485 * @param stdClass $parentcontext Block's parent context
486 * @param stdClass $currentcontext Current context of block
487 * @return array
489 function book_page_type_list($pagetype, $parentcontext, $currentcontext) {
490 $module_pagetype = array('mod-book-*'=>get_string('page-mod-book-x', 'mod_book'));
491 return $module_pagetype;
495 * Export book resource contents
497 * @param stdClass $cm Course module object
498 * @param string $baseurl Base URL for file downloads
499 * @return array of file content
501 function book_export_contents($cm, $baseurl) {
502 global $DB;
504 $contents = array();
505 $context = context_module::instance($cm->id);
507 $book = $DB->get_record('book', array('id' => $cm->instance), '*', MUST_EXIST);
509 $fs = get_file_storage();
511 $chapters = $DB->get_records('book_chapters', array('bookid' => $book->id), 'pagenum');
513 $structure = array();
514 $currentchapter = 0;
516 foreach ($chapters as $chapter) {
517 if ($chapter->hidden) {
518 continue;
521 // Generate the book structure.
522 $thischapter = array(
523 "title" => format_string($chapter->title, true, array('context' => $context)),
524 "href" => $chapter->id . "/index.html",
525 "level" => 0,
526 "subitems" => array()
529 // Main chapter.
530 if (!$chapter->subchapter) {
531 $currentchapter = $chapter->pagenum;
532 $structure[$currentchapter] = $thischapter;
533 } else {
534 // Subchapter.
535 $thischapter['level'] = 1;
536 $structure[$currentchapter]["subitems"][] = $thischapter;
539 // Export the chapter contents.
541 // Main content (html).
542 $filename = 'index.html';
543 $chapterindexfile = array();
544 $chapterindexfile['type'] = 'file';
545 $chapterindexfile['filename'] = $filename;
546 // Each chapter in a subdirectory.
547 $chapterindexfile['filepath'] = "/{$chapter->id}/";
548 $chapterindexfile['filesize'] = 0;
549 $chapterindexfile['fileurl'] = moodle_url::make_webservice_pluginfile_url(
550 $context->id, 'mod_book', 'chapter', $chapter->id, '/', 'index.html')->out(false);
551 $chapterindexfile['timecreated'] = $book->timecreated;
552 $chapterindexfile['timemodified'] = $book->timemodified;
553 $chapterindexfile['content'] = format_string($chapter->title, true, array('context' => $context));
554 $chapterindexfile['sortorder'] = 0;
555 $chapterindexfile['userid'] = null;
556 $chapterindexfile['author'] = null;
557 $chapterindexfile['license'] = null;
558 $contents[] = $chapterindexfile;
560 // Chapter files (images usually).
561 $files = $fs->get_area_files($context->id, 'mod_book', 'chapter', $chapter->id, 'sortorder DESC, id ASC', false);
562 foreach ($files as $fileinfo) {
563 $file = array();
564 $file['type'] = 'file';
565 $file['filename'] = $fileinfo->get_filename();
566 $file['filepath'] = "/{$chapter->id}/";
567 $file['filesize'] = $fileinfo->get_filesize();
568 $file['fileurl'] = moodle_url::make_webservice_pluginfile_url(
569 $context->id, 'mod_book', 'chapter', $chapter->id,
570 $fileinfo->get_filepath(), $fileinfo->get_filename())->out(false);
571 $file['timecreated'] = $fileinfo->get_timecreated();
572 $file['timemodified'] = $fileinfo->get_timemodified();
573 $file['sortorder'] = $fileinfo->get_sortorder();
574 $file['userid'] = $fileinfo->get_userid();
575 $file['author'] = $fileinfo->get_author();
576 $file['license'] = $fileinfo->get_license();
577 $contents[] = $file;
581 // First content is the structure in encoded JSON format.
582 $structurefile = array();
583 $structurefile['type'] = 'content';
584 $structurefile['filename'] = 'structure';
585 $structurefile['filepath'] = "/";
586 $structurefile['filesize'] = 0;
587 $structurefile['fileurl'] = null;
588 $structurefile['timecreated'] = $book->timecreated;
589 $structurefile['timemodified'] = $book->timemodified;
590 $structurefile['content'] = json_encode(array_values($structure));
591 $structurefile['sortorder'] = 0;
592 $structurefile['userid'] = null;
593 $structurefile['author'] = null;
594 $structurefile['license'] = null;
596 // Add it as first element.
597 array_unshift($contents, $structurefile);
599 return $contents;
603 * Mark the activity completed (if required) and trigger the course_module_viewed event.
605 * @param stdClass $book book object
606 * @param stdClass $chapter chapter object
607 * @param bool $islaschapter is the las chapter of the book?
608 * @param stdClass $course course object
609 * @param stdClass $cm course module object
610 * @param stdClass $context context object
611 * @since Moodle 3.0
613 function book_view($book, $chapter, $islastchapter, $course, $cm, $context) {
615 // First case, we are just opening the book.
616 if (empty($chapter)) {
617 \mod_book\event\course_module_viewed::create_from_book($book, $context)->trigger();
619 } else {
620 \mod_book\event\chapter_viewed::create_from_chapter($book, $context, $chapter)->trigger();
622 if ($islastchapter) {
623 // We cheat a bit here in assuming that viewing the last page means the user viewed the whole book.
624 $completion = new completion_info($course);
625 $completion->set_module_viewed($cm);