MDL-85738 course: Add support for AI access controls
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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'));
|
||||
|
||||
@@ -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),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user