1682 lines
62 KiB
PHP
1682 lines
62 KiB
PHP
<?php
|
|
// This file is part of Moodle - http://moodle.org/
|
|
//
|
|
// Moodle is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// Moodle is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
namespace core_course;
|
|
|
|
use ArrayIterator;
|
|
use core\exception\coding_exception;
|
|
use IteratorAggregate;
|
|
use stdClass;
|
|
use Traversable;
|
|
use core\url;
|
|
use core\output\core_renderer;
|
|
use core\output\renderer_base;
|
|
use core\lang_string;
|
|
use section_info;
|
|
use core\context\module as context_module;
|
|
use core\context\course as context_course;
|
|
use core_courseformat\output\activitybadge;
|
|
use core\component;
|
|
use core\output\html_writer;
|
|
|
|
/**
|
|
* Data about a single module on a course.
|
|
*
|
|
* This contains most of the fields in the course_modules table, plus additional data when required.
|
|
*
|
|
* The object can be accessed by core or any plugin (i.e. course format, block, filter, etc.) as
|
|
* get_fast_modinfo($courseorid)->cms[$coursemoduleid]
|
|
* or
|
|
* get_fast_modinfo($courseorid)->instances[$moduletype][$instanceid]
|
|
*
|
|
* There are three stages when activity module can add/modify data in this object:
|
|
*
|
|
* <b>Stage 1 - during building the cache.</b>
|
|
* Allows to add to the course cache static user-independent information about the module.
|
|
* Modules should try to include only absolutely necessary information that may be required
|
|
* when displaying course view page. The information is stored in application-level cache
|
|
* and reset when {@see rebuild_course_cache()} is called or cache is purged by admin.
|
|
*
|
|
* Modules can implement callback XXX_get_coursemodule_info() returning instance of object
|
|
* {@see cached_cm_info}
|
|
*
|
|
* <b>Stage 2 - dynamic data.</b>
|
|
* Dynamic data is user-dependent, it is stored in request-level cache. To reset this cache
|
|
* {@see get_fast_modinfo()} with $reset argument may be called.
|
|
*
|
|
* Dynamic data is obtained when any of the following properties/methods is requested:
|
|
* - {@see cm_info::$url}
|
|
* - {@see cm_info::$name}
|
|
* - {@see cm_info::$onclick}
|
|
* - {@see cm_info::get_icon_url()}
|
|
* - {@see cm_info::$uservisible}
|
|
* - {@see cm_info::$available}
|
|
* - {@see cm_info::$availableinfo}
|
|
* - plus any of the properties listed in Stage 3.
|
|
*
|
|
* Modules can implement callback <b>XXX_cm_info_dynamic()</b> and inside this callback they
|
|
* are allowed to use any of the following set methods:
|
|
* - {@see cm_info::set_available()}
|
|
* - {@see cm_info::set_name()}
|
|
* - {@see cm_info::set_no_view_link()}
|
|
* - {@see cm_info::set_user_visible()}
|
|
* - {@see cm_info::set_on_click()}
|
|
* - {@see cm_info::set_icon_url()}
|
|
* - {@see cm_info::override_customdata()}
|
|
* Any methods affecting view elements can also be set in this callback.
|
|
*
|
|
* <b>Stage 3 (view data).</b>
|
|
* Also user-dependend data stored in request-level cache. Second stage is created
|
|
* because populating the view data can be expensive as it may access much more
|
|
* Moodle APIs such as filters, user information, output renderers and we
|
|
* don't want to request it until necessary.
|
|
* View data is obtained when any of the following properties/methods is requested:
|
|
* - {@see cm_info::$afterediticons}
|
|
* - {@see cm_info::$content}
|
|
* - {@see cm_info::get_formatted_content()}
|
|
* - {@see cm_info::$extraclasses}
|
|
* - {@see cm_info::$afterlink}
|
|
*
|
|
* Modules can implement callback <b>XXX_cm_info_view()</b> and inside this callback they
|
|
* are allowed to use any of the following set methods:
|
|
* - {@see cm_info::set_after_edit_icons()}
|
|
* - {@see cm_info::set_after_link()}
|
|
* - {@see cm_info::set_content()}
|
|
* - {@see cm_info::set_extra_classes()}
|
|
*
|
|
* @package core_course
|
|
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
* @copyright Sam Marshall
|
|
* @property-read int $id Course-module ID - from course_modules table
|
|
* @property-read int $instance Module instance (ID within module table) - from course_modules table
|
|
* @property-read int $course Course ID - from course_modules table
|
|
* @property-read string $idnumber 'ID number' from course-modules table (arbitrary text set by user) - from
|
|
* course_modules table
|
|
* @property-read int $added Time that this course-module was added (unix time) - from course_modules table
|
|
* @property-read int $visible Visible setting (0 or 1; if this is 0, students cannot see/access the activity) - from
|
|
* course_modules table
|
|
* @property-read int $visibleoncoursepage Visible on course page setting - from course_modules table, adjusted to
|
|
* whether course format allows this module to have the "stealth" mode
|
|
* @property-read int $visibleold Old visible setting (if the entire section is hidden, the previous value for
|
|
* visible is stored in this field) - from course_modules table
|
|
* @property-read int $groupmode Group mode (one of the constants NOGROUPS, SEPARATEGROUPS, or VISIBLEGROUPS) - from
|
|
* course_modules table. Use {@see cm_info::$effectivegroupmode} to find the actual group mode that may be forced by course.
|
|
* @property-read int $groupingid Grouping ID (0 = all groupings)
|
|
* @property-read bool $coursegroupmodeforce Indicates whether the course containing the module has forced the groupmode
|
|
* This means that cm_info::$groupmode should be ignored and cm_info::$coursegroupmode be used instead
|
|
* @property-read int $coursegroupmode Group mode (one of the constants NOGROUPS, SEPARATEGROUPS, or VISIBLEGROUPS) - from
|
|
* course table - as specified for the course containing the module
|
|
* Effective only if {@see cm_info::$coursegroupmodeforce} is set
|
|
* @property-read int $effectivegroupmode Effective group mode for this module (one of the constants NOGROUPS, SEPARATEGROUPS,
|
|
* or VISIBLEGROUPS). This can be different from groupmode set for the module if the groupmode is forced for the course.
|
|
* This value will always be NOGROUPS if module type does not support group mode.
|
|
* @property-read int $indent Indent level on course page (0 = no indent) - from course_modules table
|
|
* @property-read int $completion Activity completion setting for this activity, COMPLETION_TRACKING_xx constant - from
|
|
* course_modules table
|
|
* @property-read mixed $completiongradeitemnumber Set to the item number (usually 0) if completion depends on a particular
|
|
* grade of this activity, or null if completion does not depend on a grade - from course_modules table
|
|
* @property-read int $completionview 1 if 'on view' completion is enabled, 0 otherwise - from course_modules table
|
|
* @property-read int $completionexpected Set to a unix time if completion of this activity is expected at a
|
|
* particular time, 0 if no time set - from course_modules table
|
|
* @property-read string $availability Availability information as JSON string or null if none -
|
|
* from course_modules table
|
|
* @property-read int $showdescription Controls whether the description of the activity displays on the course main page (in
|
|
* addition to anywhere it might display within the activity itself). 0 = do not show
|
|
* on main page, 1 = show on main page.
|
|
* @property-read string $extra (deprecated) Extra HTML that is put in an unhelpful part of the HTML when displaying this module in
|
|
* course page - from cached data in modinfo field. Deprecated, replaced by ->extraclasses and ->onclick
|
|
* @property-read string $icon Name of icon to use - from cached data in modinfo field
|
|
* @property-read string $iconcomponent Component that contains icon - from cached data in modinfo field
|
|
* @property-read string $modname Name of module e.g. 'forum' (this is the same name as the module's main database
|
|
* table) - from cached data in modinfo field
|
|
* @property-read int $module ID of module type - from course_modules table
|
|
* @property-read string $name Name of module instance for display on page e.g. 'General discussion forum' - from cached
|
|
* data in modinfo field
|
|
* @property-read int $sectionnum Section number that this course-module is in (section 0 = above the calendar, section 1
|
|
* = week/topic 1, etc) - from cached data in modinfo field
|
|
* @property-read int $sectionid Section id - from course_modules table
|
|
* @property-read array $conditionscompletion Availability conditions for this course-module based on the completion of other
|
|
* course-modules (array from other course-module id to required completion state for that
|
|
* module) - from cached data in modinfo field
|
|
* @property-read array $conditionsgrade Availability conditions for this course-module based on course grades (array from
|
|
* grade item id to object with ->min, ->max fields) - from cached data in modinfo field
|
|
* @property-read array $conditionsfield Availability conditions for this course-module based on user fields
|
|
* @property-read bool $available True if this course-module is available to students i.e. if all availability conditions
|
|
* are met - obtained dynamically
|
|
* @property-read string $availableinfo If course-module is not available to students, this string gives information about
|
|
* availability which can be displayed to students and/or staff (e.g. 'Available from 3
|
|
* January 2010') for display on main page - obtained dynamically
|
|
* @property-read bool $uservisible True if this course-module is available to the CURRENT user (for example, if current user
|
|
* has viewhiddenactivities capability, they can access the course-module even if it is not
|
|
* visible or not available, so this would be true in that case)
|
|
* @property-read context_module $context Module context
|
|
* @property-read string $modfullname Returns a localised human-readable name of the module type - calculated on request
|
|
* @property-read string $modplural Returns a localised human-readable name of the module type in plural form
|
|
* Calculated on request
|
|
* @property-read string $content Content to display on main (view) page - calculated on request
|
|
* @property-read url|null $url URL to link to for this module, or null if it doesn't have a view page - calculated on request
|
|
* @property-read string $extraclasses Extra CSS classes to add to html output for this activity on main page
|
|
* C'alculated on request
|
|
* @property-read string $onclick Content of HTML on-click attribute already escaped - calculated on request
|
|
* @property-read mixed $customdata Optional custom data stored in modinfo cache for this activity, or null if none
|
|
* @property-read string $afterlink Extra HTML code to display after link - calculated on request
|
|
* @property-read string $afterediticons Extra HTML code to display after editing icons (e.g. more icons) - calculated on request
|
|
* @property-read bool $deletioninprogress True if this course module is scheduled for deletion, false otherwise.
|
|
* @property-read bool $downloadcontent True if content download is enabled for this course module, false otherwise.
|
|
* @property-read bool $lang the forced language for this activity (language pack name). Null means not forced.
|
|
* @property-read int|null $enableaitools AI tools for course_modules table
|
|
* @property-read string|null $enabledaiactions AI actions for course_modules table
|
|
*/
|
|
class cm_info implements IteratorAggregate {
|
|
/**
|
|
* @var int State: Only basic data from modinfo cache is available.
|
|
*/
|
|
private const STATE_BASIC = 0;
|
|
|
|
/**
|
|
* @var int State: In the process of building dynamic data (to avoid recursive calls to obtain_dynamic_data())
|
|
*/
|
|
private const STATE_BUILDING_DYNAMIC = 1;
|
|
|
|
/**
|
|
* @var int State: Dynamic data is available too.
|
|
*/
|
|
private const STATE_DYNAMIC = 2;
|
|
|
|
/**
|
|
* @var int State: In the process of building view data (to avoid recursive calls to obtain_view_data())
|
|
*/
|
|
private const STATE_BUILDING_VIEW = 3;
|
|
|
|
/**
|
|
* @var int State: View data (for course page) is available.
|
|
*/
|
|
private const STATE_VIEW = 4;
|
|
|
|
/** @var modinfo Parent object */
|
|
private $modinfo;
|
|
|
|
/**
|
|
* Level of information stored inside this object (STATE_xx constant)
|
|
* @var int
|
|
*/
|
|
private $state;
|
|
|
|
/**
|
|
* Course-module ID - from course_modules table
|
|
* @var int
|
|
*/
|
|
private $id;
|
|
|
|
/**
|
|
* Module instance (ID within module table) - from course_modules table
|
|
* @var int
|
|
*/
|
|
private $instance;
|
|
|
|
/**
|
|
* 'ID number' from course-modules table (arbitrary text set by user) - from
|
|
* course_modules table
|
|
* @var string
|
|
*/
|
|
private $idnumber;
|
|
|
|
/**
|
|
* Time that this course-module was added (unix time) - from course_modules table
|
|
* @var int
|
|
*/
|
|
private $added;
|
|
|
|
/**
|
|
* @var int
|
|
*
|
|
* This variable is not used and is included here only so it can be documented.
|
|
* Once the database entry is removed from course_modules, it should be deleted
|
|
* here too.
|
|
* @deprecated Do not use this variable
|
|
*/
|
|
#[\core\attribute\deprecated(
|
|
since: '2.0',
|
|
mdl: 'MDL-26781',
|
|
)]
|
|
private $score;
|
|
|
|
/**
|
|
* Visible setting (0 or 1; if this is 0, students cannot see/access the activity) - from
|
|
* course_modules table
|
|
* @var int
|
|
*/
|
|
private $visible;
|
|
|
|
/**
|
|
* Visible on course page setting - from course_modules table
|
|
* @var int
|
|
*/
|
|
private $visibleoncoursepage;
|
|
|
|
/**
|
|
* Old visible setting (if the entire section is hidden, the previous value for
|
|
* visible is stored in this field) - from course_modules table
|
|
* @var int
|
|
*/
|
|
private $visibleold;
|
|
|
|
/**
|
|
* Group mode (one of the constants NONE, SEPARATEGROUPS, or VISIBLEGROUPS) - from
|
|
* course_modules table
|
|
* @var int
|
|
*/
|
|
private $groupmode;
|
|
|
|
/**
|
|
* Grouping ID (0 = all groupings)
|
|
* @var int
|
|
*/
|
|
private $groupingid;
|
|
|
|
/**
|
|
* Indent level on course page (0 = no indent) - from course_modules table
|
|
* @var int
|
|
*/
|
|
private $indent;
|
|
|
|
/**
|
|
* Activity completion setting for this activity, COMPLETION_TRACKING_xx constant - from
|
|
* course_modules table
|
|
* @var int
|
|
*/
|
|
private $completion;
|
|
|
|
/**
|
|
* Set to the item number (usually 0) if completion depends on a particular
|
|
* grade of this activity, or null if completion does not depend on a grade - from
|
|
* course_modules table
|
|
* @var mixed
|
|
*/
|
|
private $completiongradeitemnumber;
|
|
|
|
/**
|
|
* 1 if pass grade completion is enabled, 0 otherwise - from course_modules table
|
|
* @var int
|
|
*/
|
|
private $completionpassgrade;
|
|
|
|
/**
|
|
* 1 if 'on view' completion is enabled, 0 otherwise - from course_modules table
|
|
* @var int
|
|
*/
|
|
private $completionview;
|
|
|
|
/** @var int Set to a unix time if completion of this activity is expected at a particular time, 0 if no time set - from course_modules table */
|
|
private $completionexpected;
|
|
|
|
/** @var string Availability information as JSON string or null if none - from course_modules table */
|
|
private $availability;
|
|
|
|
/**
|
|
* @var int Whether the description of this activity is displayed on the course main page.
|
|
*
|
|
* Note: This would be in addition to anywhere it might display within the activity itself.
|
|
*
|
|
* 0 = do not show on main page, 1 = show on main page.
|
|
*/
|
|
private $showdescription;
|
|
|
|
/**
|
|
* @var string Extra HTML
|
|
*
|
|
* Extra HTML that is put in an unhelpful part of the HTML when displaying this module in
|
|
* course page - from cached data in modinfo field
|
|
* @deprecated This is crazy, don't use it. Replaced by ->extraclasses and ->onclick
|
|
*/
|
|
#[\core\attribute\deprecated(
|
|
replacement: '->extraclasses and ->onclick',
|
|
since: '2.0',
|
|
mdl: 'MDL-25981',
|
|
reason: 'This is crazy, don\'t use it.'
|
|
)]
|
|
private $extra;
|
|
|
|
/**
|
|
* Name of icon to use - from cached data in modinfo field
|
|
* @var string
|
|
*/
|
|
private $icon;
|
|
|
|
/**
|
|
* Component that contains icon - from cached data in modinfo field
|
|
* @var string
|
|
*/
|
|
private $iconcomponent;
|
|
|
|
/**
|
|
* The instance record form the module table
|
|
* @var stdClass
|
|
*/
|
|
private $instancerecord;
|
|
|
|
/**
|
|
* Name of module e.g. 'forum' (this is the same name as the module's main database
|
|
* table) - from cached data in modinfo field
|
|
* @var string
|
|
*/
|
|
private $modname;
|
|
|
|
/**
|
|
* ID of module - from course_modules table
|
|
* @var int
|
|
*/
|
|
private $module;
|
|
|
|
/**
|
|
* Name of module instance for display on page e.g. 'General discussion forum' - from cached
|
|
* data in modinfo field
|
|
* @var string
|
|
*/
|
|
private $name;
|
|
|
|
/**
|
|
* Section number that this course-module is in (section 0 = above the calendar, section 1
|
|
* = week/topic 1, etc) - from cached data in modinfo field
|
|
* @var int
|
|
*/
|
|
private $sectionnum;
|
|
|
|
/**
|
|
* Section id - from course_modules table
|
|
* @var int
|
|
*/
|
|
private $sectionid;
|
|
|
|
/**
|
|
* Availability conditions for this course-module based on the completion of other
|
|
* course-modules (array from other course-module id to required completion state for that
|
|
* module) - from cached data in modinfo field
|
|
* @var array
|
|
*/
|
|
private $conditionscompletion;
|
|
|
|
/**
|
|
* Availability conditions for this course-module based on course grades (array from
|
|
* grade item id to object with ->min, ->max fields) - from cached data in modinfo field
|
|
* @var array
|
|
*/
|
|
private $conditionsgrade;
|
|
|
|
/**
|
|
* Availability conditions for this course-module based on user fields
|
|
* @var array
|
|
*/
|
|
private $conditionsfield;
|
|
|
|
/**
|
|
* True if this course-module is available to students i.e. if all availability conditions
|
|
* are met - obtained dynamically
|
|
* @var bool
|
|
*/
|
|
private $available;
|
|
|
|
/**
|
|
* If course-module is not available to students, this string gives information about
|
|
* availability which can be displayed to students and/or staff (e.g. 'Available from 3
|
|
* January 2010') for display on main page - obtained dynamically
|
|
* @var string
|
|
*/
|
|
private $availableinfo;
|
|
|
|
/**
|
|
* True if this course-module is available to the CURRENT user (for example, if current user
|
|
* has viewhiddenactivities capability, they can access the course-module even if it is not
|
|
* visible or not available, so this would be true in that case)
|
|
* @var bool
|
|
*/
|
|
private $uservisible;
|
|
|
|
/**
|
|
* True if this course-module is visible to the CURRENT user on the course page
|
|
* @var bool
|
|
*/
|
|
private $uservisibleoncoursepage;
|
|
|
|
/**
|
|
* @var url|null The activity URL, if any.
|
|
*/
|
|
private $url;
|
|
|
|
/**
|
|
* @var string
|
|
*/
|
|
private $content;
|
|
|
|
/**
|
|
* @var bool
|
|
*/
|
|
private $contentisformatted;
|
|
|
|
/**
|
|
* @var bool True if the content has a special course item display like labels.
|
|
*/
|
|
private $customcmlistitem;
|
|
|
|
/**
|
|
* @var string
|
|
*/
|
|
private $extraclasses;
|
|
|
|
/**
|
|
* @var url full external url pointing to icon image for activity
|
|
*/
|
|
private $iconurl;
|
|
|
|
/**
|
|
* @var string
|
|
*/
|
|
private $onclick;
|
|
|
|
/**
|
|
* @var mixed
|
|
*/
|
|
private $customdata;
|
|
|
|
/**
|
|
* @var string
|
|
*/
|
|
private $afterlink;
|
|
|
|
/**
|
|
* @var string
|
|
*/
|
|
private $afterediticons;
|
|
|
|
/**
|
|
* @var bool representing the deletion state of the module. True if the mod is scheduled for deletion.
|
|
*/
|
|
private $deletioninprogress;
|
|
|
|
/**
|
|
* @var int enable/disable download content for this course module
|
|
*/
|
|
private $downloadcontent;
|
|
|
|
/**
|
|
* @var string|null the forced language for this activity (language pack name). Null means not forced.
|
|
*/
|
|
private $lang;
|
|
|
|
/**
|
|
* @var int|null enable/disable AI tools for this course module
|
|
*/
|
|
private $enableaitools;
|
|
|
|
/**
|
|
* @var string|null enabled AI actions for this course module
|
|
*/
|
|
private $enabledaiactions;
|
|
|
|
/**
|
|
* List of class read-only properties and their getter methods.
|
|
* Used by magic functions __get(), __isset().
|
|
*
|
|
* @var array
|
|
*/
|
|
private static $standardproperties = [
|
|
'url' => 'get_url',
|
|
'content' => 'get_content',
|
|
'extraclasses' => 'get_extra_classes',
|
|
'onclick' => 'get_on_click',
|
|
'customdata' => 'get_custom_data',
|
|
'afterlink' => 'get_after_link',
|
|
'afterediticons' => 'get_after_edit_icons',
|
|
'modfullname' => 'get_module_type_name',
|
|
'modplural' => 'get_module_type_name_plural',
|
|
'id' => false,
|
|
'added' => false,
|
|
'availability' => false,
|
|
'available' => 'get_available',
|
|
'availableinfo' => 'get_available_info',
|
|
'completion' => false,
|
|
'completionexpected' => false,
|
|
'completiongradeitemnumber' => false,
|
|
'completionpassgrade' => false,
|
|
'completionview' => false,
|
|
'conditionscompletion' => false,
|
|
'conditionsfield' => false,
|
|
'conditionsgrade' => false,
|
|
'context' => 'get_context',
|
|
'course' => 'get_course_id',
|
|
'coursegroupmode' => 'get_course_groupmode',
|
|
'coursegroupmodeforce' => 'get_course_groupmodeforce',
|
|
'customcmlistitem' => 'has_custom_cmlist_item',
|
|
'effectivegroupmode' => 'get_effective_groupmode',
|
|
'extra' => false,
|
|
'groupingid' => false,
|
|
'groupmembersonly' => 'get_deprecated_group_members_only',
|
|
'groupmode' => false,
|
|
'icon' => false,
|
|
'iconcomponent' => false,
|
|
'idnumber' => false,
|
|
'indent' => false,
|
|
'instance' => false,
|
|
'modname' => false,
|
|
'module' => false,
|
|
'name' => 'get_name',
|
|
'score' => false,
|
|
'section' => 'get_section_id',
|
|
'sectionid' => false,
|
|
'sectionnum' => false,
|
|
'showdescription' => false,
|
|
'uservisible' => 'get_user_visible',
|
|
'visible' => false,
|
|
'visibleoncoursepage' => false,
|
|
'visibleold' => false,
|
|
'deletioninprogress' => false,
|
|
'downloadcontent' => false,
|
|
'lang' => false,
|
|
'enableaitools' => false,
|
|
'enabledaiactions' => false,
|
|
];
|
|
|
|
/**
|
|
* List of methods with no arguments that were public prior to Moodle 2.6.
|
|
*
|
|
* They can still be accessed publicly via magic __call() function with no warnings
|
|
* but are not listed in the class methods list.
|
|
* For the consistency of the code it is better to use corresponding properties.
|
|
*
|
|
* These methods be deprecated completely in later versions.
|
|
*
|
|
* @var array $standardmethods
|
|
*/
|
|
private static $standardmethods = [
|
|
// Following methods are not recommended to use because there have associated read-only properties.
|
|
'get_url',
|
|
'get_content',
|
|
'get_extra_classes',
|
|
'get_on_click',
|
|
'get_custom_data',
|
|
'get_after_link',
|
|
'get_after_edit_icons',
|
|
// Method obtain_dynamic_data() should not be called from outside of this class but it was public before Moodle 2.6.
|
|
'obtain_dynamic_data',
|
|
];
|
|
|
|
/**
|
|
* Magic method to call functions that are now declared as private but were public in Moodle before 2.6.
|
|
* These private methods can not be used anymore.
|
|
*
|
|
* @param string $name
|
|
* @param array $arguments
|
|
* @return mixed
|
|
* @throws coding_exception
|
|
*/
|
|
public function __call($name, $arguments) {
|
|
if (in_array($name, self::$standardmethods)) {
|
|
$message = "cm_info::$name() can not be used anymore.";
|
|
if ($alternative = array_search($name, self::$standardproperties)) {
|
|
$message .= " Please use the property cm_info->$alternative instead.";
|
|
}
|
|
throw new coding_exception($message);
|
|
}
|
|
throw new coding_exception("Method cm_info::{$name}() does not exist");
|
|
}
|
|
|
|
/**
|
|
* Magic method getter
|
|
*
|
|
* @param string $name
|
|
* @return mixed
|
|
*/
|
|
public function __get($name) {
|
|
\core\deprecation::emit_deprecation_if_present([__CLASS__, $name]);
|
|
if (isset(self::$standardproperties[$name])) {
|
|
if ($method = self::$standardproperties[$name]) {
|
|
return $this->$method();
|
|
} else {
|
|
return $this->$name;
|
|
}
|
|
} else {
|
|
debugging('Invalid cm_info property accessed: ' . $name);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
#[\Override]
|
|
public function getIterator(): Traversable {
|
|
// Make sure dynamic properties are retrieved prior to view properties.
|
|
$this->obtain_dynamic_data();
|
|
$ret = [];
|
|
|
|
// Do not iterate over deprecated properties.
|
|
$props = self::$standardproperties;
|
|
unset($props['groupmembersonly']);
|
|
|
|
foreach ($props as $key => $unused) {
|
|
if (\core\deprecation::is_deprecated([__CLASS__, $key])) {
|
|
continue;
|
|
}
|
|
|
|
$ret[$key] = $this->__get($key);
|
|
}
|
|
return new ArrayIterator($ret);
|
|
}
|
|
|
|
/**
|
|
* Magic method for function isset()
|
|
*
|
|
* @param string $name
|
|
* @return bool
|
|
*/
|
|
public function __isset($name) {
|
|
\core\deprecation::emit_deprecation_if_present([__CLASS__, $name]);
|
|
if (isset(self::$standardproperties[$name])) {
|
|
$value = $this->__get($name);
|
|
return isset($value);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Magic method setter
|
|
*
|
|
* Will display the developer warning when trying to set/overwrite property.
|
|
*
|
|
* @param string $name
|
|
* @param mixed $value
|
|
*/
|
|
public function __set($name, $value) {
|
|
debugging("It is not allowed to set the property cm_info::\${$name}", DEBUG_DEVELOPER);
|
|
}
|
|
|
|
/**
|
|
* Whether this activity has a view page.
|
|
*
|
|
* Note: modules may still have a view.php file, but return false if this is not intended to be linked to
|
|
* from standard parts of the interface.
|
|
* For an example of these, see `mod_label`.
|
|
*
|
|
* @return bool True if this module has a 'view' page that should be linked to in navigation etc.
|
|
*/
|
|
public function has_view() {
|
|
return !is_null($this->url);
|
|
}
|
|
|
|
/**
|
|
* Gets the URL to link to for this module.
|
|
*
|
|
* This method is normally called by the property ->url, but can be called directly if
|
|
* there is a case when it might be called recursively (you can't call property values
|
|
* recursively).
|
|
*
|
|
* @return url URL to link to for this module, or null if it doesn't have a view page
|
|
*/
|
|
public function get_url() {
|
|
$this->obtain_dynamic_data();
|
|
return $this->url;
|
|
}
|
|
|
|
/**
|
|
* Obtains content to display on main (view) page.
|
|
* Note: Will collect view data, if not already obtained.
|
|
* @return string Content to display on main page below link, or empty string if none
|
|
*/
|
|
private function get_content() {
|
|
$this->obtain_view_data();
|
|
return $this->content;
|
|
}
|
|
|
|
/**
|
|
* Returns the content to display on course/overview page, formatted and passed through filters
|
|
*
|
|
* if $options['context'] is not specified, the module context is used
|
|
*
|
|
* @param array|stdClass $options formatting options, see {@see format_text()}
|
|
* @return string
|
|
*/
|
|
public function get_formatted_content($options = []) {
|
|
$this->obtain_view_data();
|
|
if (empty($this->content)) {
|
|
return '';
|
|
}
|
|
if ($this->contentisformatted) {
|
|
return $this->content;
|
|
}
|
|
|
|
// Improve filter performance by preloading filter setttings for all activities on the course.
|
|
// This does nothing if called multiple times.
|
|
filter_preload_activities($this->get_modinfo());
|
|
|
|
$options = (array)$options;
|
|
if (!isset($options['context'])) {
|
|
$options['context'] = $this->get_context();
|
|
}
|
|
return format_text($this->content, FORMAT_HTML, $options);
|
|
}
|
|
|
|
/**
|
|
* Return the module custom cmlist item flag.
|
|
*
|
|
* Activities like label uses this flag to indicate that it should be
|
|
* displayed as a custom course item instead of a tipical activity card.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function has_custom_cmlist_item(): bool {
|
|
$this->obtain_view_data();
|
|
return $this->customcmlistitem ?? false;
|
|
}
|
|
|
|
/**
|
|
* Getter method for property $name, ensures that dynamic data is obtained.
|
|
*
|
|
* This method is normally called by the property ->name, but can be called directly if there
|
|
* is a case when it might be called recursively (you can't call property values recursively).
|
|
*
|
|
* @return string
|
|
*/
|
|
public function get_name() {
|
|
$this->obtain_dynamic_data();
|
|
return $this->name;
|
|
}
|
|
|
|
/**
|
|
* Returns the name to display on course/overview page, formatted and passed through filters
|
|
*
|
|
* if $options['context'] is not specified, the module context is used
|
|
*
|
|
* @param array|stdClass $options formatting options, see {@see format_string()}
|
|
* @return string
|
|
*/
|
|
public function get_formatted_name($options = []) {
|
|
global $CFG;
|
|
$options = (array)$options;
|
|
if (!isset($options['context'])) {
|
|
$options['context'] = $this->get_context();
|
|
}
|
|
// Improve filter performance by preloading filter setttings for all
|
|
// activities on the course (this does nothing if called multiple
|
|
// times).
|
|
if (!empty($CFG->filterall)) {
|
|
filter_preload_activities($this->get_modinfo());
|
|
}
|
|
return format_string($this->get_name(), true, $options);
|
|
}
|
|
|
|
/**
|
|
* Note: Will collect view data, if not already obtained.
|
|
* @return string Extra CSS classes to add to html output for this activity on main page
|
|
*/
|
|
private function get_extra_classes() {
|
|
$this->obtain_view_data();
|
|
return $this->extraclasses;
|
|
}
|
|
|
|
/**
|
|
* Get the on-click attribute.
|
|
*
|
|
* Note: This string will be used literally as a string so should be pre-escaped.
|
|
*
|
|
* @return string Content of HTML on-click attribute.
|
|
*/
|
|
private function get_on_click() {
|
|
// Does not need view data; may be used by navigation.
|
|
$this->obtain_dynamic_data();
|
|
return $this->onclick;
|
|
}
|
|
/**
|
|
* Getter method for property $customdata, ensures that dynamic data is retrieved.
|
|
*
|
|
* This method is normally called by the property ->customdata, but can be called directly if there
|
|
* is a case when it might be called recursively (you can't call property values recursively).
|
|
*
|
|
* @return mixed Optional custom data stored in modinfo cache for this activity, or null if none
|
|
*/
|
|
public function get_custom_data() {
|
|
$this->obtain_dynamic_data();
|
|
return $this->customdata;
|
|
}
|
|
|
|
/**
|
|
* Note: Will collect view data, if not already obtained.
|
|
* @return string Extra HTML code to display after link
|
|
*/
|
|
private function get_after_link() {
|
|
$this->obtain_view_data();
|
|
return $this->afterlink;
|
|
}
|
|
|
|
/**
|
|
* Get the activity badge data associated to this course module (if the module supports it).
|
|
* Modules can use this method to provide additional data to be displayed in the activity badge.
|
|
*
|
|
* @param renderer_base|null $output Output render to use, or null for default (global)
|
|
* @return stdClass|null The activitybadge data (badgecontent, badgestyle...) or null if the module doesn't implement it.
|
|
*/
|
|
public function get_activitybadge(?renderer_base $output = null): ?stdClass {
|
|
global $OUTPUT;
|
|
|
|
$activibybadgeclass = activitybadge::create_instance($this);
|
|
if (empty($activibybadgeclass)) {
|
|
return null;
|
|
}
|
|
|
|
if (!isset($output)) {
|
|
$output = $OUTPUT;
|
|
}
|
|
|
|
return $activibybadgeclass->export_for_template($output);
|
|
}
|
|
|
|
/**
|
|
* Note: Will collect view data, if not already obtained.
|
|
* @return string Extra HTML code to display after editing icons (e.g. more icons)
|
|
*/
|
|
private function get_after_edit_icons() {
|
|
$this->obtain_view_data();
|
|
return $this->afterediticons;
|
|
}
|
|
|
|
/**
|
|
* Fetch the module's icon URL.
|
|
*
|
|
* This function fetches the course module instance's icon URL.
|
|
* This method adds a `filtericon` parameter in the URL when rendering the monologo version of the course module icon or when
|
|
* the plugin declares, via its `filtericon` custom data, that the icon needs to be filtered.
|
|
* This additional information can be used by plugins when rendering the module icon to determine whether to apply
|
|
* CSS filtering to the icon.
|
|
*
|
|
* @param core_renderer $output Output render to use, or null for default (global)
|
|
* @return url Icon URL for a suitable icon to put beside this cm
|
|
*/
|
|
public function get_icon_url($output = null) {
|
|
global $OUTPUT;
|
|
$this->obtain_dynamic_data();
|
|
if (!$output) {
|
|
$output = $OUTPUT;
|
|
}
|
|
|
|
$ismonologo = false;
|
|
if (!empty($this->iconurl)) {
|
|
// Support modules setting their own, external, icon image.
|
|
$icon = $this->iconurl;
|
|
} else if (!empty($this->icon)) {
|
|
// Fallback to normal local icon + component processing.
|
|
if (substr($this->icon, 0, 4) === 'mod/') {
|
|
[$modname, $iconname] = explode('/', substr($this->icon, 4), 2);
|
|
$icon = $output->image_url($iconname, $modname);
|
|
} else {
|
|
if (!empty($this->iconcomponent)) {
|
|
// Icon has specified component.
|
|
$icon = $output->image_url($this->icon, $this->iconcomponent);
|
|
} else {
|
|
// Icon does not have specified component, use default.
|
|
$icon = $output->image_url($this->icon);
|
|
}
|
|
}
|
|
} else {
|
|
$icon = $output->image_url('monologo', $this->modname);
|
|
// Activity modules may only have an `icon` icon instead of a `monologo` icon.
|
|
// So we need to determine if the module really has a `monologo` icon.
|
|
$ismonologo = component::has_monologo_icon('mod', $this->modname);
|
|
}
|
|
|
|
// Determine whether the icon will be filtered in the CSS.
|
|
// This can be controlled by the module by declaring a 'filtericon' custom data.
|
|
// If the 'filtericon' custom data is not set, icon filtering will be determined whether the module has a `monologo` icon.
|
|
// Additionally, we need to cast custom data to array as some modules may treat it as an object.
|
|
$filtericon = ((array)$this->customdata)['filtericon'] ?? $ismonologo;
|
|
if ($filtericon) {
|
|
$icon->param('filtericon', 1);
|
|
}
|
|
return $icon;
|
|
}
|
|
|
|
/**
|
|
* Get the grouping label
|
|
*
|
|
* @param string $textclasses additional classes for grouping label
|
|
* @return string An empty string or HTML grouping label span tag
|
|
*/
|
|
public function get_grouping_label($textclasses = '') {
|
|
$groupinglabel = '';
|
|
if (
|
|
$this->effectivegroupmode != NOGROUPS
|
|
&& !empty($this->groupingid)
|
|
&& has_capability('moodle/course:managegroups', context_course::instance($this->course))
|
|
) {
|
|
$groupings = groups_get_all_groupings($this->course);
|
|
$groupinglabel = html_writer::tag(
|
|
'span',
|
|
'(' . format_string($groupings[$this->groupingid]->name) . ')',
|
|
['class' => 'groupinglabel ' . $textclasses],
|
|
);
|
|
}
|
|
return $groupinglabel;
|
|
}
|
|
|
|
/**
|
|
* Returns a localised human-readable name of the module type.
|
|
*
|
|
* @param bool $plural If true, the function returns the plural form of the name.
|
|
* @return lang_string|null
|
|
*/
|
|
public function get_module_type_name($plural = false) {
|
|
$modnames = get_module_types_names($plural);
|
|
if (isset($modnames[$this->modname])) {
|
|
return $modnames[$this->modname];
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns a localised human-readable name of the module type in plural form - calculated on request
|
|
*
|
|
* @return string
|
|
*/
|
|
private function get_module_type_name_plural() {
|
|
return $this->get_module_type_name(true);
|
|
}
|
|
|
|
/**
|
|
* Get the modinfo object that this cm_info came from.
|
|
*
|
|
* @return modinfo Modinfo object that this came from
|
|
*/
|
|
public function get_modinfo(): modinfo {
|
|
return $this->modinfo;
|
|
}
|
|
|
|
/**
|
|
* Returns the section this module belongs to
|
|
*
|
|
* @return section_info
|
|
*/
|
|
public function get_section_info() {
|
|
return $this->modinfo->get_section_info_by_id($this->sectionid);
|
|
}
|
|
|
|
/**
|
|
* Getter method for property $section that returns section id.
|
|
*
|
|
* This method is called by the property ->section.
|
|
*
|
|
* @return int
|
|
*/
|
|
private function get_section_id(): int {
|
|
return $this->sectionid;
|
|
}
|
|
|
|
/**
|
|
* Returns course object that was used in the first {@see get_fast_modinfo()} call.
|
|
*
|
|
* It may not contain all fields from DB table {course} but always has at least the following:
|
|
* id,shortname,fullname,format,enablecompletion,groupmode,groupmodeforce,cacherev
|
|
*
|
|
* If the course object lacks the field you need you can use the global
|
|
* function {@see get_course()} that will save extra query if you access
|
|
* current course or frontpage course.
|
|
*
|
|
* @return stdClass
|
|
*/
|
|
public function get_course() {
|
|
return $this->modinfo->get_course();
|
|
}
|
|
|
|
/**
|
|
* Returns course id for which the modinfo was generated.
|
|
*
|
|
* @return int
|
|
*/
|
|
private function get_course_id() {
|
|
return $this->modinfo->get_course_id();
|
|
}
|
|
|
|
/**
|
|
* Returns group mode used for the course containing the module
|
|
*
|
|
* @return int one of constants NOGROUPS, SEPARATEGROUPS, VISIBLEGROUPS
|
|
*/
|
|
private function get_course_groupmode() {
|
|
return $this->modinfo->get_course()->groupmode;
|
|
}
|
|
|
|
/**
|
|
* Returns whether group mode is forced for the course containing the module
|
|
*
|
|
* @return bool
|
|
*/
|
|
private function get_course_groupmodeforce() {
|
|
return $this->modinfo->get_course()->groupmodeforce;
|
|
}
|
|
|
|
/**
|
|
* Returns effective groupmode of the module that may be overwritten by forced course groupmode.
|
|
*
|
|
* @return int one of constants NOGROUPS, SEPARATEGROUPS, VISIBLEGROUPS
|
|
*/
|
|
private function get_effective_groupmode() {
|
|
$groupmode = $this->groupmode;
|
|
if ($this->modinfo->get_course()->groupmodeforce) {
|
|
$groupmode = $this->modinfo->get_course()->groupmode;
|
|
if ($groupmode != NOGROUPS && !plugin_supports('mod', $this->modname, FEATURE_GROUPS, false)) {
|
|
$groupmode = NOGROUPS;
|
|
}
|
|
}
|
|
return $groupmode;
|
|
}
|
|
|
|
/**
|
|
* Get the context for that activity.
|
|
*
|
|
* @return context_module Current module context
|
|
*/
|
|
private function get_context() {
|
|
return context_module::instance($this->id);
|
|
}
|
|
|
|
/**
|
|
* Returns itself in the form of stdClass.
|
|
*
|
|
* The object includes all fields that table course_modules has and additionally
|
|
* fields 'name', 'modname', 'sectionnum' (if requested).
|
|
*
|
|
* This can be used as a faster alternative to {@see get_coursemodule_from_id()}
|
|
*
|
|
* @param bool $additionalfields include additional fields 'name', 'modname', 'sectionnum'
|
|
* @return stdClass
|
|
*/
|
|
public function get_course_module_record($additionalfields = false) {
|
|
$cmrecord = new stdClass();
|
|
|
|
// Standard fields from table course_modules.
|
|
static $cmfields = ['id', 'course', 'module', 'instance', 'section', 'idnumber', 'added',
|
|
'score', 'indent', 'visible', 'visibleoncoursepage', 'visibleold', 'groupmode', 'groupingid',
|
|
'completion', 'completiongradeitemnumber', 'completionview', 'completionexpected', 'completionpassgrade',
|
|
'showdescription', 'availability', 'deletioninprogress', 'downloadcontent', 'lang',
|
|
'enableaitools', 'enabledaiactions',
|
|
];
|
|
|
|
foreach ($cmfields as $key) {
|
|
$cmrecord->$key = $this->$key;
|
|
}
|
|
|
|
// Additional fields that function get_coursemodule_from_id() adds.
|
|
if ($additionalfields) {
|
|
$cmrecord->name = $this->name;
|
|
$cmrecord->modname = $this->modname;
|
|
$cmrecord->sectionnum = $this->sectionnum;
|
|
}
|
|
|
|
return $cmrecord;
|
|
}
|
|
|
|
/**
|
|
* Return the activity database table record.
|
|
*
|
|
* The instance record will be cached after the first call.
|
|
*
|
|
* @return stdClass
|
|
*/
|
|
public function get_instance_record() {
|
|
global $DB;
|
|
if (!isset($this->instancerecord)) {
|
|
$this->instancerecord = $DB->get_record(
|
|
table: $this->modname,
|
|
conditions: ['id' => $this->instance],
|
|
strictness: MUST_EXIST,
|
|
);
|
|
}
|
|
return $this->instancerecord;
|
|
}
|
|
|
|
/**
|
|
* Returns the section delegated by this module, if any.
|
|
*
|
|
* @return section_info|null
|
|
*/
|
|
public function get_delegated_section_info(): ?section_info {
|
|
$delegatedsections = $this->modinfo->get_sections_delegated_by_cm();
|
|
if (!array_key_exists($this->id, $delegatedsections)) {
|
|
return null;
|
|
}
|
|
return $delegatedsections[$this->id];
|
|
}
|
|
|
|
/**
|
|
* Sets content to display on course view page below link (if present).
|
|
* @param string $content New content as HTML string (empty string if none)
|
|
* @param bool $isformatted Whether user content is already passed through format_text/format_string and should not
|
|
* be formatted again. This can be useful when module adds interactive elements on top of formatted user text.
|
|
* @return void
|
|
*/
|
|
public function set_content($content, $isformatted = false) {
|
|
$this->content = $content;
|
|
$this->contentisformatted = $isformatted;
|
|
}
|
|
|
|
/**
|
|
* Sets extra classes to include in CSS.
|
|
* @param string $extraclasses Extra classes (empty string if none)
|
|
* @return void
|
|
*/
|
|
public function set_extra_classes($extraclasses) {
|
|
$this->extraclasses = $extraclasses;
|
|
}
|
|
|
|
/**
|
|
* Sets the external full url that points to the icon being used
|
|
* by the activity. Useful for external-tool modules (lti...)
|
|
* If set, takes precedence over $icon and $iconcomponent
|
|
*
|
|
* @param url $iconurl full external url pointing to icon image for activity
|
|
* @return void
|
|
*/
|
|
public function set_icon_url(url $iconurl) {
|
|
$this->iconurl = $iconurl;
|
|
}
|
|
|
|
/**
|
|
* Sets value of on-click attribute for JavaScript.
|
|
* Note: May not be called from _cm_info_view (only _cm_info_dynamic).
|
|
* @param string $onclick New onclick attribute which should be HTML-escaped
|
|
* (empty string if none)
|
|
* @return void
|
|
*/
|
|
public function set_on_click($onclick) {
|
|
$this->check_not_view_only();
|
|
$this->onclick = $onclick;
|
|
}
|
|
|
|
/**
|
|
* Overrides the value of an element in the customdata array.
|
|
*
|
|
* @param string $name The key in the customdata array
|
|
* @param mixed $value The value
|
|
*/
|
|
public function override_customdata($name, $value) {
|
|
if (!is_array($this->customdata)) {
|
|
$this->customdata = [];
|
|
}
|
|
$this->customdata[$name] = $value;
|
|
}
|
|
|
|
/**
|
|
* Sets HTML that displays after link on course view page.
|
|
* @param string $afterlink HTML string (empty string if none)
|
|
* @return void
|
|
*/
|
|
public function set_after_link($afterlink) {
|
|
$this->afterlink = $afterlink;
|
|
}
|
|
|
|
/**
|
|
* Sets HTML that displays after edit icons on course view page.
|
|
* @param string $afterediticons HTML string (empty string if none)
|
|
* @return void
|
|
*/
|
|
public function set_after_edit_icons($afterediticons) {
|
|
$this->afterediticons = $afterediticons;
|
|
}
|
|
|
|
/**
|
|
* Changes the name (text of link) for this module instance.
|
|
* Note: May not be called from _cm_info_view (only _cm_info_dynamic).
|
|
* @param string $name Name of activity / link text
|
|
* @return void
|
|
*/
|
|
public function set_name($name) {
|
|
if ($this->state < self::STATE_BUILDING_DYNAMIC) {
|
|
$this->update_user_visible();
|
|
}
|
|
$this->name = $name;
|
|
}
|
|
|
|
/**
|
|
* Turns off the view link for this module instance.
|
|
* Note: May not be called from _cm_info_view (only _cm_info_dynamic).
|
|
* @return void
|
|
*/
|
|
public function set_no_view_link() {
|
|
$this->check_not_view_only();
|
|
$this->url = null;
|
|
}
|
|
|
|
/**
|
|
* Sets the 'uservisible' flag. This can be used (by setting false) to prevent access and
|
|
* display of this module link for the current user.
|
|
* Note: May not be called from _cm_info_view (only _cm_info_dynamic).
|
|
* @param bool $uservisible
|
|
* @return void
|
|
*/
|
|
public function set_user_visible($uservisible) {
|
|
$this->check_not_view_only();
|
|
$this->uservisible = $uservisible;
|
|
}
|
|
|
|
/**
|
|
* Sets the 'customcmlistitem' flag
|
|
*
|
|
* This can be used (by setting true) to prevent the course from rendering the
|
|
* activity item as a regular activity card. This is applied to activities like labels.
|
|
*
|
|
* @param bool $customcmlistitem if the cmlist item of that activity has a special dysplay other than a card.
|
|
*/
|
|
public function set_custom_cmlist_item(bool $customcmlistitem) {
|
|
$this->customcmlistitem = $customcmlistitem;
|
|
}
|
|
|
|
/**
|
|
* Sets the 'available' flag and related details. This flag is normally used to make
|
|
* course modules unavailable until a certain date or condition is met. (When a course
|
|
* module is unavailable, it is still visible to users who have viewhiddenactivities
|
|
* permission.)
|
|
*
|
|
* When this is function is called, user-visible status is recalculated automatically.
|
|
*
|
|
* The $showavailability flag does not really do anything any more, but is retained
|
|
* for backward compatibility. Setting this to false will cause $availableinfo to
|
|
* be ignored.
|
|
*
|
|
* Note: May not be called from _cm_info_view (only _cm_info_dynamic).
|
|
* @param bool $available False if this item is not 'available'
|
|
* @param int $showavailability 0 = do not show this item at all if it's not available,
|
|
* 1 = show this item greyed out with the following message
|
|
* @param string $availableinfo Information about why this is not available, or
|
|
* empty string if not displaying
|
|
* @return void
|
|
*/
|
|
public function set_available($available, $showavailability = 0, $availableinfo = '') {
|
|
$this->check_not_view_only();
|
|
$this->available = $available;
|
|
if (!$showavailability) {
|
|
$availableinfo = '';
|
|
}
|
|
$this->availableinfo = $availableinfo;
|
|
$this->update_user_visible();
|
|
}
|
|
|
|
/**
|
|
* Some set functions can only be called from _cm_info_dynamic and not _cm_info_view.
|
|
* This is because they may affect parts of this object which are used on pages other
|
|
* than the view page (e.g. in the navigation block, or when checking access on
|
|
* module pages).
|
|
* @return void
|
|
*/
|
|
private function check_not_view_only() {
|
|
if ($this->state >= self::STATE_DYNAMIC) {
|
|
throw new coding_exception(
|
|
'Cannot set this data from _cm_info_view because it may ' .
|
|
'affect other pages as well as view'
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Constructor should not be called directly; use {@see get_fast_modinfo()}
|
|
*
|
|
* @param modinfo $modinfo Parent object
|
|
* @param mixed $notused1 Argument not used
|
|
* @param stdClass $mod Module object from the modinfo field of course table
|
|
* @param mixed $notused2 Argument not used
|
|
*/
|
|
public function __construct(modinfo $modinfo, $notused1, $mod, $notused2) {
|
|
$this->modinfo = $modinfo;
|
|
|
|
$this->id = $mod->cm;
|
|
$this->instance = $mod->id;
|
|
$this->modname = $mod->mod;
|
|
$this->idnumber = isset($mod->idnumber) ? $mod->idnumber : '';
|
|
$this->name = $mod->name;
|
|
$this->visible = $mod->visible;
|
|
$this->visibleoncoursepage = $mod->visibleoncoursepage;
|
|
$this->sectionnum = $mod->section; // Note weirdness with name here. Keeping for backwards compatibility.
|
|
$this->groupmode = isset($mod->groupmode) ? $mod->groupmode : 0;
|
|
$this->groupingid = isset($mod->groupingid) ? $mod->groupingid : 0;
|
|
$this->indent = isset($mod->indent) ? $mod->indent : 0;
|
|
$this->extra = isset($mod->extra) ? $mod->extra : '';
|
|
$this->extraclasses = isset($mod->extraclasses) ? $mod->extraclasses : '';
|
|
// The iconurl may be stored as either string or instance of url.
|
|
$this->iconurl = isset($mod->iconurl) ? new url($mod->iconurl) : '';
|
|
$this->onclick = isset($mod->onclick) ? $mod->onclick : '';
|
|
$this->content = isset($mod->content) ? $mod->content : '';
|
|
$this->icon = isset($mod->icon) ? $mod->icon : '';
|
|
$this->iconcomponent = isset($mod->iconcomponent) ? $mod->iconcomponent : '';
|
|
$this->customdata = isset($mod->customdata) ? $mod->customdata : '';
|
|
$this->showdescription = isset($mod->showdescription) ? $mod->showdescription : 0;
|
|
$this->state = self::STATE_BASIC;
|
|
|
|
$this->sectionid = isset($mod->sectionid) ? $mod->sectionid : 0;
|
|
$this->module = isset($mod->module) ? $mod->module : 0;
|
|
$this->added = isset($mod->added) ? $mod->added : 0;
|
|
$this->score = isset($mod->score) ? $mod->score : 0;
|
|
$this->visibleold = isset($mod->visibleold) ? $mod->visibleold : 0;
|
|
$this->deletioninprogress = isset($mod->deletioninprogress) ? $mod->deletioninprogress : 0;
|
|
$this->downloadcontent = $mod->downloadcontent ?? null;
|
|
$this->lang = $mod->lang ?? null;
|
|
|
|
// Note: it saves effort and database space to always include the
|
|
// availability and completion fields, even if availability or completion
|
|
// are actually disabled.
|
|
$this->completion = isset($mod->completion) ? $mod->completion : 0;
|
|
$this->completionpassgrade = isset($mod->completionpassgrade) ? $mod->completionpassgrade : 0;
|
|
$this->completiongradeitemnumber = isset($mod->completiongradeitemnumber)
|
|
? $mod->completiongradeitemnumber : null;
|
|
$this->completionview = isset($mod->completionview)
|
|
? $mod->completionview : 0;
|
|
$this->completionexpected = isset($mod->completionexpected)
|
|
? $mod->completionexpected : 0;
|
|
$this->availability = isset($mod->availability) ? $mod->availability : null;
|
|
$this->conditionscompletion = isset($mod->conditionscompletion)
|
|
? $mod->conditionscompletion : [];
|
|
$this->conditionsgrade = isset($mod->conditionsgrade)
|
|
? $mod->conditionsgrade : [];
|
|
$this->conditionsfield = isset($mod->conditionsfield)
|
|
? $mod->conditionsfield : [];
|
|
|
|
static $modviews = [];
|
|
if (!isset($modviews[$this->modname])) {
|
|
$modviews[$this->modname] = !plugin_supports(
|
|
'mod',
|
|
$this->modname,
|
|
FEATURE_NO_VIEW_LINK,
|
|
);
|
|
}
|
|
$this->url = $modviews[$this->modname]
|
|
? new url('/mod/' . $this->modname . '/view.php', ['id' => $this->id])
|
|
: null;
|
|
}
|
|
|
|
/**
|
|
* Creates a cm_info object from a database record (also accepts cm_info
|
|
* in which case it is just returned unchanged).
|
|
*
|
|
* @param stdClass|cm_info|null|bool $cm Stdclass or cm_info (or null or false)
|
|
* @param int $userid Optional userid (default to current)
|
|
* @return cm_info|null Object as cm_info, or null if input was null/false
|
|
*/
|
|
public static function create($cm, $userid = 0) {
|
|
// Null, false, etc. gets passed through as null.
|
|
if (!$cm) {
|
|
return null;
|
|
}
|
|
// If it is already a cm_info object with the right user, just return it.
|
|
if (($cm instanceof cm_info) && ($cm->get_modinfo()->userid == $userid)) {
|
|
return $cm;
|
|
}
|
|
// Otherwise load modinfo.
|
|
if (empty($cm->id) || empty($cm->course)) {
|
|
throw new coding_exception('$cm must contain ->id and ->course');
|
|
}
|
|
$modinfo = get_fast_modinfo($cm->course, $userid);
|
|
return $modinfo->get_cm($cm->id);
|
|
}
|
|
|
|
/**
|
|
* If dynamic data for this course-module is not yet available, gets it.
|
|
*
|
|
* This function is automatically called when requesting any modinfo property
|
|
* that can be modified by modules (have a set_xxx method).
|
|
*
|
|
* Dynamic data is data which does not come directly from the cache but is calculated at
|
|
* runtime based on the current user. Primarily this concerns whether the user can access
|
|
* the module or not.
|
|
*
|
|
* As part of this function, the module's _cm_info_dynamic function from its lib.php will
|
|
* be called (if it exists). Make sure that the functions that are called here do not use
|
|
* any getter magic method from cm_info.
|
|
* @return void
|
|
*/
|
|
private function obtain_dynamic_data() {
|
|
global $CFG;
|
|
$userid = $this->modinfo->get_user_id();
|
|
if ($this->state >= self::STATE_BUILDING_DYNAMIC || $userid == -1) {
|
|
return;
|
|
}
|
|
$this->state = self::STATE_BUILDING_DYNAMIC;
|
|
|
|
if (!empty($CFG->enableavailability)) {
|
|
// Get availability information.
|
|
$ci = new \core_availability\info_module($this);
|
|
|
|
// Note that the modinfo currently available only includes minimal details (basic data)
|
|
// but we know that this function does not need anything more than basic data.
|
|
$this->available = $ci->is_available(
|
|
$this->availableinfo,
|
|
true,
|
|
$userid,
|
|
$this->modinfo,
|
|
);
|
|
} else {
|
|
$this->available = true;
|
|
}
|
|
|
|
// Check parent section.
|
|
if ($this->available) {
|
|
$parentsection = $this->modinfo->get_section_info($this->sectionnum);
|
|
if (!$parentsection->get_available()) {
|
|
// Do not store info from section here, as that is already presented from the section (if appropriate).
|
|
// Just change the flag.
|
|
$this->available = false;
|
|
}
|
|
}
|
|
|
|
// Update visible state for current user.
|
|
$this->update_user_visible();
|
|
|
|
// Let module make dynamic changes at this point.
|
|
$this->call_mod_function('cm_info_dynamic');
|
|
$this->state = self::STATE_DYNAMIC;
|
|
}
|
|
|
|
/**
|
|
* Getter method for property $uservisible, ensures that dynamic data is retrieved.
|
|
*
|
|
* This method is normally called by the property ->uservisible, but can be called directly if
|
|
* there is a case when it might be called recursively (you can't call property values
|
|
* recursively).
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function get_user_visible() {
|
|
$this->obtain_dynamic_data();
|
|
return $this->uservisible;
|
|
}
|
|
|
|
/**
|
|
* Returns whether this module is visible to the current user on course page
|
|
*
|
|
* Activity may be visible on the course page but not available, for example
|
|
* when it is hidden conditionally but the condition information is displayed.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function is_visible_on_course_page() {
|
|
$this->obtain_dynamic_data();
|
|
return $this->uservisibleoncoursepage;
|
|
}
|
|
|
|
/**
|
|
* Use this method if you want to check if the plugin overrides any visibility checks to block rendering to the display.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function is_of_type_that_can_display(): bool {
|
|
return modinfo::is_mod_type_visible_on_course($this->modname);
|
|
}
|
|
|
|
/**
|
|
* Whether this module is available but hidden from course page
|
|
*
|
|
* "Stealth" modules are the ones that are not shown on course page but available by following url.
|
|
* They are normally also displayed in grade reports and other reports.
|
|
* Module will be stealth either if visibleoncoursepage=0 or it is a visible module inside the hidden
|
|
* section.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function is_stealth() {
|
|
return !$this->visibleoncoursepage ||
|
|
($this->visible && ($section = $this->get_section_info()) && !$section->visible);
|
|
}
|
|
|
|
/**
|
|
* Getter method for property $available, ensures that dynamic data is retrieved
|
|
* @return bool
|
|
*/
|
|
private function get_available() {
|
|
$this->obtain_dynamic_data();
|
|
return $this->available;
|
|
}
|
|
|
|
/**
|
|
* Getter method for property $availableinfo, ensures that dynamic data is retrieved
|
|
*
|
|
* @return string Available info (HTML)
|
|
*/
|
|
private function get_available_info() {
|
|
$this->obtain_dynamic_data();
|
|
return $this->availableinfo;
|
|
}
|
|
|
|
/**
|
|
* Works out whether activity is available to the current user
|
|
*
|
|
* If the activity is unavailable, additional checks are required to determine if its hidden or greyed out
|
|
*
|
|
* @return void
|
|
*/
|
|
private function update_user_visible() {
|
|
$userid = $this->modinfo->get_user_id();
|
|
if ($userid == -1) {
|
|
return null;
|
|
}
|
|
$this->uservisible = true;
|
|
|
|
// If the module is being deleted, set the uservisible state to false and return.
|
|
if ($this->deletioninprogress) {
|
|
$this->uservisible = false;
|
|
return null;
|
|
}
|
|
|
|
// If the user cannot access the activity set the uservisible flag to false.
|
|
// Additional checks are required to determine whether the activity is entirely hidden or just greyed out.
|
|
if (
|
|
!$this->visible && !has_capability('moodle/course:viewhiddenactivities', $this->get_context(), $userid)
|
|
|| (
|
|
!$this->get_available()
|
|
&& !has_capability('moodle/course:ignoreavailabilityrestrictions', $this->get_context(), $userid)
|
|
)
|
|
) {
|
|
$this->uservisible = false;
|
|
}
|
|
|
|
// Check group membership.
|
|
if ($this->is_user_access_restricted_by_capability()) {
|
|
$this->uservisible = false;
|
|
// Ensure activity is completely hidden from the user.
|
|
$this->availableinfo = '';
|
|
}
|
|
|
|
$capabilities = [
|
|
'moodle/course:manageactivities',
|
|
'moodle/course:activityvisibility',
|
|
'moodle/course:viewhiddenactivities',
|
|
];
|
|
$this->uservisibleoncoursepage = $this->uservisible &&
|
|
($this->visibleoncoursepage || has_any_capability($capabilities, $this->get_context(), $userid));
|
|
// Activity that is not available, not hidden from course page and has availability
|
|
// info is actually visible on the course page (with availability info and without a link).
|
|
if (!$this->uservisible && $this->visibleoncoursepage && $this->availableinfo) {
|
|
$this->uservisibleoncoursepage = true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks whether mod/...:view capability restricts the current user's access.
|
|
*
|
|
* @return bool|null True if the user access is restricted.
|
|
*/
|
|
public function is_user_access_restricted_by_capability() {
|
|
$userid = $this->modinfo->get_user_id();
|
|
if ($userid == -1) {
|
|
return null;
|
|
}
|
|
$capability = 'mod/' . $this->modname . ':view';
|
|
$capabilityinfo = get_capability_info($capability);
|
|
if (!$capabilityinfo) {
|
|
// Capability does not exist, no one is prevented from seeing the activity.
|
|
return false;
|
|
}
|
|
|
|
// You are blocked if you don't have the capability.
|
|
return !has_capability($capability, $this->get_context(), $userid);
|
|
}
|
|
|
|
/**
|
|
* Calls a module function (if exists), passing in one parameter: this object.
|
|
* @param string $type Name of function e.g. if this is 'grooblezorb' and the modname is
|
|
* 'forum' then it will try to call 'mod_forum_grooblezorb' or 'forum_grooblezorb'
|
|
* @return void
|
|
*/
|
|
private function call_mod_function($type) {
|
|
global $CFG;
|
|
$libfile = $CFG->dirroot . '/mod/' . $this->modname . '/lib.php';
|
|
if (file_exists($libfile)) {
|
|
include_once($libfile);
|
|
$function = 'mod_' . $this->modname . '_' . $type;
|
|
if (function_exists($function)) {
|
|
$function($this);
|
|
} else {
|
|
$function = $this->modname . '_' . $type;
|
|
if (function_exists($function)) {
|
|
$function($this);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* If view data for this course-module is not yet available, obtains it.
|
|
*
|
|
* This function is automatically called if any of the functions (marked) which require
|
|
* view data are called.
|
|
*
|
|
* View data is data which is needed only for displaying the course main page (& any similar
|
|
* functionality on other pages) but is not needed in general. Obtaining view data may have
|
|
* a performance cost.
|
|
*
|
|
* As part of this function, the module's _cm_info_view function from its lib.php will
|
|
* be called (if it exists).
|
|
* @return void
|
|
*/
|
|
private function obtain_view_data() {
|
|
if ($this->state >= self::STATE_BUILDING_VIEW || $this->modinfo->get_user_id() == -1) {
|
|
return;
|
|
}
|
|
$this->obtain_dynamic_data();
|
|
$this->state = self::STATE_BUILDING_VIEW;
|
|
|
|
// Let module make changes at this point.
|
|
$this->call_mod_function('cm_info_view');
|
|
$this->state = self::STATE_VIEW;
|
|
}
|
|
}
|
|
|
|
// Alias this class to the old name.
|
|
// This file will be autoloaded by the legacyclasses autoload system.
|
|
// In future all uses of this class will be corrected and the legacy references will be removed.
|
|
class_alias(cm_info::class, \cm_info::class);
|