Skip to content

Commit

Permalink
MBS-9408: prevent manual block creation, add block in subcontext (#7)
Browse files Browse the repository at this point in the history
* MBS-9408: prevent manual block creation, add block in subcontext

* MBS-9408: add comment and minor fixes.

* MBS-9408: add override annotations

---------

Co-authored-by: Andreas <[email protected]>
  • Loading branch information
awagner and Andreas authored Nov 14, 2024
1 parent 0deb5ac commit 162188f
Show file tree
Hide file tree
Showing 5 changed files with 353 additions and 35 deletions.
52 changes: 33 additions & 19 deletions block_ai_chat.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.

use block_ai_chat\local\helper;
use local_ai_manager\local\userinfo;

/**
* Block class for block_ai_chat
Expand All @@ -41,6 +41,7 @@ public function init(): void {
*
* @return bool
*/
#[\Override]
public function has_config(): bool {
return true;
}
Expand All @@ -52,6 +53,7 @@ public function has_config(): bool {
* @throws coding_exception
* @throws moodle_exception
*/
#[\Override]
public function get_content(): stdClass {
global $USER;

Expand All @@ -64,7 +66,8 @@ public function get_content(): stdClass {
$this->content->footer = '';

$context = \context_block::instance($this->instance->id);
if (!has_capability('block/ai_chat:view', $context) || !has_capability('local/ai_manager:use', $context)) {
if (!has_capability('block/ai_chat:view', $context) ||
!has_capability('local/ai_manager:use', $context)) {
return $this->content;
}
$tenant = \core\di::get(\local_ai_manager\local\tenant::class);
Expand All @@ -75,16 +78,12 @@ public function get_content(): stdClass {
return $this->content;
}
if ($tenant->is_tenant_allowed() && !$configmanager->is_tenant_enabled()) {
if ($aiconfig['role'] ===
\local_ai_manager\local\userinfo::get_role_as_string(\local_ai_manager\local\userinfo::ROLE_BASIC
)) {
if ($aiconfig['role'] === userinfo::get_role_as_string(userinfo::ROLE_BASIC)) {
return $this->content;
}
}
if (!$chatconfig['isconfigured']) {
if ($aiconfig['role'] ===
\local_ai_manager\local\userinfo::get_role_as_string(\local_ai_manager\local\userinfo::ROLE_BASIC
)) {
if ($aiconfig['role'] === userinfo::get_role_as_string(userinfo::ROLE_BASIC)) {
return $this->content;
}
}
Expand All @@ -103,33 +102,48 @@ public function get_content(): stdClass {
*
* @return bool
*/
#[\Override]
public function instance_allow_multiple(): bool {
return false;
}

/**
* Returns on which page formats this block can be used.
* Returns on which page formats this block can be added.
*
* We do not want any user to create the block manually.
* But me must add at least one applicable format here otherwise it will lead to an installation error,
* because the block::_self_test fails.
*
* There are only two ways to create block instances:
* - Check "add ai block" in the settings form of a course
* - Admin has set up an automatic create of a block instance using the plugin settings.
*
* @return array
*/
#[\Override]
public function applicable_formats(): array {
return ['course-view' => true];
}

/**
* We don't want any user to manually create an instance of this block.
*
* @param $page
* @return false
*/
#[\Override]
public function user_can_addto($page) {
$tenant = \core\di::get(\local_ai_manager\local\tenant::class);
// Add exception for site admin, otherwise we cannot add the block to the dashboard in the website administration.
if (!$tenant->is_tenant_allowed() && !is_siteadmin()) {
return false;
}
if (helper::show_global_block($page)) {
// If a global block is being shown on a page, we do not allow the user to add an own block.
return false;
}
return parent::user_can_addto($page);
return false;
}

/**
* Do any additional initialization you may need at the time a new block instance is created
*
* @return boolean
* /
* @return true
* @throws dml_exception
*/
#[\Override]
public function instance_create() {
global $DB;
Expand Down
19 changes: 9 additions & 10 deletions classes/local/helper.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,22 +26,21 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class helper {

/**
* Check if a block is present.
*
* Check, if a block is existing in course context.
* @param int $courseid
* @return mixed
* @return object|bool
* @throws \dml_exception
*/
public static function check_block_present($courseid) {
public static function has_block_in_course_context(int $courseid): object|bool {
global $DB;

// Check if tenant is enabled for the school.
$sql = "SELECT bi.id
FROM {block_instances} bi
JOIN {context} ctx ON bi.parentcontextid = ctx.id
WHERE bi.blockname = :blockname AND ctx.contextlevel = :contextlevel
AND ctx.instanceid = :courseid";
$sql = "SELECT bi.*
FROM {block_instances} bi
JOIN {context} ctx ON bi.parentcontextid = ctx.id
WHERE bi.blockname = :blockname AND ctx.contextlevel = :contextlevel
AND ctx.instanceid = :courseid";

$params = [
'blockname' => 'ai_chat',
Expand Down
15 changes: 10 additions & 5 deletions classes/local/hook_callbacks.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public static function handle_after_form_definition(\core_course\hook\after_form
}

/**
* Check for addaichat form setting and add/remove ai-chat blockk.
* Check for addaichat form setting and add/remove ai-chat block.
*
* @param after_form_submission $hook
*/
Expand All @@ -56,15 +56,16 @@ public static function handle_after_form_submission(\core_course\hook\after_form

// Check if block_ai_chat instance is present.
$courseid = $data->id;
$blockinstance = \block_ai_chat\local\helper::check_block_present($courseid);
$blockinstance = helper::has_block_in_course_context($courseid);

if (!empty($data->addaichat) && $data->addaichat == '1') {
if (!$blockinstance) {
// Add block instance.
$newinstance = new \stdClass;
$newinstance->blockname = 'ai_chat';
$newinstance->parentcontextid = \context_course::instance($courseid)->id;
$newinstance->showinsubcontexts = 0;
// We want to make the block usable for single activity courses as well, so display in subcontexts.
$newinstance->showinsubcontexts = 1;
$newinstance->pagetypepattern = '*';
$newinstance->subpagepattern = null;
$newinstance->defaultregion = 'side-pre';
Expand All @@ -75,6 +76,8 @@ public static function handle_after_form_submission(\core_course\hook\after_form
$newinstance->id = $DB->insert_record('block_instances', $newinstance);
}
} else {
// If tenant is not allowed, $data->addaichat will be empty,
// so an existing instance will be deleted by following lines.
if ($blockinstance) {
// Remove block instance.
blocks_delete_instance($blockinstance);
Expand All @@ -85,7 +88,9 @@ public static function handle_after_form_submission(\core_course\hook\after_form
/**
* Check if block instance is present and set addaichat form setting.
*
* @param after_form_submission $hook
* @param \core_course\hook\after_form_definition_after_data $hook
* @return void
* @throws \dml_exception
*/
public static function handle_after_form_definition_after_data(\core_course\hook\after_form_definition_after_data $hook): void {
// Get form data.
Expand All @@ -94,7 +99,7 @@ public static function handle_after_form_definition_after_data(\core_course\hook
if (!empty($formwrapper->get_course()->id)) {
$courseid = $formwrapper->get_course()->id;

$blockinstance = \block_ai_chat\local\helper::check_block_present($courseid);
$blockinstance = helper::has_block_in_course_context($courseid);
if ($blockinstance) {
// Block present, so set checkbox accordingly.
$mform->setDefault('addaichat', "checked");
Expand Down
Loading

0 comments on commit 162188f

Please sign in to comment.