2 // This file is part of Moodle - http://moodle.org/
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.
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/>.
18 * Book module core interaction API
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;
28 * Returns list of available numbering types
31 function book_get_numbering_types() {
32 global $CFG; // required for the include
34 require_once(dirname(__FILE__
).'/locallib.php');
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')
45 * Returns list of available navigation link types.
48 function book_get_nav_types() {
49 require_once(dirname(__FILE__
).'/locallib.php');
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'),
59 * Returns list of available navigation link CSS classes.
62 function book_get_nav_classes() {
63 return array ('navtoc', 'navimages', 'navtext');
67 * Returns all other caps used in module
70 function book_get_extra_capabilities() {
71 // used for group-members-only
72 return array('moodle/site:accessallgroups');
78 * @param stdClass $data
79 * @param stdClass $mform
80 * @return int new book instance id
82 function book_add_instance($data, $mform) {
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);
95 * Update book instance.
97 * @param stdClass $data
98 * @param stdClass $mform
101 function book_update_instance($data, $mform) {
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
));
119 * Delete book instance by activity id
122 * @return bool success
124 function book_delete_instance($id) {
127 if (!$book = $DB->get_record('book', array('id'=>$id))) {
131 $DB->delete_records('book_chapters', array('bookid'=>$book->id
));
132 $DB->delete_records('book', array('id'=>$book->id
));
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) {
164 function book_cron () {
169 * No grading in book.
174 function book_grades($bookid) {
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
185 * @param int $scaleid
186 * @return boolean True if the scale is used by any journal
188 function book_scale_used($bookid, $scaleid) {
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) {
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.
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);
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.
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);
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) {
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
293 function book_extend_settings_navigation(settings_navigation
$settingsnav, navigation_node
$booknode) {
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");
314 $string = get_string("turneditingon");
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
327 * @param object $context
330 function book_get_file_areas($course, $cm, $context) {
332 $areas['chapter'] = get_string('chapters', 'mod_book');
337 * File browsing support for book module chapter area.
338 * @param object $browser
339 * @param object $areas
340 * @param object $course
342 * @param object $context
343 * @param string $filearea
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) {
352 // note: 'intro' area is handled in file_browser automatically
354 if (!has_capability('mod/book:read', $context)) {
358 if ($filearea !== 'chapter') {
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)) {
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()) {
400 if ($context->contextlevel
!= CONTEXT_MODULE
) {
404 require_course_login($course, true, $cm);
406 if ($filearea !== 'chapter') {
410 if (!has_capability('mod/book:read', $context)) {
414 $chid = (int)array_shift($args);
416 if (!$book = $DB->get_record('book', array('id'=>$cm->instance
))) {
420 if (!$chapter = $DB->get_record('book_chapters', array('id'=>$chid, 'bookid'=>$book->id
))) {
424 if ($chapter->hidden
and !has_capability('mod/book:viewhiddenchapters', $context)) {
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
);
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>";
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);
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()) {
470 // Nasty hack because we do not have file revisions in book yet.
471 $lifetime = $CFG->filelifetime
;
472 if ($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
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) {
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();
516 foreach ($chapters as $chapter) {
517 if ($chapter->hidden
) {
521 // Generate the book structure.
522 $thischapter = array(
523 "title" => format_string($chapter->title
, true, array('context' => $context)),
524 "href" => $chapter->id
. "/index.html",
526 "subitems" => array()
530 if (!$chapter->subchapter
) {
531 $currentchapter = $chapter->pagenum
;
532 $structure[$currentchapter] = $thischapter;
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) {
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();
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);
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
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();
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);