From 1bdbabcb605f4168e21865baab070a8a209015c2 Mon Sep 17 00:00:00 2001 From: Michael hughes Date: Fri, 22 Mar 2024 22:45:46 +0000 Subject: [PATCH] Got basic AIProvider form working --- ai/classes/api.php | 19 ++++++ ai/classes/form/openaiapiprovider.php | 85 +++++++++++++++++++++++---- ai/index.php | 16 ++++- lang/en/ai.php | 15 +++++ lib/db/access.php | 7 +++ 5 files changed, 129 insertions(+), 13 deletions(-) diff --git a/ai/classes/api.php b/ai/classes/api.php index 242320d64480b..c4352199e6fe5 100644 --- a/ai/classes/api.php +++ b/ai/classes/api.php @@ -13,6 +13,8 @@ class api { const ACTION_REMOVE_PROVIDER = "remove"; const ACTION_EDIT_PROVIDER = "edit"; const ACTION_MANAGE_PROVIDERS = "manage"; + + const ACTION_SAVE_PROVIDER = "save"; /** * Return a list of AIProviders that are available for specified context. * @param $context @@ -52,4 +54,21 @@ public static function get_providers($contextid = null, $allowchat = null, $allo $providers = aiprovider::get_records($filters); return array_values($providers); } + public static function create_provider($data) { + return self::create_or_update_provider($data, true); + } + public static function update_provider($data) { + return self::create_or_update_provider($data, false); + } + protected static function create_or_update_provider($data, bool $create) { + //TODO Capability check. + $provider = new aiprovider($data->id ?? 0, $data); + + if ($create) { + $provider->create(); + } else { + $provider->update(); + } + return $provider; + } } diff --git a/ai/classes/form/openaiapiprovider.php b/ai/classes/form/openaiapiprovider.php index 0f6058249e90c..9c827b4e5fecc 100644 --- a/ai/classes/form/openaiapiprovider.php +++ b/ai/classes/form/openaiapiprovider.php @@ -1,11 +1,17 @@ disabledIf('embeddingmodel', 'allowembeddings', 'notchecked'); $mform->addElement('header','constraints', get_string('constraints', 'ai')); - $mform->addElement('checkbox', 'systemwide', get_string('allowsystemwide', 'ai')); - $displaylist = \core_course_category::make_categories_list('moodle/course:changecategory'); -// if (!isset($displaylist[$course->category])) { -// //always keep current -// $displaylist[$course->category] = core_course_category::get($course->category, MUST_EXIST, true) -// ->get_formatted_name(); -// } - $mform->addElement('autocomplete', 'contextid', get_string('coursecategory'), $displaylist); -// $mform->addRule('contextid', null, 'required', null, 'client'); - $mform->addHelpButton('contextid', 'coursecategory'); - $mform->disabledIf('contextid', 'systemwide', 'checked'); + $displaylist = [ + "" => get_string('anywhere', 'ai'), + "-1" => get_string('anyusercourse', 'ai') + ]; + $displaylist = + $displaylist + + \core_course_category::make_categories_list('moodle/ai:selectcategory') + ; + + $mform->addElement('autocomplete', 'categoryid', get_string('scopecoursecategory','ai'), $displaylist); + $mform->addHelpButton('categoryid', 'scopecoursecategory', 'ai'); + $mform->setDefault('categoryid', null); // a null category is technical "whole" site + + $coursedisplaylist = \get_courses("all", "shortname"); + $coursedisplaylist = array_map(function($course) { + return $course->shortname; + }, $coursedisplaylist); + $coursedisplaylist = ["" => "No Restriction"] + $coursedisplaylist; + $mform->addElement('autocomplete', 'courseid', get_string('course'), $coursedisplaylist); + $mform->addHelpButton('courseid', 'scopecourse', 'ai'); + $mform->setDefault('courseid', null); // a null category is technical "whole" site +// $mform->disabledIf('courseid', 'categoryid', 'neq', ""); + + $mform->addElement('hidden', 'contextid', ); + $mform->setType('contextid', PARAM_RAW); + + $mform->addElement('hidden', 'onlyenrolledcourses', ); + $mform->setType('onlyenrolledcourses', PARAM_RAW); + + $mform->addElement('hidden', 'enabled', true); + $mform->setType('enabled', PARAM_ALPHA); + + $mform->addElement('hidden', 'type', $this->_customdata['type']); + $mform->setType('type', PARAM_ALPHANUM); + + $mform->addElement('hidden', 'action', api::ACTION_EDIT_PROVIDER); + $mform->setType('action', PARAM_ALPHA); + + $mform->addElement('hidden', 'id', $provider->get('id')); + $mform->setType('id', PARAM_INT); $this->add_action_buttons(true, get_string('savechanges', 'ai')); } + + protected function filter_data_for_persistent($data) { + if (!empty($data->categoryid)) { + $data->onlyenrolledcourses = false; + if ($data->categoryid >0) { + $data->contextid = \core_course_category::get($data->categoryid)->get_context()->id; + } else{ + $data->contextid = $data->categoryid; + if ($data->contextid == -1) { + $data->onlyenrolledcourses = true; + } + } + } else if (!empty($data->courseid)) { + $data->contextid = \core\context\course::instance($data->courseid)->id; + } + return (object) array_diff_key((array) $data, array_flip((array) static::$foreignfields)); + } + function extra_validation($data, $files, array &$errors) { + parent::extra_validation($data, $files, $errors); + if(!empty($data->categoryid) && !empty($data->courseid)) { + // $data->category is not allowed to be set. + $errors['courseid'] = "Course constraint cannot be set whilst a category one is set"; + } + var_dump($errors); + return $errors; + } } diff --git a/ai/index.php b/ai/index.php index 23975fcce6757..f7d312e78efd8 100644 --- a/ai/index.php +++ b/ai/index.php @@ -52,8 +52,22 @@ redirect(new moodle_url('/admin/tool/oauth2/issuers.php')); } else if ($action == api::ACTION_EDIT_PROVIDER) { // Handle edit. - if ($data = $mform->get_data()) { + if ($mform->is_cancelled()) { + echo 'cancelled'; + } + if ($mform->is_submitted()) { + echo 'submitted'; + } + echo 'validated '. (int)$mform->is_validated(); + if ($data = $mform->get_data()) { + var_dump($data); + if (!empty($data->id)) { + core_ai\api::update_provider($data); + } else { + core_ai\api::create_provider($data); + } + exit(); } else { echo $OUTPUT->header(); $mform->display(); diff --git a/lang/en/ai.php b/lang/en/ai.php index 557e026a368b3..a3773be72441e 100644 --- a/lang/en/ai.php +++ b/lang/en/ai.php @@ -24,6 +24,8 @@ $string['pluginname'] = 'AI Providers'; $string['aiprovider'] = 'AI Provider'; $string['addprovider'] = 'Add AI Provider'; +$string['anyusercourse'] = 'Any course user is enrolled in'; +$string['anywhere'] = 'Anywhere in site'; $string['enabled'] = 'Enabled'; $string['disabled'] = 'Disabled'; $string['removeprovider'] = 'Remove AI Provider'; @@ -68,5 +70,18 @@ $string['embeddingmodel'] = 'Embedding Model'; $string['embeddingmodel_help'] = 'Embedding Model'; +$string['scopecoursecategory'] = 'Category'; +$string['scopecoursecategory_help'] = 'Limit AI scope to courses and sub-categories. + +This can be limited to work only against the user\'s enrolled courses. + +Users must hold the `moodle/ai:selectcategory` capability on a category to choose it.'; +$string['scopecourse'] = 'Course(s)'; +$string['scopecourse_help'] = 'Limit AI scope to specific courses. + +Not available if a category scope constraint has been chosen. + +Users must hold the `moodle/ai:selectcourse` capability on a course to choose it.'; + $string['constraints'] = 'Constraints'; $string['savechanges'] = 'Save changes'; diff --git a/lib/db/access.php b/lib/db/access.php index 96f981434419f..88c32ba119675 100644 --- a/lib/db/access.php +++ b/lib/db/access.php @@ -2772,4 +2772,11 @@ 'manager' => CAP_ALLOW, ] ], + 'moodle/ai:selectcategory' => [ + 'captype' => 'read', + 'contextlevel' => CONTEXT_COURSECAT, + 'archetypes' => [ + 'manager' => CAP_ALLOW, + ] + ] );