See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 400 and 401] [Versions 401 and 403]
1 <?php 2 3 // This file is part of Moodle - http://moodle.org/ 4 // 5 // Moodle is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // Moodle is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU General Public License for more details. 14 // 15 // You should have received a copy of the GNU General Public License 16 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 17 18 /** 19 * Mandatory public API of folder module 20 * 21 * @package mod_folder 22 * @copyright 2009 Petr Skoda {@link http://skodak.org} 23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 */ 25 26 defined('MOODLE_INTERNAL') || die(); 27 28 /** Display folder contents on a separate page */ 29 define('FOLDER_DISPLAY_PAGE', 0); 30 /** Display folder contents inline in a course */ 31 define('FOLDER_DISPLAY_INLINE', 1); 32 33 /** 34 * List of features supported in Folder module 35 * @param string $feature FEATURE_xx constant for requested feature 36 * @return mixed True if module supports feature, false if not, null if doesn't know or string for the module purpose. 37 */ 38 function folder_supports($feature) { 39 switch($feature) { 40 case FEATURE_MOD_ARCHETYPE: return MOD_ARCHETYPE_RESOURCE; 41 case FEATURE_GROUPS: return false; 42 case FEATURE_GROUPINGS: return false; 43 case FEATURE_MOD_INTRO: return true; 44 case FEATURE_COMPLETION_TRACKS_VIEWS: return true; 45 case FEATURE_GRADE_HAS_GRADE: return false; 46 case FEATURE_GRADE_OUTCOMES: return false; 47 case FEATURE_BACKUP_MOODLE2: return true; 48 case FEATURE_SHOW_DESCRIPTION: return true; 49 case FEATURE_MOD_PURPOSE: return MOD_PURPOSE_CONTENT; 50 51 default: return null; 52 } 53 } 54 55 /** 56 * This function is used by the reset_course_userdata function in moodlelib. 57 * @param $data the data submitted from the reset course. 58 * @return array status array 59 */ 60 function folder_reset_userdata($data) { 61 62 // Any changes to the list of dates that needs to be rolled should be same during course restore and course reset. 63 // See MDL-9367. 64 65 return array(); 66 } 67 68 /** 69 * List the actions that correspond to a view of this module. 70 * This is used by the participation report. 71 * 72 * Note: This is not used by new logging system. Event with 73 * crud = 'r' and edulevel = LEVEL_PARTICIPATING will 74 * be considered as view action. 75 * 76 * @return array 77 */ 78 function folder_get_view_actions() { 79 return array('view', 'view all'); 80 } 81 82 /** 83 * List the actions that correspond to a post of this module. 84 * This is used by the participation report. 85 * 86 * Note: This is not used by new logging system. Event with 87 * crud = ('c' || 'u' || 'd') and edulevel = LEVEL_PARTICIPATING 88 * will be considered as post action. 89 * 90 * @return array 91 */ 92 function folder_get_post_actions() { 93 return array('update', 'add'); 94 } 95 96 /** 97 * Add folder instance. 98 * @param object $data 99 * @param object $mform 100 * @return int new folder instance id 101 */ 102 function folder_add_instance($data, $mform) { 103 global $DB; 104 105 $cmid = $data->coursemodule; 106 $draftitemid = $data->files; 107 108 $data->timemodified = time(); 109 // If 'showexpanded' is not set, apply the site config. 110 if (!isset($data->showexpanded)) { 111 $data->showexpanded = get_config('folder', 'showexpanded'); 112 } 113 $data->id = $DB->insert_record('folder', $data); 114 115 // we need to use context now, so we need to make sure all needed info is already in db 116 $DB->set_field('course_modules', 'instance', $data->id, array('id'=>$cmid)); 117 $context = context_module::instance($cmid); 118 119 if ($draftitemid) { 120 file_save_draft_area_files($draftitemid, $context->id, 'mod_folder', 'content', 0, array('subdirs'=>true)); 121 } 122 123 $completiontimeexpected = !empty($data->completionexpected) ? $data->completionexpected : null; 124 \core_completion\api::update_completion_date_event($data->coursemodule, 'folder', $data->id, $completiontimeexpected); 125 126 return $data->id; 127 } 128 129 /** 130 * Update folder instance. 131 * @param object $data 132 * @param object $mform 133 * @return bool true 134 */ 135 function folder_update_instance($data, $mform) { 136 global $CFG, $DB; 137 138 $cmid = $data->coursemodule; 139 $draftitemid = $data->files; 140 141 $data->timemodified = time(); 142 $data->id = $data->instance; 143 $data->revision++; 144 145 $DB->update_record('folder', $data); 146 147 $context = context_module::instance($cmid); 148 if ($draftitemid = file_get_submitted_draft_itemid('files')) { 149 file_save_draft_area_files($draftitemid, $context->id, 'mod_folder', 'content', 0, array('subdirs'=>true)); 150 } 151 152 $completiontimeexpected = !empty($data->completionexpected) ? $data->completionexpected : null; 153 \core_completion\api::update_completion_date_event($data->coursemodule, 'folder', $data->id, $completiontimeexpected); 154 155 return true; 156 } 157 158 /** 159 * Delete folder instance. 160 * @param int $id 161 * @return bool true 162 */ 163 function folder_delete_instance($id) { 164 global $DB; 165 166 if (!$folder = $DB->get_record('folder', array('id'=>$id))) { 167 return false; 168 } 169 170 $cm = get_coursemodule_from_instance('folder', $id); 171 \core_completion\api::update_completion_date_event($cm->id, 'folder', $folder->id, null); 172 173 // note: all context files are deleted automatically 174 175 $DB->delete_records('folder', array('id'=>$folder->id)); 176 177 return true; 178 } 179 180 /** 181 * Lists all browsable file areas 182 * 183 * @package mod_folder 184 * @category files 185 * @param stdClass $course course object 186 * @param stdClass $cm course module object 187 * @param stdClass $context context object 188 * @return array 189 */ 190 function folder_get_file_areas($course, $cm, $context) { 191 $areas = array(); 192 $areas['content'] = get_string('foldercontent', 'folder'); 193 194 return $areas; 195 } 196 197 /** 198 * File browsing support for folder module content area. 199 * 200 * @package mod_folder 201 * @category files 202 * @param file_browser $browser file browser instance 203 * @param array $areas file areas 204 * @param stdClass $course course object 205 * @param stdClass $cm course module object 206 * @param stdClass $context context object 207 * @param string $filearea file area 208 * @param int $itemid item ID 209 * @param string $filepath file path 210 * @param string $filename file name 211 * @return file_info instance or null if not found 212 */ 213 function folder_get_file_info($browser, $areas, $course, $cm, $context, $filearea, $itemid, $filepath, $filename) { 214 global $CFG; 215 216 217 if ($filearea === 'content') { 218 if (!has_capability('mod/folder:view', $context)) { 219 return NULL; 220 } 221 $fs = get_file_storage(); 222 223 $filepath = is_null($filepath) ? '/' : $filepath; 224 $filename = is_null($filename) ? '.' : $filename; 225 if (!$storedfile = $fs->get_file($context->id, 'mod_folder', 'content', 0, $filepath, $filename)) { 226 if ($filepath === '/' and $filename === '.') { 227 $storedfile = new virtual_root_file($context->id, 'mod_folder', 'content', 0); 228 } else { 229 // not found 230 return null; 231 } 232 } 233 234 require_once("$CFG->dirroot/mod/folder/locallib.php"); 235 $urlbase = $CFG->wwwroot.'/pluginfile.php'; 236 237 // students may read files here 238 $canwrite = has_capability('mod/folder:managefiles', $context); 239 return new folder_content_file_info($browser, $context, $storedfile, $urlbase, $areas[$filearea], true, true, $canwrite, false); 240 } 241 242 // note: folder_intro handled in file_browser automatically 243 244 return null; 245 } 246 247 /** 248 * Serves the folder files. 249 * 250 * @package mod_folder 251 * @category files 252 * @param stdClass $course course object 253 * @param stdClass $cm course module 254 * @param stdClass $context context object 255 * @param string $filearea file area 256 * @param array $args extra arguments 257 * @param bool $forcedownload whether or not force download 258 * @param array $options additional options affecting the file serving 259 * @return bool false if file not found, does not return if found - just send the file 260 */ 261 function folder_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload, array $options=array()) { 262 global $CFG, $DB; 263 264 if ($context->contextlevel != CONTEXT_MODULE) { 265 return false; 266 } 267 268 require_course_login($course, true, $cm); 269 if (!has_capability('mod/folder:view', $context)) { 270 return false; 271 } 272 273 if ($filearea !== 'content') { 274 // intro is handled automatically in pluginfile.php 275 return false; 276 } 277 278 array_shift($args); // ignore revision - designed to prevent caching problems only 279 280 $fs = get_file_storage(); 281 $relativepath = implode('/', $args); 282 $fullpath = "/$context->id/mod_folder/content/0/$relativepath"; 283 if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) { 284 return false; 285 } 286 287 // Set security posture for in-browser display. 288 if (!$forcedownload) { 289 header("Content-Security-Policy: default-src 'none'; img-src 'self'; media-src 'self'"); 290 } 291 292 // Finally send the file. 293 send_stored_file($file, 0, 0, $forcedownload, $options); 294 } 295 296 /** 297 * Return a list of page types 298 * @param string $pagetype current page type 299 * @param stdClass $parentcontext Block's parent context 300 * @param stdClass $currentcontext Current context of block 301 */ 302 function folder_page_type_list($pagetype, $parentcontext, $currentcontext) { 303 $module_pagetype = array('mod-folder-*'=>get_string('page-mod-folder-x', 'folder')); 304 return $module_pagetype; 305 } 306 307 /** 308 * Export folder resource contents 309 * 310 * @return array of file content 311 */ 312 function folder_export_contents($cm, $baseurl) { 313 global $CFG, $DB; 314 $contents = array(); 315 $context = context_module::instance($cm->id); 316 $folder = $DB->get_record('folder', array('id'=>$cm->instance), '*', MUST_EXIST); 317 318 $fs = get_file_storage(); 319 $files = $fs->get_area_files($context->id, 'mod_folder', 'content', 0, 'sortorder DESC, id ASC', false); 320 321 foreach ($files as $fileinfo) { 322 $file = array(); 323 $file['type'] = 'file'; 324 $file['filename'] = $fileinfo->get_filename(); 325 $file['filepath'] = $fileinfo->get_filepath(); 326 $file['filesize'] = $fileinfo->get_filesize(); 327 $file['fileurl'] = file_encode_url("$CFG->wwwroot/" . $baseurl, '/'.$context->id.'/mod_folder/content/'.$folder->revision.$fileinfo->get_filepath().$fileinfo->get_filename(), true); 328 $file['timecreated'] = $fileinfo->get_timecreated(); 329 $file['timemodified'] = $fileinfo->get_timemodified(); 330 $file['sortorder'] = $fileinfo->get_sortorder(); 331 $file['userid'] = $fileinfo->get_userid(); 332 $file['author'] = $fileinfo->get_author(); 333 $file['license'] = $fileinfo->get_license(); 334 $file['mimetype'] = $fileinfo->get_mimetype(); 335 $file['isexternalfile'] = $fileinfo->is_external_file(); 336 if ($file['isexternalfile']) { 337 $file['repositorytype'] = $fileinfo->get_repository_type(); 338 } 339 $contents[] = $file; 340 } 341 342 return $contents; 343 } 344 345 /** 346 * Register the ability to handle drag and drop file uploads 347 * @return array containing details of the files / types the mod can handle 348 */ 349 function folder_dndupload_register() { 350 return array('files' => array( 351 array('extension' => 'zip', 'message' => get_string('dnduploadmakefolder', 'mod_folder')) 352 )); 353 } 354 355 /** 356 * Handle a file that has been uploaded 357 * @param object $uploadinfo details of the file / content that has been uploaded 358 * @return int instance id of the newly created mod 359 */ 360 function folder_dndupload_handle($uploadinfo) { 361 global $DB, $USER; 362 363 // Gather the required info. 364 $data = new stdClass(); 365 $data->course = $uploadinfo->course->id; 366 $data->name = $uploadinfo->displayname; 367 $data->intro = '<p>'.$uploadinfo->displayname.'</p>'; 368 $data->introformat = FORMAT_HTML; 369 $data->coursemodule = $uploadinfo->coursemodule; 370 $data->files = null; // We will unzip the file and sort out the contents below. 371 372 $data->id = folder_add_instance($data, null); 373 374 // Retrieve the file from the draft file area. 375 $context = context_module::instance($uploadinfo->coursemodule); 376 file_save_draft_area_files($uploadinfo->draftitemid, $context->id, 'mod_folder', 'temp', 0, array('subdirs'=>true)); 377 $fs = get_file_storage(); 378 $files = $fs->get_area_files($context->id, 'mod_folder', 'temp', 0, 'sortorder', false); 379 // Only ever one file - extract the contents. 380 $file = reset($files); 381 382 $success = $file->extract_to_storage(new zip_packer(), $context->id, 'mod_folder', 'content', 0, '/', $USER->id); 383 $fs->delete_area_files($context->id, 'mod_folder', 'temp', 0); 384 385 if ($success) { 386 return $data->id; 387 } 388 389 $DB->delete_records('folder', array('id' => $data->id)); 390 return false; 391 } 392 393 /** 394 * Given a coursemodule object, this function returns the extra 395 * information needed to print this activity in various places. 396 * 397 * If folder needs to be displayed inline we store additional information 398 * in customdata, so functions {@link folder_cm_info_dynamic()} and 399 * {@link folder_cm_info_view()} do not need to do DB queries 400 * 401 * @param cm_info $cm 402 * @return cached_cm_info info 403 */ 404 function folder_get_coursemodule_info($cm) { 405 global $DB; 406 if (!($folder = $DB->get_record('folder', array('id' => $cm->instance), 407 'id, name, display, showexpanded, showdownloadfolder, forcedownload, intro, introformat'))) { 408 return NULL; 409 } 410 $cminfo = new cached_cm_info(); 411 $cminfo->name = $folder->name; 412 if ($folder->display == FOLDER_DISPLAY_INLINE) { 413 // prepare folder object to store in customdata 414 $fdata = new stdClass(); 415 $fdata->showexpanded = $folder->showexpanded; 416 $fdata->showdownloadfolder = $folder->showdownloadfolder; 417 $fdata->forcedownload = $folder->forcedownload; 418 if ($cm->showdescription && strlen(trim($folder->intro))) { 419 $fdata->intro = $folder->intro; 420 if ($folder->introformat != FORMAT_MOODLE) { 421 $fdata->introformat = $folder->introformat; 422 } 423 } 424 $cminfo->customdata = $fdata; 425 } else { 426 if ($cm->showdescription) { 427 // Convert intro to html. Do not filter cached version, filters run at display time. 428 $cminfo->content = format_module_intro('folder', $folder, $cm->id, false); 429 } 430 } 431 return $cminfo; 432 } 433 434 /** 435 * Sets dynamic information about a course module 436 * 437 * This function is called from cm_info when displaying the module 438 * mod_folder can be displayed inline on course page and therefore have no course link 439 * 440 * @param cm_info $cm 441 */ 442 function folder_cm_info_dynamic(cm_info $cm) { 443 if ($cm->get_custom_data()) { 444 // the field 'customdata' is not empty IF AND ONLY IF we display contens inline 445 $cm->set_no_view_link(); 446 } 447 } 448 449 /** 450 * Overwrites the content in the course-module object with the folder files list 451 * if folder.display == FOLDER_DISPLAY_INLINE 452 * 453 * @param cm_info $cm 454 */ 455 function folder_cm_info_view(cm_info $cm) { 456 global $PAGE; 457 if ($cm->uservisible && $cm->customdata && 458 has_capability('mod/folder:view', $cm->context)) { 459 // Restore folder object from customdata. 460 // Note the field 'customdata' is not empty IF AND ONLY IF we display contens inline. 461 // Otherwise the content is default. 462 $folder = $cm->customdata; 463 $folder->id = (int)$cm->instance; 464 $folder->course = (int)$cm->course; 465 $folder->display = FOLDER_DISPLAY_INLINE; 466 $folder->name = $cm->name; 467 if (empty($folder->intro)) { 468 $folder->intro = ''; 469 } 470 if (empty($folder->introformat)) { 471 $folder->introformat = FORMAT_MOODLE; 472 } 473 // display folder 474 $renderer = $PAGE->get_renderer('mod_folder'); 475 $cm->set_content($renderer->display_folder($folder), true); 476 } 477 } 478 479 /** 480 * Mark the activity completed (if required) and trigger the course_module_viewed event. 481 * 482 * @param stdClass $folder folder object 483 * @param stdClass $course course object 484 * @param stdClass $cm course module object 485 * @param stdClass $context context object 486 * @since Moodle 3.0 487 */ 488 function folder_view($folder, $course, $cm, $context) { 489 490 // Trigger course_module_viewed event. 491 $params = array( 492 'context' => $context, 493 'objectid' => $folder->id 494 ); 495 496 $event = \mod_folder\event\course_module_viewed::create($params); 497 $event->add_record_snapshot('course_modules', $cm); 498 $event->add_record_snapshot('course', $course); 499 $event->add_record_snapshot('folder', $folder); 500 $event->trigger(); 501 502 // Completion. 503 $completion = new completion_info($course); 504 $completion->set_module_viewed($cm); 505 } 506 507 /** 508 * Check if the folder can be zipped and downloaded. 509 * @param stdClass $folder 510 * @param context_module $cm 511 * @return bool True if the folder can be zipped and downloaded. 512 * @throws \dml_exception 513 */ 514 function folder_archive_available($folder, $cm) { 515 if (!$folder->showdownloadfolder) { 516 return false; 517 } 518 519 $context = context_module::instance($cm->id); 520 $fs = get_file_storage(); 521 $dir = $fs->get_area_tree($context->id, 'mod_folder', 'content', 0); 522 523 $size = folder_get_directory_size($dir); 524 $maxsize = get_config('folder', 'maxsizetodownload') * 1024 * 1024; 525 526 if ($size == 0) { 527 return false; 528 } 529 530 if (!empty($maxsize) && $size > $maxsize) { 531 return false; 532 } 533 534 return true; 535 } 536 537 /** 538 * Recursively measure the size of the files in a directory. 539 * @param array $directory 540 * @return int size of directory contents in bytes 541 */ 542 function folder_get_directory_size($directory) { 543 $size = 0; 544 545 foreach ($directory['files'] as $file) { 546 $size += $file->get_filesize(); 547 } 548 549 foreach ($directory['subdirs'] as $subdirectory) { 550 $size += folder_get_directory_size($subdirectory); 551 } 552 553 return $size; 554 } 555 556 /** 557 * Mark the activity completed (if required) and trigger the all_files_downloaded event. 558 * 559 * @param stdClass $folder folder object 560 * @param stdClass $course course object 561 * @param stdClass $cm course module object 562 * @param stdClass $context context object 563 * @since Moodle 3.1 564 */ 565 function folder_downloaded($folder, $course, $cm, $context) { 566 $params = array( 567 'context' => $context, 568 'objectid' => $folder->id 569 ); 570 $event = \mod_folder\event\all_files_downloaded::create($params); 571 $event->add_record_snapshot('course_modules', $cm); 572 $event->add_record_snapshot('course', $course); 573 $event->add_record_snapshot('folder', $folder); 574 $event->trigger(); 575 576 // Completion. 577 $completion = new completion_info($course); 578 $completion->set_module_viewed($cm); 579 } 580 581 /** 582 * Returns all uploads since a given time in specified folder. 583 * 584 * @param array $activities 585 * @param int $index 586 * @param int $timestart 587 * @param int $courseid 588 * @param int $cmid 589 * @param int $userid 590 * @param int $groupid not used, but required for compatibilty with other modules 591 */ 592 function folder_get_recent_mod_activity(&$activities, &$index, $timestart, $courseid, $cmid, $userid=0, $groupid=0) { 593 global $DB, $OUTPUT; 594 595 $modinfo = get_fast_modinfo($courseid); 596 $cm = $modinfo->cms[$cmid]; 597 598 $context = context_module::instance($cm->id); 599 if (!has_capability('mod/folder:view', $context)) { 600 return; 601 } 602 $instance = $DB->get_record('folder', ['id' => $cm->instance], '*', MUST_EXIST); 603 604 $files = folder_get_recent_activity($context, $timestart, $userid); 605 foreach ($files as $file) { 606 $tmpactivity = (object) [ 607 'type' => 'folder', 608 'cmid' => $cm->id, 609 'sectionnum' => $cm->sectionnum, 610 'timestamp' => $file->get_timemodified(), 611 'user' => core_user::get_user($file->get_userid()), 612 ]; 613 614 $url = moodle_url::make_pluginfile_url( 615 $file->get_contextid(), 616 'mod_folder', 617 'content', 618 $file->get_itemid(), 619 $file->get_filepath(), 620 $file->get_filename(), 621 !empty($instance->forcedownload) 622 ); 623 624 if (file_extension_in_typegroup($file->get_filename(), 'web_image')) { 625 $image = $url->out(false, array('preview' => 'tinyicon', 'oid' => $file->get_timemodified())); 626 $image = html_writer::empty_tag('img', array('src' => $image)); 627 } else { 628 $image = $OUTPUT->pix_icon(file_file_icon($file, 24), $file->get_filename(), 'moodle'); 629 } 630 631 $tmpactivity->content = (object) [ 632 'image' => $image, 633 'filename' => $file->get_filename(), 634 'url' => $url, 635 ]; 636 637 $activities[$index++] = $tmpactivity; 638 } 639 } 640 641 /** 642 * Outputs the folder uploads indicated by $activity. 643 * 644 * @param object $activity the activity object the folder resides in 645 * @param int $courseid the id of the course the folder resides in 646 * @param bool $detail not used, but required for compatibilty with other modules 647 * @param int $modnames not used, but required for compatibilty with other modules 648 * @param bool $viewfullnames not used, but required for compatibilty with other modules 649 */ 650 function folder_print_recent_mod_activity($activity, $courseid, $detail, $modnames, $viewfullnames) { 651 global $OUTPUT; 652 653 $content = $activity->content; 654 $tableoptions = [ 655 'border' => '0', 656 'cellpadding' => '3', 657 'cellspacing' => '0' 658 ]; 659 $output = html_writer::start_tag('table', $tableoptions); 660 $output .= html_writer::start_tag('tr'); 661 $output .= html_writer::tag('td', $content->image, ['class' => 'fp-icon', 'valign' => 'top']); 662 $output .= html_writer::start_tag('td'); 663 $output .= html_writer::start_div('fp-filename'); 664 $output .= html_writer::link($content->url, $content->filename); 665 $output .= html_writer::end_div(); 666 667 // Show the uploader. 668 $fullname = fullname($activity->user, $viewfullnames); 669 $userurl = new moodle_url('/user/view.php'); 670 $userurl->params(['id' => $activity->user->id, 'course' => $courseid]); 671 $by = new stdClass(); 672 $by->name = html_writer::link($userurl, $fullname); 673 $by->date = userdate($activity->timestamp); 674 $authornamedate = get_string('bynameondate', 'folder', $by); 675 $output .= html_writer::div($authornamedate, 'user'); 676 677 // Finish up the table. 678 $output .= html_writer::end_tag('tr'); 679 $output .= html_writer::end_tag('table'); 680 681 echo $output; 682 } 683 684 /** 685 * Gets recent file uploads in a given folder. Does not perform security checks. 686 * 687 * @param object $context 688 * @param int $timestart 689 * @param int $userid 690 * 691 * @return array 692 */ 693 function folder_get_recent_activity($context, $timestart, $userid=0) { 694 $newfiles = array(); 695 $fs = get_file_storage(); 696 $files = $fs->get_area_files($context->id, 'mod_folder', 'content'); 697 foreach ($files as $file) { 698 if ($file->get_timemodified() <= $timestart) { 699 continue; 700 } 701 if ($file->get_filename() === '.') { 702 continue; 703 } 704 if (!empty($userid) && $userid !== $file->get_userid()) { 705 continue; 706 } 707 $newfiles[] = $file; 708 } 709 return $newfiles; 710 } 711 712 /** 713 * Given a course and a date, prints a summary of all the new 714 * files posted in folder resources since that date 715 * 716 * @uses CONTEXT_MODULE 717 * @param object $course 718 * @param bool $viewfullnames capability 719 * @param int $timestart 720 * @return bool success 721 */ 722 function folder_print_recent_activity($course, $viewfullnames, $timestart) { 723 global $OUTPUT; 724 725 $folders = get_all_instances_in_course('folder', $course); 726 727 if (empty($folders)) { 728 return false; 729 } 730 731 // The list of all new files. 732 $newfiles = []; 733 // Save the force download setting of all instances with files indexed by context. 734 $forcedownloads = []; 735 736 $modinfo = get_fast_modinfo($course); 737 foreach ($folders as $folder) { 738 // Skip resources if the user can't view them. 739 $cm = $modinfo->cms[$folder->coursemodule]; 740 $context = context_module::instance($cm->id); 741 if (!has_capability('mod/folder:view', $context)) { 742 continue; 743 } 744 745 // Get the files uploaded in the current time frame. 746 $newfiles = array_merge($newfiles, folder_get_recent_activity($context, $timestart)); 747 if (!isset($forcedownloads[$context->id])) { 748 $forcedownloads[$context->id] = !empty($folder->forcedownload); 749 } 750 } 751 752 if (empty($newfiles)) { 753 return false; 754 } 755 756 // Build list of files. 757 echo $OUTPUT->heading(get_string('newfoldercontent', 'folder') . ':', 6); 758 $list = html_writer::start_tag('ul', ['class' => 'unlist']); 759 foreach ($newfiles as $file) { 760 $filename = $file->get_filename(); 761 $contextid = $file->get_contextid(); 762 $url = moodle_url::make_pluginfile_url( 763 $contextid, 764 'mod_folder', 765 'content', 766 $file->get_itemid(), 767 $file->get_filepath(), $filename, 768 $forcedownloads[$contextid] ?? false 769 ); 770 771 $list .= html_writer::start_tag('li'); 772 $list .= html_writer::start_div('head'); 773 $list .= html_writer::div(userdate($file->get_timemodified(), get_string('strftimerecent')), 'date'); 774 $list .= html_writer::div($file->get_author(), 'name'); 775 $list .= html_writer::end_div(); // Head. 776 777 $list .= html_writer::start_div('info'); 778 $list .= html_writer::link($url, $filename); 779 $list .= html_writer::end_div(); // Info. 780 $list .= html_writer::end_tag('li'); 781 } 782 $list .= html_writer::end_tag('ul'); 783 echo $list; 784 return true; 785 } 786 787 /** 788 * Check if the module has any update that affects the current user since a given time. 789 * 790 * @param cm_info $cm course module data 791 * @param int $from the time to check updates from 792 * @param array $filter if we need to check only specific updates 793 * @return stdClass an object with the different type of areas indicating if they were updated or not 794 * @since Moodle 3.2 795 */ 796 function folder_check_updates_since(cm_info $cm, $from, $filter = array()) { 797 $updates = course_check_module_updates_since($cm, $from, array('content'), $filter); 798 return $updates; 799 } 800 801 /** 802 * This function receives a calendar event and returns the action associated with it, or null if there is none. 803 * 804 * This is used by block_myoverview in order to display the event appropriately. If null is returned then the event 805 * is not displayed on the block. 806 * 807 * @param calendar_event $event 808 * @param \core_calendar\action_factory $factory 809 * @param int $userid User id to use for all capability checks, etc. Set to 0 for current user (default). 810 * @return \core_calendar\local\event\entities\action_interface|null 811 */ 812 function mod_folder_core_calendar_provide_event_action(calendar_event $event, 813 \core_calendar\action_factory $factory, 814 int $userid = 0) { 815 global $USER; 816 817 if (!$userid) { 818 $userid = $USER->id; 819 } 820 821 $cm = get_fast_modinfo($event->courseid, $userid)->instances['folder'][$event->instance]; 822 823 if (!$cm->uservisible) { 824 // The module is not visible to the user for any reason. 825 return null; 826 } 827 828 $completion = new \completion_info($cm->get_course()); 829 830 $completiondata = $completion->get_data($cm, false, $userid); 831 832 if ($completiondata->completionstate != COMPLETION_INCOMPLETE) { 833 return null; 834 } 835 836 return $factory->create_instance( 837 get_string('view'), 838 new \moodle_url('/mod/folder/view.php', ['id' => $cm->id]), 839 1, 840 true 841 ); 842 } 843 844 /** 845 * Given an array with a file path, it returns the itemid and the filepath for the defined filearea. 846 * 847 * @param string $filearea The filearea. 848 * @param array $args The path (the part after the filearea and before the filename). 849 * @return array The itemid and the filepath inside the $args path, for the defined filearea. 850 */ 851 function mod_folder_get_path_from_pluginfile(string $filearea, array $args) : array { 852 // Folder never has an itemid (the number represents the revision but it's not stored in database). 853 array_shift($args); 854 855 // Get the filepath. 856 if (empty($args)) { 857 $filepath = '/'; 858 } else { 859 $filepath = '/' . implode('/', $args) . '/'; 860 } 861 862 return [ 863 'itemid' => 0, 864 'filepath' => $filepath, 865 ]; 866 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body