MDL-85738 course: Add support for AI access controls

This commit is contained in:
Stevani Andolo
2025-09-03 18:30:24 +08:00
parent 29ef751472
commit 5906a94e29
10 changed files with 217 additions and 4 deletions
@@ -82,3 +82,19 @@ Feature: AI course assist features
When I click on "AI features" "button"
And I click on "Summarise" "button"
Then I should see "Welcome to the new AI feature!" in the ".ai-drawer" "css_element"
Scenario: AI features are not available when AI tools is disabled at course level
Given I am on the "Course 1" "course editing" page logged in as teacher1
When I set the following fields to these values:
| Allow AI tools for this course | No |
And I press "Save and display"
And I am on the "PageName1" "page activity" page logged in as teacher1
Then "AI features" "button" should not exist
Scenario: AI features are not available when AI tools is disabled at module level
Given I am on the "PageName1" "page activity editing" page logged in as teacher1
When I set the following fields to these values:
| Allow AI tools in this activity | No |
And I press "Save and display"
And I am on the "PageName1" "page activity" page logged in as teacher1
Then "AI features" "button" should not exist
+6 -2
View File
@@ -409,7 +409,9 @@ class backup_module_structure_step extends backup_structure_step {
'visibleold', 'groupmode', 'groupingid',
'completion', 'completiongradeitemnumber', 'completionpassgrade',
'completionview', 'completionexpected',
'availability', 'showdescription', 'downloadcontent', 'lang'));
'availability', 'showdescription', 'downloadcontent', 'lang',
'enableaitools', 'enabledaiactions',
));
$tags = new backup_nested_element('tags');
$tag = new backup_nested_element('tag', array('id'), array('name', 'rawname'));
@@ -533,7 +535,9 @@ class backup_course_structure_step extends backup_structure_step {
'requested',
'showactivitydates',
'showcompletionconditions', 'pdfexportfont',
'enablecompletion', 'completionstartonenrol', 'completionnotify'));
'enablecompletion', 'completionstartonenrol', 'completionnotify',
'enableaitools', 'enabledaiactions',
));
$category = new backup_nested_element('category', array('id'), array(
'name', 'description'));
+17 -1
View File
@@ -177,6 +177,8 @@ use core\output\html_writer;
* @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 {
/**
@@ -514,6 +516,16 @@ class cm_info implements IteratorAggregate {
*/
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().
@@ -573,6 +585,8 @@ class cm_info implements IteratorAggregate {
'deletioninprogress' => false,
'downloadcontent' => false,
'lang' => false,
'enableaitools' => false,
'enabledaiactions' => false,
];
/**
@@ -1091,7 +1105,9 @@ class cm_info implements IteratorAggregate {
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'];
'showdescription', 'availability', 'deletioninprogress', 'downloadcontent', 'lang',
'enableaitools', 'enabledaiactions',
];
foreach ($cmfields as $key) {
$cmrecord->$key = $this->$key;
@@ -70,6 +70,8 @@ abstract class helper_for_get_mods_by_courses {
$moddetails['visible'] = $modinstance->visible;
$moddetails['groupmode'] = $modinstance->groupmode;
$moddetails['groupingid'] = $modinstance->groupingid;
$moddetails['enableaitools'] = $modinstance->enableaitools;
$moddetails['enabledaiactions'] = $modinstance->enabledaiactions;
}
return $moddetails;
@@ -132,6 +134,8 @@ abstract class helper_for_get_mods_by_courses {
'groupmode' => new external_value(PARAM_INT, 'Group mode', VALUE_OPTIONAL),
'groupingid' => new external_value(PARAM_INT, 'Group id', VALUE_OPTIONAL),
'lang' => new external_value(PARAM_SAFEDIR, 'Forced activity language', VALUE_OPTIONAL),
'enableaitools' => new external_value(PARAM_INT, 'AI tools status', VALUE_OPTIONAL),
'enabledaiactions' => new external_value(PARAM_TEXT, 'Enabled AI actions', VALUE_OPTIONAL),
];
}
}
+2
View File
@@ -1058,6 +1058,8 @@ class modinfo {
$mods[$cmid]->deletioninprogress = $rawmods[$cmid]->deletioninprogress;
$mods[$cmid]->downloadcontent = $rawmods[$cmid]->downloadcontent;
$mods[$cmid]->lang = $rawmods[$cmid]->lang;
$mods[$cmid]->enableaitools = $rawmods[$cmid]->enableaitools;
$mods[$cmid]->enabledaiactions = $rawmods[$cmid]->enabledaiactions;
$modname = $mods[$cmid]->mod;
$functionname = $modname . "_get_coursemodule_info";
+11
View File
@@ -418,6 +418,17 @@ class course_edit_form extends moodleform {
array('itemtype' => 'course', 'component' => 'core'));
}
// Add AI tools section if AI placement (course or editor) is available and at least one provider is enabled.
$courseplacementenabled = aiplacement_courseassist\utils::is_course_assist_available();
$editorplacementenabled = aiplacement_editor\utils::is_html_editor_placement_available();
$providerenabled = \core\di::get(core_ai\manager::class)->get_provider_instances(['enabled' => 1]);
if (($courseplacementenabled || $editorplacementenabled) && $providerenabled) {
$mform->addElement('header', 'aitoolshdr', get_string('aitools', 'ai'));
$mform->addElement('selectyesno', 'enableaitools', get_string('enableaitoolsincourse', 'ai'));
$mform->setDefault('enableaitools', $course->enableaitools ?? 1);
$mform->addElement('static', 'aitoolsincoursedesc', '', get_string('aitoolsincoursedesc', 'ai'));
}
// Add custom fields to the form.
$handler = core_course\customfield\course_handler::create();
$handler->set_parent_context($categorycontext); // For course handler only.
+58
View File
@@ -123,6 +123,18 @@ function add_moduleinfo($moduleinfo, $course, $mform = null) {
$moduleinfo->beforemod = null;
}
// Set enableaitools for the activity.
if (isset($moduleinfo->enableaitools)) {
$newcm->enableaitools = $moduleinfo->enableaitools;
}
// Set enabledaiactions for the activity.
if (isset($moduleinfo->enabledaiactions)) {
$newcm->enabledaiactions = $moduleinfo->enabledaiactions;
} else {
$newcm->enabledaiactions = null;
}
// From this point we make database changes, so start transaction.
$transaction = $DB->start_delegated_transaction();
@@ -448,6 +460,7 @@ function edit_module_post_actions($moduleinfo, $course) {
* @return object the completed module info
*/
function set_moduleinfo_defaults($moduleinfo) {
global $PAGE;
if (empty($moduleinfo->coursemodule)) {
// Add.
@@ -512,6 +525,39 @@ function set_moduleinfo_defaults($moduleinfo) {
$moduleinfo->section = 0;
}
// Enable AI tools by default.
if (!isset($moduleinfo->enableaitools)) {
$moduleinfo->enableaitools = null;
}
$enabledaiactions = [];
// Get and check for enabled AI actions in the course placement.
$aiactions = aiplacement_courseassist\utils::get_actions_available($PAGE->context, false);
foreach ($aiactions as $action) {
$value = 0;
$actionname = "action-" . $action['action'];
if (!empty($moduleinfo->{$actionname})) {
$value = 1;
}
$enabledaiactions[$action['action']] = $value;
}
// Get and check for enabled AI actions in the editor placement.
$aiactions = aiplacement_editor\utils::get_actions_available($PAGE->context, false);
foreach ($aiactions as $action) {
$value = 0;
$actionname = "action-" . $action['action'];
if (!empty($moduleinfo->{$actionname})) {
$value = 1;
}
$enabledaiactions[$action['action']] = $value;
}
// Set enabledaiactions for the activity.
if (!empty($enabledaiactions)) {
$moduleinfo->enabledaiactions = json_encode($enabledaiactions);
}
return $moduleinfo;
}
@@ -669,6 +715,18 @@ function update_moduleinfo($cm, $moduleinfo, $course, $mform = null) {
$cm->showdescription = 0;
}
// Set enableaitools for the activity.
if (isset($moduleinfo->enableaitools)) {
$cm->enableaitools = $moduleinfo->enableaitools;
}
// Set enabledaiactions for the activity.
if (isset($moduleinfo->enabledaiactions)) {
$cm->enabledaiactions = $moduleinfo->enabledaiactions;
} else {
$cm->enabledaiactions = null;
}
$DB->update_record('course_modules', $cm);
// Update embedded links and save files.
+63 -1
View File
@@ -510,7 +510,7 @@ abstract class moodleform_mod extends moodleform {
* Adds all the standard elements to a form to edit the settings for an activity module.
*/
protected function standard_coursemodule_elements() {
global $COURSE, $CFG, $DB, $OUTPUT;
global $COURSE, $CFG, $DB, $OUTPUT, $PAGE;
$mform =& $this->_form;
$this->_outcomesused = false;
@@ -675,6 +675,68 @@ abstract class moodleform_mod extends moodleform {
$this->standard_hidden_coursemodule_elements();
$this->plugin_extend_coursemodule_standard_elements();
$availableactions = [];
// Get available actions for the AI course placement.
if (aiplacement_courseassist\utils::is_course_assist_available()) {
$aicourseplacementactions = aiplacement_courseassist\utils::get_actions_available($this->get_context(), false);
$availableactions = array_merge($availableactions, $aicourseplacementactions);
}
// Get available actions for the AI editor placement.
if (aiplacement_editor\utils::is_html_editor_placement_available()) {
$aieditorplacementactions = aiplacement_editor\utils::get_actions_available($this->get_context(), false);
$availableactions = array_merge($availableactions, $aieditorplacementactions);
}
// Current set of enabled AI actions.
$enabledaiactions = ($this->_cm && $this->_cm->enabledaiactions)
? (array) json_decode($this->_cm->enabledaiactions)
: true;
// Show AI tools in activity settings if AI course assist is available.
$aimanager = \core\di::get(core_ai\manager::class);
if (!empty($availableactions) && $aimanager->get_provider_instances(['enabled' => 1])) {
$mform->addElement('header', 'aitoolshdr', get_string('aitools', 'ai'));
// Check if AI tools is enabled in the course.
if (is_null($COURSE->enableaitools) || $COURSE->enableaitools) {
$mform->addElement('selectyesno', 'enableaitools', get_string('enableaitoolsincourseactivity', 'ai'));
$mform->setDefault('enableaitools', $this->_cm->enableaitools ?? 1);
$mform->addElement('static', 'aitoolsincourseactivitydesc', '', get_string('aitoolsincourseactivitydesc', 'ai'));
$mform->addElement('static', 'aiactionshdr', get_string('aiactionshdr', 'core_ai'), '');
$mform->hideIf('aiactionshdr', 'enableaitools', 'eq', 0);
// Get enabled AI actions.
if (is_array($enabledaiactions)) {
$filteredenabledaiactions = array_filter($enabledaiactions, function ($value): bool {
return $value === 1;
});
}
foreach ($availableactions as $action) {
$actionname = 'action-' . $action['action'];
$actiontext = 'action_' . $action['action'];
// Set actions option's default value.
$defaultvalue = $this->_cm ? 0 : 1; // Default to 'On' for new activities.
if ($enabledaiactions === true || array_key_exists($action['action'], $filteredenabledaiactions)) {
$defaultvalue = 1;
}
$mform->addElement('select', $actionname, $action['buttontext'], [
1 => get_string('on', 'core_ai'),
0 => get_string('off', 'core_ai'),
]);
$mform->addHelpButton($actionname, $actiontext, 'core_ai');
$mform->setDefault($actionname, $defaultvalue);
$mform->hideIf($actionname, 'enableaitools', 'eq', 0);
}
} else {
$mform->addElement('static', 'aiactionshdr', '', get_string('aitoolsnotenabled', 'ai'));
}
}
}
/**
@@ -117,3 +117,23 @@ Feature: Generate image using AI
And I should see "Welcome to the new AI feature!" in the "AI usage policy" "dialogue"
And I click on "Accept and continue" "button" in the "AI usage policy" "dialogue"
And I should see "Describe the image you want AI to create"
@javascript
Scenario: Image generation is not available when AI tools is disabled at course level
Given I am on the "Course 1" "course editing" page logged in as teacher1
When I set the following fields to these values:
| Allow AI tools for this course | No |
And I press "Save and display"
And I am on the "PageName1" "page activity" page logged in as teacher1
And I navigate to "Settings" in current page administration
Then "AI generate image" button should not exist in the "Description" TinyMCE editor
@javascript
Scenario: Image generation is not available when AI tools is disabled at module level
Given I am on the "PageName1" "page activity editing" page logged in as teacher1
When I set the following fields to these values:
| Allow AI tools in this activity | No |
And I press "Save and display"
And I am on the "PageName1" "page activity" page logged in as teacher1
And I navigate to "Settings" in current page administration
Then "AI generate image" button should not exist in the "Description" TinyMCE editor
@@ -118,3 +118,23 @@ Feature: Generate text using AI
And I should see "Welcome to the new AI feature!" in the "AI usage policy" "dialogue"
And I click on "Accept and continue" "button" in the "AI usage policy" "dialogue"
And I should see "Describe the text you want AI to create"
@javascript
Scenario: Text generation is not available when AI tools is disabled at course level
Given I am on the "Course 1" "course editing" page logged in as teacher1
When I set the following fields to these values:
| Allow AI tools for this course | No |
And I press "Save and display"
And I am on the "PageName1" "page activity" page logged in as teacher1
And I navigate to "Settings" in current page administration
Then "AI generate text" button should not exist in the "Description" TinyMCE editor
@javascript
Scenario: Text generation is not available when AI tools is disabled at module level
Given I am on the "PageName1" "page activity editing" page logged in as teacher1
When I set the following fields to these values:
| Allow AI tools in this activity | No |
And I press "Save and display"
And I am on the "PageName1" "page activity" page logged in as teacher1
And I navigate to "Settings" in current page administration
Then "AI generate text" button should not exist in the "Description" TinyMCE editor