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