Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403] [Versions 39 and 310]
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->customdata) { 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 $COURSE, $DB, $OUTPUT; 593 594 if ($COURSE->id == $courseid) { 595 $course = $COURSE; 596 } else { 597 $course = $DB->get_record('course', array('id' => $courseid)); 598 } 599 600 $modinfo = get_fast_modinfo($course); 601 $cm = $modinfo->cms[$cmid]; 602 603 $context = context_module::instance($cm->id); 604 if (!has_capability('mod/folder:view', $context)) { 605 return; 606 } 607 $files = folder_get_recent_activity($context, $timestart, $userid); 608 609 foreach ($files as $file) { 610 $tmpactivity = new stdClass(); 611 612 $tmpactivity->type = 'folder'; 613 $tmpactivity->cmid = $cm->id; 614 $tmpactivity->sectionnum = $cm->sectionnum; 615 $tmpactivity->timestamp = $file->get_timemodified(); 616 $tmpactivity->user = core_user::get_user($file->get_userid()); 617 618 $tmpactivity->content = new stdClass(); 619 $tmpactivity->content->url = moodle_url::make_pluginfile_url($file->get_contextid(), 'mod_folder', 'content', 620 $file->get_itemid(), $file->get_filepath(), $file->get_filename()); 621 622 if (file_extension_in_typegroup($file->get_filename(), 'web_image')) { 623 $image = $tmpactivity->content->url->out(false, array('preview' => 'tinyicon', 'oid' => $file->get_timemodified())); 624 $image = html_writer::empty_tag('img', array('src' => $image)); 625 } else { 626 $image = $OUTPUT->pix_icon(file_file_icon($file, 24), $file->get_filename(), 'moodle'); 627 } 628 629 $tmpactivity->content->image = $image; 630 $tmpactivity->content->filename = $file->get_filename(); 631 632 $activities[$index++] = $tmpactivity; 633 } 634 635 } 636 637 /** 638 * Outputs the folder uploads indicated by $activity. 639 * 640 * @param object $activity the activity object the folder resides in 641 * @param int $courseid the id of the course the folder resides in 642 * @param bool $detail not used, but required for compatibilty with other modules 643 * @param int $modnames not used, but required for compatibilty with other modules 644 * @param bool $viewfullnames not used, but required for compatibilty with other modules 645 */ 646 function folder_print_recent_mod_activity($activity, $courseid, $detail, $modnames, $viewfullnames) { 647 global $OUTPUT; 648 649 $content = $activity->content; 650 $tableoptions = [ 651 'border' => '0', 652 'cellpadding' => '3', 653 'cellspacing' => '0' 654 ]; 655 $output = html_writer::start_tag('table', $tableoptions); 656 $output .= html_writer::start_tag('tr'); 657 $output .= html_writer::tag('td', $content->image, ['class' => 'fp-icon', 'valign' => 'top']); 658 $output .= html_writer::start_tag('td'); 659 $output .= html_writer::start_div('fp-filename'); 660 $output .= html_writer::link($content->url, $content->filename); 661 $output .= html_writer::end_div(); 662 663 // Show the uploader. 664 $fullname = fullname($activity->user, $viewfullnames); 665 $userurl = new moodle_url('/user/view.php'); 666 $userurl->params(['id' => $activity->user->id, 'course' => $courseid]); 667 $by = new stdClass(); 668 $by->name = html_writer::link($userurl, $fullname); 669 $by->date = userdate($activity->timestamp); 670 $authornamedate = get_string('bynameondate', 'folder', $by); 671 $output .= html_writer::div($authornamedate, 'user'); 672 673 // Finish up the table. 674 $output .= html_writer::end_tag('tr'); 675 $output .= html_writer::end_tag('table'); 676 677 echo $output; 678 } 679 680 /** 681 * Gets recent file uploads in a given folder. Does not perform security checks. 682 * 683 * @param object $context 684 * @param int $timestart 685 * @param int $userid 686 * 687 * @return array 688 */ 689 function folder_get_recent_activity($context, $timestart, $userid=0) { 690 $newfiles = array(); 691 $fs = get_file_storage(); 692 $files = $fs->get_area_files($context->id, 'mod_folder', 'content'); 693 foreach ($files as $file) { 694 if ($file->get_timemodified() <= $timestart) { 695 continue; 696 } 697 if ($file->get_filename() === '.') { 698 continue; 699 } 700 if (!empty($userid) && $userid !== $file->get_userid()) { 701 continue; 702 } 703 $newfiles[] = $file; 704 } 705 return $newfiles; 706 } 707 708 /** 709 * Given a course and a date, prints a summary of all the new 710 * files posted in folder resources since that date 711 * 712 * @uses CONTEXT_MODULE 713 * @param object $course 714 * @param bool $viewfullnames capability 715 * @param int $timestart 716 * @return bool success 717 */ 718 function folder_print_recent_activity($course, $viewfullnames, $timestart) { 719 global $OUTPUT; 720 721 $folders = get_all_instances_in_course('folder', $course); 722 723 if (empty($folders)) { 724 return false; 725 } 726 727 $newfiles = array(); 728 729 $modinfo = get_fast_modinfo($course); 730 foreach ($folders as $folder) { 731 // Skip resources if the user can't view them. 732 $cm = $modinfo->cms[$folder->coursemodule]; 733 $context = context_module::instance($cm->id); 734 if (!has_capability('mod/folder:view', $context)) { 735 continue; 736 } 737 738 // Get the files uploaded in the current time frame. 739 $newfiles = array_merge($newfiles, folder_get_recent_activity($context, $timestart)); 740 } 741 742 if (empty($newfiles)) { 743 return false; 744 } 745 746 // Build list of files. 747 echo $OUTPUT->heading(get_string('newfoldercontent', 'folder') . ':', 6); 748 $list = html_writer::start_tag('ul', ['class' => 'unlist']); 749 foreach ($newfiles as $file) { 750 $filename = $file->get_filename(); 751 $url = moodle_url::make_pluginfile_url($file->get_contextid(), 'mod_folder', 'content', 752 $file->get_itemid(), $file->get_filepath(), $filename); 753 754 $list .= html_writer::start_tag('li'); 755 $list .= html_writer::start_div('head'); 756 $list .= html_writer::div(userdate($file->get_timemodified(), get_string('strftimerecent')), 'date'); 757 $list .= html_writer::div($file->get_author(), 'name'); 758 $list .= html_writer::end_div(); // Head. 759 760 $list .= html_writer::start_div('info'); 761 $list .= html_writer::link($url, $filename); 762 $list .= html_writer::end_div(); // Info. 763 $list .= html_writer::end_tag('li'); 764 } 765 $list .= html_writer::end_tag('ul'); 766 echo $list; 767 return true; 768 } 769 770 /** 771 * Check if the module has any update that affects the current user since a given time. 772 * 773 * @param cm_info $cm course module data 774 * @param int $from the time to check updates from 775 * @param array $filter if we need to check only specific updates 776 * @return stdClass an object with the different type of areas indicating if they were updated or not 777 * @since Moodle 3.2 778 */ 779 function folder_check_updates_since(cm_info $cm, $from, $filter = array()) { 780 $updates = course_check_module_updates_since($cm, $from, array('content'), $filter); 781 return $updates; 782 } 783 784 /** 785 * This function receives a calendar event and returns the action associated with it, or null if there is none. 786 * 787 * This is used by block_myoverview in order to display the event appropriately. If null is returned then the event 788 * is not displayed on the block. 789 * 790 * @param calendar_event $event 791 * @param \core_calendar\action_factory $factory 792 * @param int $userid User id to use for all capability checks, etc. Set to 0 for current user (default). 793 * @return \core_calendar\local\event\entities\action_interface|null 794 */ 795 function mod_folder_core_calendar_provide_event_action(calendar_event $event, 796 \core_calendar\action_factory $factory, 797 int $userid = 0) { 798 global $USER; 799 800 if (!$userid) { 801 $userid = $USER->id; 802 } 803 804 $cm = get_fast_modinfo($event->courseid, $userid)->instances['folder'][$event->instance]; 805 806 if (!$cm->uservisible) { 807 // The module is not visible to the user for any reason. 808 return null; 809 } 810 811 $completion = new \completion_info($cm->get_course()); 812 813 $completiondata = $completion->get_data($cm, false, $userid); 814 815 if ($completiondata->completionstate != COMPLETION_INCOMPLETE) { 816 return null; 817 } 818 819 return $factory->create_instance( 820 get_string('view'), 821 new \moodle_url('/mod/folder/view.php', ['id' => $cm->id]), 822 1, 823 true 824 ); 825 } 826 827 /** 828 * Given an array with a file path, it returns the itemid and the filepath for the defined filearea. 829 * 830 * @param string $filearea The filearea. 831 * @param array $args The path (the part after the filearea and before the filename). 832 * @return array The itemid and the filepath inside the $args path, for the defined filearea. 833 */ 834 function mod_folder_get_path_from_pluginfile(string $filearea, array $args) : array { 835 // Folder never has an itemid (the number represents the revision but it's not stored in database). 836 array_shift($args); 837 838 // Get the filepath. 839 if (empty($args)) { 840 $filepath = '/'; 841 } else { 842 $filepath = '/' . implode('/', $args) . '/'; 843 } 844 845 return [ 846 'itemid' => 0, 847 'filepath' => $filepath, 848 ]; 849 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body