See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 401 and 402] [Versions 401 and 403]
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/>. 16 17 /** 18 * Book module core interaction API 19 * 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 23 */ 24 25 defined('MOODLE_INTERNAL') || die; 26 27 /** 28 * Returns list of available numbering types 29 * @return array 30 */ 31 function book_get_numbering_types() { 32 global $CFG; // required for the include 33 34 require_once (__DIR__.'/locallib.php'); 35 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') 41 ); 42 } 43 44 /** 45 * Returns list of available navigation link types. 46 * 47 * @deprecated since Moodle 4.0. MDL-72376. 48 * @return array 49 */ 50 function book_get_nav_types() { 51 debugging("book_get_nav_types() is deprecated. There is no replacement. Navigation is now only next and previous."); 52 require_once (__DIR__.'/locallib.php'); 53 54 return array ( 55 BOOK_LINK_TOCONLY => get_string('navtoc', 'mod_book'), 56 BOOK_LINK_IMAGE => get_string('navimages', 'mod_book'), 57 BOOK_LINK_TEXT => get_string('navtext', 'mod_book'), 58 ); 59 } 60 61 /** 62 * Returns list of available navigation link CSS classes. 63 * @return array 64 */ 65 function book_get_nav_classes() { 66 return array ('navtoc', 'navimages', 'navtext'); 67 } 68 69 /** 70 * Add book instance. 71 * 72 * @param stdClass $data 73 * @param stdClass $mform 74 * @return int new book instance id 75 */ 76 function book_add_instance($data, $mform) { 77 global $DB; 78 79 $data->timecreated = time(); 80 $data->timemodified = $data->timecreated; 81 if (!isset($data->customtitles)) { 82 $data->customtitles = 0; 83 } 84 85 $id = $DB->insert_record('book', $data); 86 87 $completiontimeexpected = !empty($data->completionexpected) ? $data->completionexpected : null; 88 \core_completion\api::update_completion_date_event($data->coursemodule, 'book', $id, $completiontimeexpected); 89 90 return $id; 91 } 92 93 /** 94 * Update book instance. 95 * 96 * @param stdClass $data 97 * @param stdClass $mform 98 * @return bool true 99 */ 100 function book_update_instance($data, $mform) { 101 global $DB; 102 103 $data->timemodified = time(); 104 $data->id = $data->instance; 105 if (!isset($data->customtitles)) { 106 $data->customtitles = 0; 107 } 108 109 $DB->update_record('book', $data); 110 111 $book = $DB->get_record('book', array('id'=>$data->id)); 112 $DB->set_field('book', 'revision', $book->revision+1, array('id'=>$book->id)); 113 114 $completiontimeexpected = !empty($data->completionexpected) ? $data->completionexpected : null; 115 \core_completion\api::update_completion_date_event($data->coursemodule, 'book', $book->id, $completiontimeexpected); 116 117 return true; 118 } 119 120 /** 121 * Delete book instance by activity id 122 * 123 * @param int $id 124 * @return bool success 125 */ 126 function book_delete_instance($id) { 127 global $DB; 128 129 if (!$book = $DB->get_record('book', array('id'=>$id))) { 130 return false; 131 } 132 133 $cm = get_coursemodule_from_instance('book', $id); 134 \core_completion\api::update_completion_date_event($cm->id, 'book', $id, null); 135 136 $DB->delete_records('book_chapters', array('bookid'=>$book->id)); 137 $DB->delete_records('book', array('id'=>$book->id)); 138 139 return true; 140 } 141 142 /** 143 * Given a course and a time, this module should find recent activity 144 * that has occurred in book activities and print it out. 145 * 146 * @param stdClass $course 147 * @param bool $viewfullnames 148 * @param int $timestart 149 * @return bool true if there was output, or false is there was none 150 */ 151 function book_print_recent_activity($course, $viewfullnames, $timestart) { 152 return false; // True if anything was printed, otherwise false 153 } 154 155 /** 156 * This function is used by the reset_course_userdata function in moodlelib. 157 * @param $data the data submitted from the reset course. 158 * @return array status array 159 */ 160 function book_reset_userdata($data) { 161 global $DB; 162 // Any changes to the list of dates that needs to be rolled should be same during course restore and course reset. 163 // See MDL-9367. 164 165 $status = []; 166 167 if (!empty($data->reset_book_tags)) { 168 // Loop through the books and remove the tags from the chapters. 169 if ($books = $DB->get_records('book', array('course' => $data->courseid))) { 170 foreach ($books as $book) { 171 if (!$cm = get_coursemodule_from_instance('book', $book->id)) { 172 continue; 173 } 174 175 $context = context_module::instance($cm->id); 176 core_tag_tag::delete_instances('mod_book', null, $context->id); 177 } 178 } 179 180 181 $status[] = [ 182 'component' => get_string('modulenameplural', 'book'), 183 'item' => get_string('tagsdeleted', 'book'), 184 'error' => false 185 ]; 186 } 187 188 return $status; 189 } 190 191 /** 192 * The elements to add the course reset form. 193 * 194 * @param moodleform $mform 195 */ 196 function book_reset_course_form_definition(&$mform) { 197 $mform->addElement('header', 'bookheader', get_string('modulenameplural', 'book')); 198 $mform->addElement('checkbox', 'reset_book_tags', get_string('removeallbooktags', 'book')); 199 } 200 201 /** 202 * No cron in book. 203 * 204 * @return bool 205 */ 206 function book_cron () { 207 return true; 208 } 209 210 /** 211 * No grading in book. 212 * 213 * @param int $bookid 214 * @return null 215 */ 216 function book_grades($bookid) { 217 return null; 218 } 219 220 /** 221 * @deprecated since Moodle 3.8 222 */ 223 function book_scale_used() { 224 throw new coding_exception('book_scale_used() can not be used anymore. Plugins can implement ' . 225 '<modname>_scale_used_anywhere, all implementations of <modname>_scale_used are now ignored'); 226 } 227 228 /** 229 * Checks if scale is being used by any instance of book 230 * 231 * This is used to find out if scale used anywhere 232 * 233 * @param int $scaleid 234 * @return bool true if the scale is used by any book 235 */ 236 function book_scale_used_anywhere($scaleid) { 237 return false; 238 } 239 240 /** 241 * Return read actions. 242 * 243 * Note: This is not used by new logging system. Event with 244 * crud = 'r' and edulevel = LEVEL_PARTICIPATING will 245 * be considered as view action. 246 * 247 * @return array 248 */ 249 function book_get_view_actions() { 250 global $CFG; // necessary for includes 251 252 $return = array('view', 'view all'); 253 254 $plugins = core_component::get_plugin_list('booktool'); 255 foreach ($plugins as $plugin => $dir) { 256 if (file_exists("$dir/lib.php")) { 257 require_once("$dir/lib.php"); 258 } 259 $function = 'booktool_'.$plugin.'_get_view_actions'; 260 if (function_exists($function)) { 261 if ($actions = $function()) { 262 $return = array_merge($return, $actions); 263 } 264 } 265 } 266 267 return $return; 268 } 269 270 /** 271 * Return write actions. 272 * 273 * Note: This is not used by new logging system. Event with 274 * crud = ('c' || 'u' || 'd') and edulevel = LEVEL_PARTICIPATING 275 * will be considered as post action. 276 * 277 * @return array 278 */ 279 function book_get_post_actions() { 280 global $CFG; // necessary for includes 281 282 $return = array('update'); 283 284 $plugins = core_component::get_plugin_list('booktool'); 285 foreach ($plugins as $plugin => $dir) { 286 if (file_exists("$dir/lib.php")) { 287 require_once("$dir/lib.php"); 288 } 289 $function = 'booktool_'.$plugin.'_get_post_actions'; 290 if (function_exists($function)) { 291 if ($actions = $function()) { 292 $return = array_merge($return, $actions); 293 } 294 } 295 } 296 297 return $return; 298 } 299 300 /** 301 * Supported features 302 * 303 * @param string $feature FEATURE_xx constant for requested feature 304 * @return mixed True if module supports feature, false if not, null if doesn't know or string for the module purpose. 305 */ 306 function book_supports($feature) { 307 switch($feature) { 308 case FEATURE_MOD_ARCHETYPE: return MOD_ARCHETYPE_RESOURCE; 309 case FEATURE_GROUPS: return false; 310 case FEATURE_GROUPINGS: return false; 311 case FEATURE_MOD_INTRO: return true; 312 case FEATURE_COMPLETION_TRACKS_VIEWS: return true; 313 case FEATURE_GRADE_HAS_GRADE: return false; 314 case FEATURE_GRADE_OUTCOMES: return false; 315 case FEATURE_BACKUP_MOODLE2: return true; 316 case FEATURE_SHOW_DESCRIPTION: return true; 317 case FEATURE_MOD_PURPOSE: return MOD_PURPOSE_CONTENT; 318 319 default: return null; 320 } 321 } 322 323 /** 324 * Adds module specific settings to the settings block 325 * 326 * @param settings_navigation $settingsnav The settings navigation object 327 * @param navigation_node $booknode The node to add module settings to 328 * @return void 329 */ 330 function book_extend_settings_navigation(settings_navigation $settingsnav, navigation_node $booknode) { 331 global $USER, $OUTPUT; 332 333 if ($booknode->children->count() > 0) { 334 $firstkey = $booknode->children->get_key_list()[0]; 335 } else { 336 $firstkey = null; 337 } 338 339 $params = $settingsnav->get_page()->url->params(); 340 341 if ($settingsnav->get_page()->cm->modname === 'book' and !empty($params['id']) and !empty($params['chapterid']) 342 and has_capability('mod/book:edit', $settingsnav->get_page()->cm->context)) { 343 if (!empty($USER->editing)) { 344 $string = get_string("turneditingoff"); 345 $edit = '0'; 346 } else { 347 $string = get_string("turneditingon"); 348 $edit = '1'; 349 } 350 $url = new moodle_url('/mod/book/view.php', array('id'=>$params['id'], 'chapterid'=>$params['chapterid'], 'edit'=>$edit, 'sesskey'=>sesskey())); 351 $editnode = navigation_node::create($string, $url, navigation_node::TYPE_SETTING); 352 $editnode->set_show_in_secondary_navigation(false); 353 $booknode->add_node($editnode, $firstkey); 354 if (!$settingsnav->get_page()->theme->haseditswitch) { 355 $settingsnav->get_page()->set_button($OUTPUT->single_button($url, $string)); 356 } 357 } 358 359 $plugins = core_component::get_plugin_list('booktool'); 360 foreach ($plugins as $plugin => $dir) { 361 if (file_exists("$dir/lib.php")) { 362 require_once("$dir/lib.php"); 363 } 364 $function = 'booktool_'.$plugin.'_extend_settings_navigation'; 365 if (function_exists($function)) { 366 $function($settingsnav, $booknode); 367 } 368 } 369 } 370 371 372 /** 373 * Lists all browsable file areas 374 * @param object $course 375 * @param object $cm 376 * @param object $context 377 * @return array 378 */ 379 function book_get_file_areas($course, $cm, $context) { 380 $areas = array(); 381 $areas['chapter'] = get_string('chapters', 'mod_book'); 382 return $areas; 383 } 384 385 /** 386 * File browsing support for book module chapter area. 387 * @param object $browser 388 * @param object $areas 389 * @param object $course 390 * @param object $cm 391 * @param object $context 392 * @param string $filearea 393 * @param int $itemid 394 * @param string $filepath 395 * @param string $filename 396 * @return object file_info instance or null if not found 397 */ 398 function book_get_file_info($browser, $areas, $course, $cm, $context, $filearea, $itemid, $filepath, $filename) { 399 global $CFG, $DB; 400 401 // note: 'intro' area is handled in file_browser automatically 402 403 if (!has_capability('mod/book:read', $context)) { 404 return null; 405 } 406 407 if ($filearea !== 'chapter') { 408 return null; 409 } 410 411 require_once (__DIR__.'/locallib.php'); 412 413 if (is_null($itemid)) { 414 return new book_file_info($browser, $course, $cm, $context, $areas, $filearea); 415 } 416 417 $fs = get_file_storage(); 418 $filepath = is_null($filepath) ? '/' : $filepath; 419 $filename = is_null($filename) ? '.' : $filename; 420 if (!$storedfile = $fs->get_file($context->id, 'mod_book', $filearea, $itemid, $filepath, $filename)) { 421 return null; 422 } 423 424 // modifications may be tricky - may cause caching problems 425 $canwrite = has_capability('mod/book:edit', $context); 426 427 $chaptername = $DB->get_field('book_chapters', 'title', array('bookid'=>$cm->instance, 'id'=>$itemid)); 428 $chaptername = format_string($chaptername, true, array('context'=>$context)); 429 430 $urlbase = $CFG->wwwroot.'/pluginfile.php'; 431 return new file_info_stored($browser, $context, $storedfile, $urlbase, $chaptername, true, true, $canwrite, false); 432 } 433 434 /** 435 * Serves the book attachments. Implements needed access control ;-) 436 * 437 * @param stdClass $course course object 438 * @param cm_info $cm course module object 439 * @param context $context context object 440 * @param string $filearea file area 441 * @param array $args extra arguments 442 * @param bool $forcedownload whether or not force download 443 * @param array $options additional options affecting the file serving 444 * @return bool false if file not found, does not return if found - just send the file 445 */ 446 function book_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload, array $options=array()) { 447 global $CFG, $DB; 448 449 if ($context->contextlevel != CONTEXT_MODULE) { 450 return false; 451 } 452 453 require_course_login($course, true, $cm); 454 455 if ($filearea !== 'chapter') { 456 return false; 457 } 458 459 if (!has_capability('mod/book:read', $context)) { 460 return false; 461 } 462 463 $chid = (int)array_shift($args); 464 465 if (!$book = $DB->get_record('book', array('id'=>$cm->instance))) { 466 return false; 467 } 468 469 if (!$chapter = $DB->get_record('book_chapters', array('id'=>$chid, 'bookid'=>$book->id))) { 470 return false; 471 } 472 473 if ($chapter->hidden and !has_capability('mod/book:viewhiddenchapters', $context)) { 474 return false; 475 } 476 477 // Download the contents of a chapter as an html file. 478 if ($args[0] == 'index.html') { 479 $filename = "index.html"; 480 481 // We need to rewrite the pluginfile URLs so the media filters can work. 482 $content = file_rewrite_pluginfile_urls($chapter->content, 'webservice/pluginfile.php', $context->id, 'mod_book', 'chapter', 483 $chapter->id); 484 $formatoptions = new stdClass; 485 $formatoptions->noclean = true; 486 $formatoptions->overflowdiv = true; 487 $formatoptions->context = $context; 488 489 $content = format_text($content, $chapter->contentformat, $formatoptions); 490 491 // Remove @@PLUGINFILE@@/. 492 $options = array('reverse' => true); 493 $content = file_rewrite_pluginfile_urls($content, 'webservice/pluginfile.php', $context->id, 'mod_book', 'chapter', 494 $chapter->id, $options); 495 $content = str_replace('@@PLUGINFILE@@/', '', $content); 496 497 $titles = ""; 498 // Format the chapter titles. 499 if (!$book->customtitles) { 500 require_once (__DIR__.'/locallib.php'); 501 $chapters = book_preload_chapters($book); 502 503 if (!$chapter->subchapter) { 504 $currtitle = book_get_chapter_title($chapter->id, $chapters, $book, $context); 505 // Note that we can't use the $OUTPUT->heading() in WS_SERVER mode. 506 $titles = "<h3>$currtitle</h3>"; 507 } else { 508 $currtitle = book_get_chapter_title($chapters[$chapter->id]->parent, $chapters, $book, $context); 509 $currsubtitle = book_get_chapter_title($chapter->id, $chapters, $book, $context); 510 // Note that we can't use the $OUTPUT->heading() in WS_SERVER mode. 511 $titles = "<h3>$currtitle</h3>"; 512 $titles .= "<h4>$currsubtitle</h4>"; 513 } 514 } 515 516 $content = $titles . $content; 517 518 send_file($content, $filename, 0, 0, true, true); 519 } else { 520 $fs = get_file_storage(); 521 $relativepath = implode('/', $args); 522 $fullpath = "/$context->id/mod_book/chapter/$chid/$relativepath"; 523 if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) { 524 return false; 525 } 526 527 // Nasty hack because we do not have file revisions in book yet. 528 $lifetime = $CFG->filelifetime; 529 if ($lifetime > 60 * 10) { 530 $lifetime = 60 * 10; 531 } 532 533 // Finally send the file. 534 send_stored_file($file, $lifetime, 0, $forcedownload, $options); 535 } 536 } 537 538 /** 539 * Return a list of page types 540 * 541 * @param string $pagetype current page type 542 * @param stdClass $parentcontext Block's parent context 543 * @param stdClass $currentcontext Current context of block 544 * @return array 545 */ 546 function book_page_type_list($pagetype, $parentcontext, $currentcontext) { 547 $module_pagetype = array('mod-book-*'=>get_string('page-mod-book-x', 'mod_book')); 548 return $module_pagetype; 549 } 550 551 /** 552 * Export book resource contents 553 * 554 * @param stdClass $cm Course module object 555 * @param string $baseurl Base URL for file downloads 556 * @return array of file content 557 */ 558 function book_export_contents($cm, $baseurl) { 559 global $DB; 560 561 $contents = array(); 562 $context = context_module::instance($cm->id); 563 564 $book = $DB->get_record('book', array('id' => $cm->instance), '*', MUST_EXIST); 565 566 $fs = get_file_storage(); 567 568 $chapters = $DB->get_records('book_chapters', array('bookid' => $book->id), 'pagenum'); 569 570 $structure = array(); 571 $currentchapter = 0; 572 573 foreach ($chapters as $chapter) { 574 if ($chapter->hidden && !has_capability('mod/book:viewhiddenchapters', $context)) { 575 continue; 576 } 577 578 // Generate the book structure. 579 $thischapter = array( 580 "title" => format_string($chapter->title, true, array('context' => $context)), 581 "href" => $chapter->id . "/index.html", 582 "level" => 0, 583 "hidden" => $chapter->hidden, 584 "subitems" => array() 585 ); 586 587 // Main chapter. 588 if (!$chapter->subchapter) { 589 $currentchapter = $chapter->pagenum; 590 $structure[$currentchapter] = $thischapter; 591 } else { 592 // Subchapter. 593 $thischapter['level'] = 1; 594 $structure[$currentchapter]["subitems"][] = $thischapter; 595 } 596 597 // Export the chapter contents. 598 599 // Main content (html). 600 $filename = 'index.html'; 601 $chapterindexfile = array(); 602 $chapterindexfile['type'] = 'file'; 603 $chapterindexfile['filename'] = $filename; 604 // Each chapter in a subdirectory. 605 $chapterindexfile['filepath'] = "/{$chapter->id}/"; 606 $chapterindexfile['filesize'] = 0; 607 $chapterindexfile['fileurl'] = moodle_url::make_webservice_pluginfile_url( 608 $context->id, 'mod_book', 'chapter', $chapter->id, '/', 'index.html')->out(false); 609 $chapterindexfile['timecreated'] = $chapter->timecreated; 610 $chapterindexfile['timemodified'] = $chapter->timemodified; 611 $chapterindexfile['content'] = format_string($chapter->title, true, array('context' => $context)); 612 $chapterindexfile['sortorder'] = 0; 613 $chapterindexfile['userid'] = null; 614 $chapterindexfile['author'] = null; 615 $chapterindexfile['license'] = null; 616 $chapterindexfile['tags'] = \core_tag\external\util::get_item_tags('mod_book', 'book_chapters', $chapter->id); 617 $contents[] = $chapterindexfile; 618 619 // Chapter files (images usually). 620 $files = $fs->get_area_files($context->id, 'mod_book', 'chapter', $chapter->id, 'sortorder DESC, id ASC', false); 621 foreach ($files as $fileinfo) { 622 $file = array(); 623 $file['type'] = 'file'; 624 $file['filename'] = $fileinfo->get_filename(); 625 $file['filepath'] = "/{$chapter->id}" . $fileinfo->get_filepath(); 626 $file['filesize'] = $fileinfo->get_filesize(); 627 $file['fileurl'] = moodle_url::make_webservice_pluginfile_url( 628 $context->id, 'mod_book', 'chapter', $chapter->id, 629 $fileinfo->get_filepath(), $fileinfo->get_filename())->out(false); 630 $file['timecreated'] = $fileinfo->get_timecreated(); 631 $file['timemodified'] = $fileinfo->get_timemodified(); 632 $file['sortorder'] = $fileinfo->get_sortorder(); 633 $file['userid'] = $fileinfo->get_userid(); 634 $file['author'] = $fileinfo->get_author(); 635 $file['license'] = $fileinfo->get_license(); 636 $file['mimetype'] = $fileinfo->get_mimetype(); 637 $file['isexternalfile'] = $fileinfo->is_external_file(); 638 if ($file['isexternalfile']) { 639 $file['repositorytype'] = $fileinfo->get_repository_type(); 640 } 641 $contents[] = $file; 642 } 643 } 644 645 // First content is the structure in encoded JSON format. 646 $structurefile = array(); 647 $structurefile['type'] = 'content'; 648 $structurefile['filename'] = 'structure'; 649 $structurefile['filepath'] = "/"; 650 $structurefile['filesize'] = 0; 651 $structurefile['fileurl'] = null; 652 $structurefile['timecreated'] = $book->timecreated; 653 $structurefile['timemodified'] = $book->timemodified; 654 $structurefile['content'] = json_encode(array_values($structure)); 655 $structurefile['sortorder'] = 0; 656 $structurefile['userid'] = null; 657 $structurefile['author'] = null; 658 $structurefile['license'] = null; 659 660 // Add it as first element. 661 array_unshift($contents, $structurefile); 662 663 return $contents; 664 } 665 666 /** 667 * Mark the activity completed (if required) and trigger the course_module_viewed event. 668 * 669 * @param stdClass $book book object 670 * @param stdClass $chapter chapter object 671 * @param bool $islaschapter is the las chapter of the book? 672 * @param stdClass $course course object 673 * @param stdClass $cm course module object 674 * @param stdClass $context context object 675 * @since Moodle 3.0 676 */ 677 function book_view($book, $chapter, $islastchapter, $course, $cm, $context) { 678 679 // First case, we are just opening the book. 680 if (empty($chapter)) { 681 \mod_book\event\course_module_viewed::create_from_book($book, $context)->trigger(); 682 683 } else { 684 \mod_book\event\chapter_viewed::create_from_chapter($book, $context, $chapter)->trigger(); 685 686 if ($islastchapter) { 687 // We cheat a bit here in assuming that viewing the last page means the user viewed the whole book. 688 $completion = new completion_info($course); 689 $completion->set_module_viewed($cm); 690 } 691 } 692 } 693 694 /** 695 * Check if the module has any update that affects the current user since a given time. 696 * 697 * @param cm_info $cm course module data 698 * @param int $from the time to check updates from 699 * @param array $filter if we need to check only specific updates 700 * @return stdClass an object with the different type of areas indicating if they were updated or not 701 * @since Moodle 3.2 702 */ 703 function book_check_updates_since(cm_info $cm, $from, $filter = array()) { 704 global $DB; 705 706 $context = $cm->context; 707 $updates = new stdClass(); 708 if (!has_capability('mod/book:read', $context)) { 709 return $updates; 710 } 711 $updates = course_check_module_updates_since($cm, $from, array('content'), $filter); 712 713 $select = 'bookid = :id AND (timecreated > :since1 OR timemodified > :since2)'; 714 $params = array('id' => $cm->instance, 'since1' => $from, 'since2' => $from); 715 if (!has_capability('mod/book:viewhiddenchapters', $context)) { 716 $select .= ' AND hidden = 0'; 717 } 718 $updates->entries = (object) array('updated' => false); 719 $entries = $DB->get_records_select('book_chapters', $select, $params, '', 'id'); 720 if (!empty($entries)) { 721 $updates->entries->updated = true; 722 $updates->entries->itemids = array_keys($entries); 723 } 724 725 return $updates; 726 } 727 728 /** 729 * Get icon mapping for font-awesome. 730 */ 731 function mod_book_get_fontawesome_icon_map() { 732 return [ 733 'mod_book:chapter' => 'fa-bookmark-o', 734 'mod_book:nav_prev' => 'fa-arrow-left', 735 'mod_book:nav_sep' => 'fa-minus', 736 'mod_book:add' => 'fa-plus', 737 'mod_book:nav_next' => 'fa-arrow-right', 738 'mod_book:nav_exit' => 'fa-arrow-up', 739 ]; 740 } 741 742 /** 743 * This function receives a calendar event and returns the action associated with it, or null if there is none. 744 * 745 * This is used by block_myoverview in order to display the event appropriately. If null is returned then the event 746 * is not displayed on the block. 747 * 748 * @param calendar_event $event 749 * @param \core_calendar\action_factory $factory 750 * @param int $userid User id to use for all capability checks, etc. Set to 0 for current user (default). 751 * @return \core_calendar\local\event\entities\action_interface|null 752 */ 753 function mod_book_core_calendar_provide_event_action(calendar_event $event, 754 \core_calendar\action_factory $factory, 755 int $userid = 0) { 756 global $USER; 757 758 if (empty($userid)) { 759 $userid = $USER->id; 760 } 761 762 $cm = get_fast_modinfo($event->courseid, $userid)->instances['book'][$event->instance]; 763 764 if (!$cm->uservisible) { 765 // The module is not visible to the user for any reason. 766 return null; 767 } 768 769 $context = context_module::instance($cm->id); 770 771 if (!has_capability('mod/book:read', $context, $userid)) { 772 return null; 773 } 774 775 $completion = new \completion_info($cm->get_course()); 776 777 $completiondata = $completion->get_data($cm, false, $userid); 778 779 if ($completiondata->completionstate != COMPLETION_INCOMPLETE) { 780 return null; 781 } 782 783 return $factory->create_instance( 784 get_string('view'), 785 new \moodle_url('/mod/book/view.php', ['id' => $cm->id]), 786 1, 787 true 788 ); 789 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body