See Release Notes
Long Term Support Release
Differences Between: [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 namespace mod_data; 18 19 use action_menu; 20 use action_menu_link_secondary; 21 use core\output\checkbox_toggleall; 22 use html_writer; 23 use mod_data\manager; 24 use moodle_url; 25 use pix_icon; 26 use stdClass; 27 use user_picture; 28 use core_user; 29 use portfolio_add_button; 30 use data_portfolio_caller; 31 use comment; 32 use core_tag_tag; 33 34 /** 35 * Class template for database activity 36 * 37 * @package mod_data 38 * @copyright 2022 Ferran Recio <ferran@moodle.com> 39 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 40 */ 41 class template { 42 43 /** @var manager the current instance manager. */ 44 private $manager; 45 46 /** @var stdClass the current instance record. */ 47 private $instance; 48 49 /** @var string the template. */ 50 private $templatecontent; 51 52 /** @var string the template name. */ 53 private $templatename; 54 55 /** @var moodle_url the base url. */ 56 private $baseurl; 57 58 /** @var string the current search if any. */ 59 private $search; 60 61 /** @var bool if ratings must be added. */ 62 private $ratings; 63 64 /** @var bool if comments must be added if not present in the template. */ 65 private $forcecomments; 66 67 /** @var bool if show more option must be added. */ 68 private $showmore; 69 70 /** @var bool if the current user can manage entries. */ 71 private $canmanageentries = null; 72 73 /** @var array if icons HTML. */ 74 private $icons = []; 75 76 /** @var array All template tags (calculated in load_template_tags). */ 77 protected $tags = []; 78 79 /** @var array The mod_data fields. */ 80 protected $fields = []; 81 82 /** 83 * Class contructor. 84 * 85 * See the add_options method for the available display options. 86 * 87 * @param manager $manager the current instance manager 88 * @param string $templatecontent the template string to use 89 * @param array $options an array of extra diplay options 90 * @param array $fields alternative array of fields (for preview presets) 91 */ 92 public function __construct(manager $manager, string $templatecontent, array $options = [], array $fields = null) { 93 $this->manager = $manager; 94 $this->instance = $manager->get_instance(); 95 $this->templatecontent = $templatecontent; 96 97 $context = $manager->get_context(); 98 $this->canmanageentries = has_capability('mod/data:manageentries', $context); 99 $this->icons = $this->get_icons(); 100 $this->fields = $fields ?? $manager->get_fields(); 101 $this->add_options($options); 102 $this->load_template_tags($templatecontent); 103 } 104 105 /** 106 * Create a template class with the default template content. 107 * 108 * @param manager $manager the current instance manager. 109 * @param string $templatename the template name. 110 * @param bool $form whether the fields should be displayed as form instead of data. 111 * @return self The template with the default content (to be displayed when no template is defined). 112 */ 113 public static function create_default_template( 114 manager $manager, 115 string $templatename, 116 bool $form = false 117 ): self { 118 $renderer = $manager->get_renderer(); 119 $content = ''; 120 switch ($templatename) { 121 case 'addtemplate': 122 case 'asearchtemplate': 123 case 'listtemplate': 124 case 'rsstemplate': 125 case 'singletemplate': 126 $template = new \mod_data\output\defaulttemplate($manager->get_fields(), $templatename, $form); 127 $content = $renderer->render_defaulttemplate($template); 128 } 129 130 // Some templates have extra options. 131 $options = self::get_default_display_options($templatename); 132 133 return new self($manager, $content, $options); 134 } 135 136 /** 137 * Get default options for templates. 138 * 139 * For instance, the list template supports the show more button. 140 * 141 * @param string $templatename the template name. 142 * @return array an array of extra diplay options. 143 */ 144 public static function get_default_display_options(string $templatename): array { 145 $options = []; 146 147 if ($templatename === 'singletemplate') { 148 $options['comments'] = true; 149 $options['ratings'] = true; 150 } 151 if ($templatename === 'listtemplate') { 152 // The "Show more" button should be only displayed in the listtemplate. 153 $options['showmore'] = true; 154 } 155 156 return $options; 157 } 158 159 /** 160 * Return the raw template content. 161 * 162 * @return string the template content before parsing 163 */ 164 public function get_template_content(): string { 165 return $this->templatecontent; 166 } 167 168 /** 169 * Add extra display options. 170 * 171 * The extra options are: 172 * - page: the current pagination page 173 * - search: the current search text 174 * - baseurl: an alternative entry url (moodle_url) 175 * - comments: if comments must be added if not present 176 * - ratings: if ratings must be added 177 * 178 * @param array $options the array of options. 179 */ 180 public function add_options(array $options = []) { 181 $cm = $this->manager->get_coursemodule(); 182 $baseurl = $options['baseurl'] ?? new moodle_url('/mod/data/view.php', ['id' => $cm->id]); 183 if (isset($options['page'])) { 184 $baseurl->params([ 185 'page' => $options['page'], 186 ]); 187 } 188 $this->baseurl = $baseurl; 189 190 // Save options. 191 $this->search = $options['search'] ?? null; 192 $this->ratings = $options['ratings'] ?? false; 193 $this->forcecomments = $options['comments'] ?? false; 194 $this->showmore = $options['showmore'] ?? false; 195 $this->templatename = $options['templatename'] ?? 'singletemplate'; 196 } 197 198 /** 199 * Scan the template tags. 200 * 201 * This method detects which tags are used in this template and store them 202 * in the $this->tags attribute. This attribute will be used to determine 203 * which replacements needs to be calculated. 204 * 205 * @param string $templatecontent the current template 206 */ 207 protected function load_template_tags(string $templatecontent) { 208 // Detect action tags. 209 $pattern = '/##(?P<tags>\w+?)##/'; 210 $matches = []; 211 preg_match_all($pattern, $templatecontent, $matches); 212 if (!isset($matches['tags']) || empty($matches['tags'])) { 213 return; 214 } 215 $this->tags = $matches['tags']; 216 } 217 218 /** 219 * Check if a tag is present in the template. 220 * 221 * @param bool $tagname the tag to check (without ##) 222 * @return bool if the tag is present 223 */ 224 public function has_tag(string $tagname): bool { 225 return in_array($tagname, $this->tags); 226 } 227 228 /** 229 * Return the current template name. 230 * 231 * @return string the template name 232 */ 233 public function get_template_name(): string { 234 return $this->templatename; 235 } 236 237 /** 238 * Generate the list of action icons. 239 * 240 * @return pix_icon[] icon name => pix_icon 241 */ 242 protected function get_icons() { 243 $attrs = ['class' => 'iconsmall dataicon']; 244 return [ 245 'edit' => new pix_icon('t/editinline', get_string('edit'), '', $attrs), 246 'delete' => new pix_icon('t/delete', get_string('delete'), '', $attrs), 247 'more' => new pix_icon('t/preview', get_string('more', 'data'), '', $attrs), 248 'approve' => new pix_icon('t/approve', get_string('approve', 'data'), '', $attrs), 249 'disapprove' => new pix_icon('t/block', get_string('disapprove', 'data'), '', $attrs), 250 ]; 251 } 252 253 /** 254 * Return the parsed entry using a template. 255 * 256 * This method apply a template replacing all necessary tags. 257 * 258 * @param array $entries of entres to parse 259 * @return string the entries outputs using the template 260 */ 261 public function parse_entries(array $entries): string { 262 if (empty($entries)) { 263 return ''; 264 } 265 $result = ''; 266 foreach ($entries as $entry) { 267 $result .= $this->parse_entry($entry); 268 } 269 return $result; 270 } 271 272 /** 273 * Parse a single entry. 274 * 275 * @param stdClass $entry the entry to parse 276 * @return string the parsed entry 277 */ 278 private function parse_entry(stdClass $entry): string { 279 if (empty($this->templatecontent)) { 280 return ''; 281 } 282 $context = $this->manager->get_context(); 283 $canmanageentry = data_user_can_manage_entry($entry, $this->instance, $context); 284 285 // Load all replacements for the entry. 286 $fields = $this->get_fields_replacements($entry); 287 $tags = $this->get_tags_replacements($entry, $canmanageentry); 288 $replacements = array_merge($fields, $tags); 289 290 $patterns = array_keys($replacements); 291 $replacement = array_values($replacements); 292 $result = str_ireplace($patterns, $replacement, $this->templatecontent); 293 294 return $this->post_parse($result, $entry); 295 } 296 297 /** 298 * Get all field replacements. 299 * 300 * @param stdClass $entry the entry object 301 * @return array of pattern => replacement 302 */ 303 private function get_fields_replacements(stdClass $entry): array { 304 $result = []; 305 foreach ($this->fields as $field) { 306 // Field value. 307 $pattern = '[[' . $field->field->name . ']]'; 308 $result[$pattern] = highlight( 309 $this->search, 310 $field->display_browse_field($entry->id, $this->templatename) 311 ); 312 // Field id. 313 $pattern = '[[' . $field->field->name . '#id]]'; 314 $result[$pattern] = $field->field->id; 315 } 316 return $result; 317 } 318 319 /** 320 * Get all standard tags replacements. 321 * 322 * @param stdClass $entry the entry object 323 * @param bool $canmanageentry if the current user can manage this entry 324 * @return array of pattern => replacement 325 */ 326 private function get_tags_replacements(stdClass $entry, bool $canmanageentry): array { 327 $result = []; 328 foreach ($this->tags as $tagname) { 329 $methodname = "get_tag_{$tagname}_replacement"; 330 if (method_exists($this, $methodname)) { 331 $pattern = "##$tagname##"; 332 $replacement = $this->$methodname($entry, $canmanageentry); 333 $result[$pattern] = $replacement; 334 } 335 } 336 return $result; 337 } 338 339 /** 340 * Add any extra information to the parsed entry. 341 * 342 * @param string $result the parsed template with the entry data 343 * @param stdClass $entry the entry object 344 * @return string the final parsed template 345 */ 346 private function post_parse(string $result, stdClass $entry): string { 347 if ($this->ratings) { 348 $result .= data_print_ratings($this->instance, $entry, false); 349 } 350 if ($this->forcecomments && strpos($this->templatecontent, '##comments##') === false) { 351 $result .= $this->get_tag_comments_replacement($entry, false); 352 } 353 return $result; 354 } 355 356 /** 357 * Returns the ##edit## tag replacement for an entry. 358 * 359 * @param stdClass $entry the entry object 360 * @param bool $canmanageentry if the current user can manage this entry 361 * @return string the tag replacement 362 */ 363 protected function get_tag_edit_replacement(stdClass $entry, bool $canmanageentry): string { 364 global $OUTPUT; 365 if (!$canmanageentry) { 366 return ''; 367 } 368 $backurl = new moodle_url($this->baseurl, [ 369 'rid' => $entry->id, 370 'mode' => 'single', 371 ]); 372 $url = new moodle_url('/mod/data/edit.php', $this->baseurl->params()); 373 $url->params([ 374 'rid' => $entry->id, 375 'sesskey' => sesskey(), 376 'backto' => urlencode($backurl->out(false)) 377 ]); 378 return html_writer::tag( 379 'span', 380 $OUTPUT->action_icon($url, $this->icons['edit']), 381 ['class' => 'edit'] 382 ); 383 } 384 385 /** 386 * Returns the ##delete## tag replacement for an entry. 387 * 388 * @param stdClass $entry the entry object 389 * @param bool $canmanageentry if the current user can manage this entry 390 * @return string the tag replacement 391 */ 392 protected function get_tag_delete_replacement(stdClass $entry, bool $canmanageentry): string { 393 global $OUTPUT; 394 if (!$canmanageentry) { 395 return ''; 396 } 397 $url = new moodle_url($this->baseurl, [ 398 'delete' => $entry->id, 399 'sesskey' => sesskey(), 400 'mode' => 'single', 401 ]); 402 403 return html_writer::tag( 404 'span', 405 $OUTPUT->action_icon($url, $this->icons['delete']), 406 ['class' => 'delete'] 407 ); 408 } 409 410 /** 411 * Returns the ##more## tag replacement for an entry. 412 * 413 * @param stdClass $entry the entry object 414 * @param bool $canmanageentry if the current user can manage this entry 415 * @return string the tag replacement 416 */ 417 protected function get_tag_more_replacement(stdClass $entry, bool $canmanageentry): string { 418 global $OUTPUT; 419 420 if (!$this->showmore) { 421 return ''; 422 } 423 424 $url = new moodle_url($this->baseurl, [ 425 'rid' => $entry->id, 426 'filter' => 1, 427 ]); 428 return html_writer::tag( 429 'span', 430 $OUTPUT->action_icon($url, $this->icons['more']), 431 ['class' => 'more'] 432 ); 433 } 434 435 /** 436 * Returns the ##moreurl## tag replacement for an entry. 437 * 438 * @param stdClass $entry the entry object 439 * @param bool $canmanageentry if the current user can manage this entry 440 * @return string the tag replacement 441 */ 442 protected function get_tag_moreurl_replacement(stdClass $entry, bool $canmanageentry): string { 443 $url = new moodle_url($this->baseurl, [ 444 'rid' => $entry->id, 445 'filter' => 1, 446 ]); 447 return $url->out(false); 448 } 449 450 /** 451 * Returns the ##delcheck## tag replacement for an entry. 452 * 453 * @param stdClass $entry the entry object 454 * @param bool $canmanageentry if the current user can manage this entry 455 * @return string the tag replacement 456 */ 457 protected function get_tag_delcheck_replacement(stdClass $entry, bool $canmanageentry): string { 458 global $OUTPUT; 459 if (!$this->canmanageentries) { 460 return ''; 461 } 462 $checkbox = new checkbox_toggleall('listview-entries', false, [ 463 'id' => "entry_{$entry->id}", 464 'name' => 'delcheck[]', 465 'classes' => 'recordcheckbox', 466 'value' => $entry->id, 467 ]); 468 return $OUTPUT->render($checkbox); 469 } 470 471 /** 472 * Returns the ##user## tag replacement for an entry. 473 * 474 * @param stdClass $entry the entry object 475 * @param bool $canmanageentry if the current user can manage this entry 476 * @return string the tag replacement 477 */ 478 protected function get_tag_user_replacement(stdClass $entry, bool $canmanageentry): string { 479 $cm = $this->manager->get_coursemodule(); 480 $url = new moodle_url('/user/view.php', [ 481 'id' => $entry->userid, 482 'course' => $cm->course, 483 ]); 484 return html_writer::tag( 485 'a', 486 fullname($entry), 487 ['href' => $url->out(false)] 488 ); 489 } 490 491 /** 492 * Returns the ##userpicture## tag replacement for an entry. 493 * 494 * @param stdClass $entry the entry object 495 * @param bool $canmanageentry if the current user can manage this entry 496 * @return string the tag replacement 497 */ 498 protected function get_tag_userpicture_replacement(stdClass $entry, bool $canmanageentry): string { 499 global $OUTPUT; 500 $cm = $this->manager->get_coursemodule(); 501 $user = user_picture::unalias($entry, null, 'userid'); 502 // If the record didn't come with user data, retrieve the user from database. 503 if (!isset($user->picture)) { 504 $user = core_user::get_user($entry->userid); 505 } 506 return $OUTPUT->user_picture($user, ['courseid' => $cm->course, 'size' => 64]); 507 } 508 509 /** 510 * Returns the ##export## tag replacement for an entry. 511 * 512 * @param stdClass $entry the entry object 513 * @param bool $canmanageentry if the current user can manage this entry 514 * @return string the tag replacement 515 */ 516 protected function get_tag_export_replacement(stdClass $entry, bool $canmanageentry): string { 517 global $CFG; 518 if (empty($CFG->enableportfolios)) { 519 return ''; 520 } 521 // Check the user can export the entry. 522 $cm = $this->manager->get_coursemodule(); 523 $context = $this->manager->get_context(); 524 $canexportall = has_capability('mod/data:exportentry', $context); 525 $canexportown = has_capability('mod/data:exportownentry', $context); 526 if (!$canexportall && !(data_isowner($entry->id) && $canexportown)) { 527 return ''; 528 } 529 // Add the portfolio export button. 530 require_once($CFG->libdir . '/portfoliolib.php'); 531 $button = new portfolio_add_button(); 532 $button->set_callback_options( 533 'data_portfolio_caller', 534 ['id' => $cm->id, 'recordid' => $entry->id], 535 'mod_data' 536 ); 537 list($formats, $files) = data_portfolio_caller::formats($this->fields, $entry); 538 $button->set_formats($formats); 539 $result = $button->to_html(PORTFOLIO_ADD_ICON_LINK); 540 if (is_null($result)) { 541 $result = ''; 542 } 543 return $result; 544 } 545 546 /** 547 * Returns the ##timeadded## tag replacement for an entry. 548 * 549 * @param stdClass $entry the entry object 550 * @param bool $canmanageentry if the current user can manage this entry 551 * @return string the tag replacement 552 */ 553 protected function get_tag_timeadded_replacement(stdClass $entry, bool $canmanageentry): string { 554 return html_writer::tag( 555 'span', 556 userdate($entry->timecreated, get_string('strftimedatemonthabbr', 'langconfig')), 557 ['title' => userdate($entry->timecreated)] 558 ); 559 } 560 561 /** 562 * Returns the ##timemodified## tag replacement for an entry. 563 * 564 * @param stdClass $entry the entry object 565 * @param bool $canmanageentry if the current user can manage this entry 566 * @return string the tag replacement 567 */ 568 protected function get_tag_timemodified_replacement(stdClass $entry, bool $canmanageentry): string { 569 return html_writer::tag( 570 'span', 571 userdate($entry->timemodified, get_string('strftimedatemonthabbr', 'langconfig')), 572 ['title' => userdate($entry->timemodified)] 573 ); 574 } 575 576 /** 577 * Returns the ##approve## tag replacement for an entry. 578 * 579 * @param stdClass $entry the entry object 580 * @param bool $canmanageentry if the current user can manage this entry 581 * @return string the tag replacement 582 */ 583 protected function get_tag_approve_replacement(stdClass $entry, bool $canmanageentry): string { 584 global $OUTPUT; 585 $context = $this->manager->get_context(); 586 if (!has_capability('mod/data:approve', $context) || !$this->instance->approval || $entry->approved) { 587 return ''; 588 } 589 $url = new moodle_url($this->baseurl, [ 590 'approve' => $entry->id, 591 'sesskey' => sesskey(), 592 ]); 593 return html_writer::tag( 594 'span', 595 $OUTPUT->action_icon($url, $this->icons['approve']), 596 ['class' => 'approve'] 597 ); 598 } 599 600 /** 601 * Returns the ##disapprove## tag replacement for an entry. 602 * 603 * @param stdClass $entry the entry object 604 * @param bool $canmanageentry if the current user can manage this entry 605 * @return string the tag replacement 606 */ 607 protected function get_tag_disapprove_replacement(stdClass $entry, bool $canmanageentry): string { 608 global $OUTPUT; 609 $context = $this->manager->get_context(); 610 if (!has_capability('mod/data:approve', $context) || !$this->instance->approval || !$entry->approved) { 611 return ''; 612 } 613 $url = new moodle_url($this->baseurl, [ 614 'disapprove' => $entry->id, 615 'sesskey' => sesskey(), 616 ]); 617 return html_writer::tag( 618 'span', 619 $OUTPUT->action_icon($url, $this->icons['disapprove']), 620 ['class' => 'disapprove'] 621 ); 622 } 623 624 /** 625 * Returns the ##approvalstatus## tag replacement for an entry. 626 * 627 * @param stdClass $entry the entry object 628 * @param bool $canmanageentry if the current user can manage this entry 629 * @return string the tag replacement 630 */ 631 protected function get_tag_approvalstatus_replacement(stdClass $entry, bool $canmanageentry): string { 632 if (!$this->instance->approval) { 633 return ''; 634 } 635 return ($entry->approved) ? '' : html_writer::div(get_string('notapproved', 'data'), 'mod-data-approval-status-badge'); 636 } 637 638 /** 639 * Returns the ##approvalstatusclass## tag replacement for an entry. 640 * 641 * @param stdClass $entry the entry object 642 * @param bool $canmanageentry if the current user can manage this entry 643 * @return string the tag replacement 644 */ 645 protected function get_tag_approvalstatusclass_replacement(stdClass $entry, bool $canmanageentry): string { 646 if (!$this->instance->approval) { 647 return ''; 648 } 649 return ($entry->approved) ? 'approved' : 'notapproved'; 650 } 651 652 /** 653 * Returns the ##comments## tag replacement for an entry. 654 * 655 * @param stdClass $entry the entry object 656 * @param bool $canmanageentry if the current user can manage this entry 657 * @return string the tag replacement 658 */ 659 protected function get_tag_comments_replacement(stdClass $entry, bool $canmanageentry): string { 660 global $CFG; 661 if (empty($CFG->usecomments) || empty($this->instance->comments)) { 662 return ''; 663 } 664 $context = $this->manager->get_context(); 665 require_once($CFG->dirroot . '/comment/lib.php'); 666 list($context, $course, $cm) = get_context_info_array($context->id); 667 $cmdata = (object)[ 668 'context' => $context, 669 'course' => $course, 670 'cm' => $cm, 671 'area' => 'database_entry', 672 'itemid' => $entry->id, 673 'showcount' => true, 674 'component' => 'mod_data', 675 ]; 676 $comment = new comment($cmdata); 677 return $comment->output(true); 678 } 679 680 /** 681 * Returns the ##tags## tag replacement for an entry. 682 * 683 * @param stdClass $entry the entry object 684 * @param bool $canmanageentry if the current user can manage this entry 685 * @return string the tag replacement 686 */ 687 protected function get_tag_tags_replacement(stdClass $entry, bool $canmanageentry): string { 688 global $OUTPUT; 689 if (!core_tag_tag::is_enabled('mod_data', 'data_records')) { 690 return ''; 691 } 692 return $OUTPUT->tag_list( 693 core_tag_tag::get_item_tags('mod_data', 'data_records', $entry->id), 694 '', 695 'data-tags' 696 ); 697 } 698 699 /** 700 * Returns the ##id## tag replacement for an entry. 701 * 702 * @param stdClass $entry the entry object 703 * @param bool $canmanageentry if the current user can manage this entry 704 * @return string the tag replacement 705 */ 706 protected function get_tag_id_replacement(stdClass $entry, bool $canmanageentry): string { 707 return (string) $entry->id; 708 } 709 710 /** 711 * Returns the ##actionsmenu## tag replacement for an entry. 712 * 713 * @param stdClass $entry the entry object 714 * @param bool $canmanageentry if the current user can manage this entry 715 * @return string the tag replacement 716 */ 717 protected function get_tag_actionsmenu_replacement(stdClass $entry, bool $canmanageentry): string { 718 global $OUTPUT, $CFG; 719 720 $actionmenu = new action_menu(); 721 $icon = $OUTPUT->pix_icon('i/menu', get_string('actions')); 722 $actionmenu->set_menu_trigger($icon, 'btn btn-icon d-flex align-items-center justify-content-center'); 723 $actionmenu->set_action_label(get_string('actions')); 724 $actionmenu->attributes['class'] .= ' entry-actionsmenu'; 725 726 // Show more. 727 if ($this->showmore) { 728 $showmoreurl = new moodle_url($this->baseurl, [ 729 'rid' => $entry->id, 730 'filter' => 1, 731 ]); 732 $actionmenu->add(new action_menu_link_secondary( 733 $showmoreurl, 734 null, 735 get_string('showmore', 'mod_data') 736 )); 737 } 738 739 if ($canmanageentry) { 740 // Edit entry. 741 $backurl = new moodle_url($this->baseurl, [ 742 'rid' => $entry->id, 743 'mode' => 'single', 744 ]); 745 $editurl = new moodle_url('/mod/data/edit.php', $this->baseurl->params()); 746 $editurl->params([ 747 'rid' => $entry->id, 748 'sesskey' => sesskey(), 749 'backto' => urlencode($backurl->out(false)) 750 ]); 751 752 $actionmenu->add(new action_menu_link_secondary( 753 $editurl, 754 null, 755 get_string('edit') 756 )); 757 758 // Delete entry. 759 $deleteurl = new moodle_url($this->baseurl, [ 760 'delete' => $entry->id, 761 'sesskey' => sesskey(), 762 'mode' => 'single', 763 ]); 764 765 $actionmenu->add(new action_menu_link_secondary( 766 $deleteurl, 767 null, 768 get_string('delete') 769 )); 770 } 771 772 // Approve/disapprove entry. 773 $context = $this->manager->get_context(); 774 if (has_capability('mod/data:approve', $context) && $this->instance->approval) { 775 if ($entry->approved) { 776 $disapproveurl = new moodle_url($this->baseurl, [ 777 'disapprove' => $entry->id, 778 'sesskey' => sesskey(), 779 ]); 780 $actionmenu->add(new action_menu_link_secondary( 781 $disapproveurl, 782 null, 783 get_string('disapprove', 'mod_data') 784 )); 785 } else { 786 $approveurl = new moodle_url($this->baseurl, [ 787 'approve' => $entry->id, 788 'sesskey' => sesskey(), 789 ]); 790 $actionmenu->add(new action_menu_link_secondary( 791 $approveurl, 792 null, 793 get_string('approve', 'mod_data') 794 )); 795 } 796 } 797 798 // Export entry to portfolio. 799 if (!empty($CFG->enableportfolios)) { 800 // Check the user can export the entry. 801 $cm = $this->manager->get_coursemodule(); 802 $canexportall = has_capability('mod/data:exportentry', $context); 803 $canexportown = has_capability('mod/data:exportownentry', $context); 804 if ($canexportall || (data_isowner($entry->id) && $canexportown)) { 805 // Add the portfolio export button. 806 require_once($CFG->libdir . '/portfoliolib.php'); 807 $button = new portfolio_add_button(); 808 $button->set_callback_options( 809 'data_portfolio_caller', 810 ['id' => $cm->id, 'recordid' => $entry->id], 811 'mod_data' 812 ); 813 $fields = $this->manager->get_fields(); 814 list($formats, $files) = data_portfolio_caller::formats($fields, $entry); 815 $button->set_formats($formats); 816 $exporturl = $button->to_html(PORTFOLIO_ADD_MOODLE_URL); 817 if (!is_null($exporturl)) { 818 $actionmenu->add(new action_menu_link_secondary( 819 $exporturl, 820 null, 821 get_string('addtoportfolio', 'portfolio') 822 )); 823 } 824 } 825 } 826 827 return $OUTPUT->render($actionmenu); 828 } 829 830 /** 831 * Parse the template as if it was for add entry. 832 * 833 * This method is similar to the parse_entry but it uses the display_add_field method 834 * instead of the display_browse_field. 835 * 836 * @param stdClass|null $processeddata the previous process data information. 837 * @param int|null $entryid the possible entry id 838 * @param stdClass|null $entrydata the entry data from a previous form or from a real entry 839 * @return string the add entry HTML content 840 */ 841 public function parse_add_entry( 842 ?stdClass $processeddata = null, 843 ?int $entryid = null, 844 ?stdClass $entrydata = null 845 ): string { 846 $manager = $this->manager; 847 $renderer = $manager->get_renderer(); 848 $templatecontent = $this->templatecontent; 849 850 if (!$processeddata) { 851 $processeddata = (object)[ 852 'generalnotifications' => [], 853 'fieldnotifications' => [], 854 ]; 855 } 856 857 $result = ''; 858 859 foreach ($processeddata->generalnotifications as $notification) { 860 $result .= $renderer->notification($notification); 861 } 862 863 $possiblefields = $manager->get_fields(); 864 $patterns = []; 865 $replacements = []; 866 867 // Then we generate strings to replace. 868 foreach ($possiblefields as $field) { 869 // To skip unnecessary calls to display_add_field(). 870 if (strpos($templatecontent, "[[" . $field->field->name . "]]") !== false) { 871 // Replace the field tag. 872 $patterns[] = "[[" . $field->field->name . "]]"; 873 $errors = ''; 874 $fieldnotifications = $processeddata->fieldnotifications[$field->field->name] ?? []; 875 if (!empty($fieldnotifications)) { 876 foreach ($fieldnotifications as $notification) { 877 $errors .= $renderer->notification($notification); 878 } 879 } 880 $fielddisplay = ''; 881 if ($field->type === 'unknown') { 882 if ($this->canmanageentries) { // Display notification for users that can manage entries. 883 $errors .= $renderer->notification(get_string('missingfieldtype', 'data', 884 (object)['name' => $field->field->name])); 885 } 886 } else { 887 $fielddisplay = $field->display_add_field($entryid, $entrydata); 888 } 889 890 $replacements[] = $errors . $fielddisplay; 891 } 892 893 // Replace the field id tag. 894 $patterns[] = "[[" . $field->field->name . "#id]]"; 895 $replacements[] = 'field_' . $field->field->id; 896 } 897 898 if (core_tag_tag::is_enabled('mod_data', 'data_records')) { 899 $patterns[] = "##tags##"; 900 $replacements[] = data_generate_tag_form($entryid); 901 } 902 903 $result .= str_ireplace($patterns, $replacements, $templatecontent); 904 return $result; 905 } 906 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body