Commit d2aed789 authored by Andrew Nicols's avatar Andrew Nicols
Browse files

MDL-63619 tool_dataprivacy: Fix inheritance from parent contexts

Inheritance should behave such that all contexts inherit from their
parent context.

Prior to this fix, if the value was not set on a context, then it was
getting a default of 'Inherit', but instead of inheritting from the
parent context, it was inheritting from its parent context _level_ which
is just wrong.
parent daf0b4f0
......@@ -914,7 +914,7 @@ class api {
* @param int $forcedvalue Use this categoryid value as if this was this context instance category.
* @return category|false
*/
public static function get_effective_context_category(\context $context, $forcedvalue=false) {
public static function get_effective_context_category(\context $context, $forcedvalue = false) {
if (!data_registry::defaults_set()) {
return false;
}
......@@ -941,15 +941,14 @@ class api {
* Returns the effective category given a context level.
*
* @param int $contextlevel
* @param int $forcedvalue Use this categoryid value as if this was this context level category.
* @return category|false
*/
public static function get_effective_contextlevel_category($contextlevel, $forcedvalue=false) {
public static function get_effective_contextlevel_category($contextlevel) {
if (!data_registry::defaults_set()) {
return false;
}
return data_registry::get_effective_contextlevel_value($contextlevel, 'category', $forcedvalue);
return data_registry::get_effective_contextlevel_value($contextlevel, 'category');
}
/**
......
......@@ -39,18 +39,6 @@ defined('MOODLE_INTERNAL') || die();
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class data_registry {
/**
* @var array Inheritance between context levels.
*/
private static $contextlevelinheritance = [
CONTEXT_USER => [CONTEXT_SYSTEM],
CONTEXT_COURSECAT => [CONTEXT_SYSTEM],
CONTEXT_COURSE => [CONTEXT_COURSECAT, CONTEXT_SYSTEM],
CONTEXT_MODULE => [CONTEXT_COURSE, CONTEXT_COURSECAT, CONTEXT_SYSTEM],
CONTEXT_BLOCK => [CONTEXT_COURSE, CONTEXT_COURSECAT, CONTEXT_SYSTEM],
];
/**
* Returns purpose and category var names from a context class name
*
......@@ -83,7 +71,6 @@ class data_registry {
* @return int[]|false[]
*/
public static function get_defaults($contextlevel, $pluginname = '') {
$classname = \context_helper::get_class_for_level($contextlevel);
list($purposevar, $categoryvar) = self::var_names_from_context($classname, $pluginname);
......@@ -104,10 +91,10 @@ class data_registry {
}
if (empty($purposeid)) {
$purposeid = false;
$purposeid = context_instance::NOTSET;
}
if (empty($categoryid)) {
$categoryid = false;
$categoryid = context_instance::NOTSET;
}
return [$purposeid, $categoryid];
......@@ -190,19 +177,24 @@ class data_registry {
* @return persistent|false It return a 'purpose' instance or a 'category' instance, depending on $element
*/
public static function get_effective_context_value(\context $context, $element, $forcedvalue = false) {
if ($element !== 'purpose' && $element !== 'category') {
throw new coding_exception('Only \'purpose\' and \'category\' are supported.');
}
$fieldname = $element . 'id';
if (empty($forcedvalue)) {
$instance = context_instance::get_record_by_contextid($context->id, false);
// Check whether this context is a user context, or a child of a user context.
// User contexts share the same context and cannot be set individually.
$parents = $context->get_parent_contexts(true);
foreach ($parents as $parent) {
if ($parent->contextlevel == CONTEXT_USER) {
// Use the context level value for the user.
return self::get_effective_contextlevel_value(CONTEXT_USER, $element);
}
}
if (!$instance) {
// If the instance does not have a value defaults to not set, so we grab the context level default as its value.
$instancevalue = context_instance::NOTSET;
} else {
$instancevalue = context_instance::NOTSET;
if (empty($forcedvalue)) {
if ($instance = context_instance::get_record_by_contextid($context->id, false)) {
$instancevalue = $instance->get($fieldname);
}
} else {
......@@ -211,48 +203,34 @@ class data_registry {
// Not set.
if ($instancevalue == context_instance::NOTSET) {
// The effective value varies depending on the context level.
if ($context->contextlevel == CONTEXT_USER) {
// Use the context level value as we don't allow people to set specific instances values.
return self::get_effective_contextlevel_value(CONTEXT_USER, $element);
}
$parents = $context->get_parent_contexts(true);
foreach ($parents as $parent) {
if ($parent->contextlevel == CONTEXT_USER) {
// Use the context level value as we don't allow people to set specific instances values.
return self::get_effective_contextlevel_value(CONTEXT_USER, $element);
}
}
// Check if we need to pass the plugin name of an activity.
$forplugin = '';
if ($context->contextlevel == CONTEXT_MODULE) {
list($course, $cm) = get_course_and_cm_from_cmid($context->instanceid);
$forplugin = $cm->modname;
}
// Use the default context level value.
list($purposeid, $categoryid) = self::get_effective_default_contextlevel_purpose_and_category(
$context->contextlevel, false, false, $forplugin
);
return self::get_element_instance($element, $$fieldname);
$instancevalue = $$fieldname;
}
// Specific value for this context instance.
if ($instancevalue != context_instance::INHERIT) {
if (context_instance::NOTSET !== $instancevalue && context_instance::INHERIT !== $instancevalue) {
// There is an actual value. Return it.
return self::get_element_instance($element, $instancevalue);
}
// This context is using inherited so let's return the parent effective value.
$parentcontext = $context->get_parent_context();
if (!$parentcontext) {
return false;
}
// There is no value set (or it is set to inherit).
// Fetch from the parent context.
$parent = $context->get_parent_context();
// The forced value should not be transmitted to parent contexts.
return self::get_effective_context_value($parentcontext, $element);
if (CONTEXT_SYSTEM == $parent->contextlevel) {
return self::get_effective_contextlevel_value(CONTEXT_SYSTEM, $element);
} else {
return self::get_effective_context_value($context->get_parent_context(), $element);
}
}
/**
......@@ -264,11 +242,9 @@ class data_registry {
*
* @param int $contextlevel
* @param string $element 'category' or 'purpose'
* @param int $forcedvalue Use this value as if this was this context level purpose.
* @return \tool_dataprivacy\purpose|false
*/
public static function get_effective_contextlevel_value($contextlevel, $element, $forcedvalue = false) {
public static function get_effective_contextlevel_value($contextlevel, $element) {
if ($element !== 'purpose' && $element !== 'category') {
throw new coding_exception('Only \'purpose\' and \'category\' are supported.');
}
......@@ -279,39 +255,15 @@ class data_registry {
'have a purpose or a category.');
}
if ($forcedvalue === false) {
$instance = contextlevel::get_record_by_contextlevel($contextlevel, false);
if (!$instance) {
// If the context level does not have a value defaults to not set, so we grab the context level default as
// its value.
$instancevalue = context_instance::NOTSET;
} else {
$instancevalue = $instance->get($fieldname);
}
} else {
$instancevalue = $forcedvalue;
}
list($purposeid, $categoryid) = self::get_effective_default_contextlevel_purpose_and_category($contextlevel);
// Not set -> Use the default context level value.
if ($instancevalue == context_instance::NOTSET) {
list($purposeid, $categoryid) = self::get_effective_default_contextlevel_purpose_and_category($contextlevel);
// Note: The $$fieldname points to either $purposeid, or $categoryid.
if (context_instance::NOTSET !== $$fieldname && context_instance::INHERIT !== $$fieldname) {
// There is a specific value set.
return self::get_element_instance($element, $$fieldname);
}
// Specific value for this context instance.
if ($instancevalue != context_instance::INHERIT) {
return self::get_element_instance($element, $instancevalue);
}
if ($contextlevel == CONTEXT_SYSTEM) {
throw new coding_exception('Something went wrong, system defaults should be set and we should already have a value.');
}
// If we reach this point is that we are inheriting so get the parent context level and repeat.
$parentcontextlevel = reset(self::$contextlevelinheritance[$contextlevel]);
// Forced value are intentionally not passed as the force value should only affect the immediate context level.
return self::get_effective_contextlevel_value($parentcontextlevel, $element);
throw new coding_exception('Something went wrong, system defaults should be set and we should already have a value.');
}
/**
......@@ -320,13 +272,13 @@ class data_registry {
* @param int $contextlevel
* @param int|bool $forcedpurposevalue Use this value as if this was this context level purpose.
* @param int|bool $forcedcategoryvalue Use this value as if this was this context level category.
* @param string $activity The plugin name of the activity.
* @param string $component The name of the component to check.
* @return int[]
*/
public static function get_effective_default_contextlevel_purpose_and_category($contextlevel, $forcedpurposevalue = false,
$forcedcategoryvalue = false, $activity = '') {
list($purposeid, $categoryid) = self::get_defaults($contextlevel, $activity);
$forcedcategoryvalue = false, $component = '') {
// Get the defaults for this context level.
list($purposeid, $categoryid) = self::get_defaults($contextlevel, $component);
// Honour forced values.
if ($forcedpurposevalue) {
......@@ -336,37 +288,19 @@ class data_registry {
$categoryid = $forcedcategoryvalue;
}
// Not set == INHERIT for defaults.
if ($purposeid == context_instance::INHERIT || $purposeid == context_instance::NOTSET) {
$purposeid = false;
}
if ($categoryid == context_instance::INHERIT || $categoryid == context_instance::NOTSET) {
$categoryid = false;
}
if ($contextlevel == CONTEXT_USER) {
// Only user context levels inherit from a parent context level.
list($parentpurposeid, $parentcategoryid) = self::get_defaults(CONTEXT_SYSTEM);
if ($contextlevel != CONTEXT_SYSTEM && ($purposeid === false || $categoryid === false)) {
foreach (self::$contextlevelinheritance[$contextlevel] as $parent) {
list($parentpurposeid, $parentcategoryid) = self::get_defaults($parent);
// Not set == INHERIT for defaults.
if ($parentpurposeid == context_instance::INHERIT || $parentpurposeid == context_instance::NOTSET) {
$parentpurposeid = false;
}
if ($parentcategoryid == context_instance::INHERIT || $parentcategoryid == context_instance::NOTSET) {
$parentcategoryid = false;
}
if ($purposeid === false && $parentpurposeid) {
$purposeid = $parentpurposeid;
}
if ($categoryid === false && $parentcategoryid) {
$categoryid = $parentcategoryid;
}
if (context_instance::INHERIT == $purposeid || context_instance::NOTSET == $purposeid) {
$purposeid = $parentpurposeid;
}
if (context_instance::INHERIT == $categoryid || context_instance::NOTSET == $categoryid) {
$categoryid = $parentcategoryid;
}
}
// They may still be false, but we return anyway.
return [$purposeid, $categoryid];
}
......@@ -379,7 +313,6 @@ class data_registry {
* @return \core\persistent
*/
private static function get_element_instance($element, $id) {
if ($element !== 'purpose' && $element !== 'category') {
throw new coding_exception('No other elements than purpose and category are allowed');
}
......
......@@ -1150,11 +1150,6 @@ class tool_dataprivacy_api_testcase extends advanced_testcase {
$this->assertEquals($purposes[0]->get('id'), $purposeid);
$this->assertEquals(false, $categoryid);
// Course inherits from system if not defined.
list($purposeid, $categoryid) = data_registry::get_effective_default_contextlevel_purpose_and_category(CONTEXT_COURSE);
$this->assertEquals($purposes[0]->get('id'), $purposeid);
$this->assertEquals(false, $categoryid);
// Course defined values should have preference.
list($purposevar, $categoryvar) = data_registry::var_names_from_context(
\context_helper::get_class_for_level(CONTEXT_COURSE)
......@@ -1168,159 +1163,293 @@ class tool_dataprivacy_api_testcase extends advanced_testcase {
// Context level defaults are also allowed to be set to 'inherit'.
set_config($purposevar, context_instance::INHERIT, 'tool_dataprivacy');
}
list($purposeid, $categoryid) = data_registry::get_effective_default_contextlevel_purpose_and_category(CONTEXT_COURSE);
$this->assertEquals($purposes[0]->get('id'), $purposeid);
$this->assertEquals($categories[0]->get('id'), $categoryid);
/**
* Ensure that when nothing is configured, all values return false.
*/
public function test_get_effective_contextlevel_unset() {
// Before setup, get_effective_contextlevel_purpose will return false.
$this->assertFalse(api::get_effective_contextlevel_category(CONTEXT_SYSTEM));
$this->assertFalse(api::get_effective_contextlevel_purpose(CONTEXT_SYSTEM));
list($purposeid, $categoryid) = data_registry::get_effective_default_contextlevel_purpose_and_category(CONTEXT_MODULE);
$this->assertEquals($purposes[0]->get('id'), $purposeid);
$this->assertEquals($categories[0]->get('id'), $categoryid);
$this->assertFalse(api::get_effective_contextlevel_category(CONTEXT_USER));
$this->assertFalse(api::get_effective_contextlevel_purpose(CONTEXT_USER));
}
public function test_get_effective_contextlevel_category() {
/**
* Ensure that when nothing is configured, all values return false.
*/
public function test_get_effective_context_unset() {
// Before setup, get_effective_contextlevel_purpose will return false.
$this->assertFalse(api::get_effective_contextlevel_category(CONTEXT_SYSTEM));
$this->assertFalse(api::get_effective_context_category(\context_system::instance()));
$this->assertFalse(api::get_effective_context_purpose(\context_system::instance()));
}
/**
* Ensure that fetching the effective value for context levels is only available to system, and user context levels.
*
* @dataProvider invalid_effective_contextlevel_provider
* @param int $contextlevel
*/
public function test_set_contextlevel_invalid_contextlevels($contextlevel) {
$this->expectException(coding_exception::class);
api::set_contextlevel((object) [
'contextlevel' => $contextlevel,
]);
}
/**
* Test effective contextlevel return.
*/
public function test_effective_contextlevel() {
$this->setAdminUser();
$this->resetAfterTest();
// Before setup, get_effective_contextlevel_purpose will return false.
$this->assertFalse(api::get_effective_contextlevel_purpose(CONTEXT_SYSTEM));
// Set the initial purpose and category.
$purpose1 = api::create_purpose((object)['name' => 'p1', 'retentionperiod' => 'PT1H', 'lawfulbases' => 'gdpr_art_6_1_a']);
$category1 = api::create_category((object)['name' => 'a']);
api::set_contextlevel((object)[
'contextlevel' => CONTEXT_SYSTEM,
'purposeid' => $purpose1->get('id'),
'categoryid' => $category1->get('id'),
]);
list($purposes, $categories, $courses, $modules) = $this->add_purposes_and_categories();
$this->assertEquals($purpose1, api::get_effective_contextlevel_purpose(CONTEXT_SYSTEM));
$this->assertEquals($category1, api::get_effective_contextlevel_category(CONTEXT_SYSTEM));
// Set the system context level to purpose 1.
$record = (object)[
'contextlevel' => CONTEXT_SYSTEM,
'purposeid' => $purposes[1]->get('id'),
'categoryid' => $categories[1]->get('id'),
];
api::set_contextlevel($record);
// The user context inherits from the system context when not set.
$this->assertEquals($purpose1, api::get_effective_contextlevel_purpose(CONTEXT_USER));
$this->assertEquals($category1, api::get_effective_contextlevel_category(CONTEXT_USER));
$purpose = api::get_effective_contextlevel_purpose(CONTEXT_SYSTEM);
$this->assertEquals($purposes[1]->get('id'), $purpose->get('id'));
// Forcing the behaviour to inherit will have the same result.
api::set_contextlevel((object) [
'contextlevel' => CONTEXT_USER,
'purposeid' => context_instance::INHERIT,
'categoryid' => context_instance::INHERIT,
]);
$this->assertEquals($purpose1, api::get_effective_contextlevel_purpose(CONTEXT_USER));
$this->assertEquals($category1, api::get_effective_contextlevel_category(CONTEXT_USER));
// Value 'not set' will get the default value for the context level. For context level defaults
// both 'not set' and 'inherit' result in inherit, so the parent context (system) default
// will be retrieved.
$purpose = api::get_effective_contextlevel_purpose(CONTEXT_USER);
$this->assertEquals($purposes[1]->get('id'), $purpose->get('id'));
// Setting specific values will override the inheritance behaviour.
$purpose2 = api::create_purpose((object)['name' => 'p2', 'retentionperiod' => 'PT2H', 'lawfulbases' => 'gdpr_art_6_1_a']);
$category2 = api::create_category((object)['name' => 'b']);
// Set the system context level to purpose 1.
api::set_contextlevel((object) [
'contextlevel' => CONTEXT_USER,
'purposeid' => $purpose2->get('id'),
'categoryid' => $category2->get('id'),
]);
// The behaviour forcing an inherit from context system should result in the same effective
// purpose.
$record->purposeid = context_instance::INHERIT;
$record->contextlevel = CONTEXT_USER;
api::set_contextlevel($record);
$purpose = api::get_effective_contextlevel_purpose(CONTEXT_USER);
$this->assertEquals($purposes[1]->get('id'), $purpose->get('id'));
$this->assertEquals($purpose2, api::get_effective_contextlevel_purpose(CONTEXT_USER));
$this->assertEquals($category2, api::get_effective_contextlevel_category(CONTEXT_USER));
}
$record->purposeid = $purposes[2]->get('id');
$record->contextlevel = CONTEXT_USER;
api::set_contextlevel($record);
/**
* Ensure that fetching the effective value for context levels is only available to system, and user context levels.
*
* @dataProvider invalid_effective_contextlevel_provider
* @param int $contextlevel
*/
public function test_effective_contextlevel_invalid_contextlevels($contextlevel) {
$this->resetAfterTest();
$purpose = api::get_effective_contextlevel_purpose(CONTEXT_USER);
$this->assertEquals($purposes[2]->get('id'), $purpose->get('id'));
$purpose1 = api::create_purpose((object)['name' => 'p1', 'retentionperiod' => 'PT1H', 'lawfulbases' => 'gdpr_art_6_1_a']);
$category1 = api::create_category((object)['name' => 'a']);
api::set_contextlevel((object)[
'contextlevel' => CONTEXT_SYSTEM,
'purposeid' => $purpose1->get('id'),
'categoryid' => $category1->get('id'),
]);
// Only system and user allowed.
$this->expectException(coding_exception::class);
$record->contextlevel = CONTEXT_COURSE;
$record->purposeid = $purposes[1]->get('id');
api::set_contextlevel($record);
api::get_effective_contextlevel_purpose($contextlevel);
}
/**
* Test effective context purposes and categories.
*
* @return null
* Data provider for invalid contextlevel fetchers.
*/
public function test_effective_context() {
public function invalid_effective_contextlevel_provider() {
return [
[CONTEXT_COURSECAT],
[CONTEXT_COURSE],
[CONTEXT_MODULE],
[CONTEXT_BLOCK],
];
}
/**
* Ensure that context inheritance works up the context tree.
*/
public function test_effective_context_inheritance() {
$this->resetAfterTest();
$this->setAdminUser();
$systemdata = $this->create_and_set_purpose_for_contextlevel('PT1S', CONTEXT_SYSTEM);
/*
* System
* - Cat
* - Subcat
* - Course
* - Forum
* - User
* - User block
*/
$cat = $this->getDataGenerator()->create_category();
$subcat = $this->getDataGenerator()->create_category(['parent' => $cat->id]);
$course = $this->getDataGenerator()->create_course(['category' => $subcat->id]);
$forum = $this->getDataGenerator()->create_module('forum', ['course' => $course->id]);
list(, $forumcm) = get_course_and_cm_from_instance($forum->id, 'forum');
list($purposes, $categories, $courses, $modules) = $this->add_purposes_and_categories();
$user = $this->getDataGenerator()->create_user();
// Define system defaults (all context levels below will inherit).
list($purposevar, $categoryvar) = data_registry::var_names_from_context(
\context_helper::get_class_for_level(CONTEXT_SYSTEM)
);
set_config($purposevar, $purposes[0]->get('id'), 'tool_dataprivacy');
set_config($categoryvar, $categories[0]->get('id'), 'tool_dataprivacy');
$contextsystem = \context_system::instance();
$contextcat = \context_coursecat::instance($cat->id);
$contextsubcat = \context_coursecat::instance($subcat->id);
$contextcourse = \context_course::instance($course->id);
$contextforum = \context_module::instance($forumcm->id);
$contextuser = \context_user::instance($user->id);
// Initially everything is set to Inherit.
$this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextsystem));
$this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextcat));
$this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextsubcat));
$this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextcourse));
$this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextforum));
$this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextuser));
$this->assertEquals($systemdata->category, api::get_effective_context_category($contextsystem));
$this->assertEquals($systemdata->category, api::get_effective_context_category($contextcat));
$this->assertEquals($systemdata->category, api::get_effective_context_category($contextsubcat));
$this->assertEquals($systemdata->category, api::get_effective_context_category($contextcourse));
$this->assertEquals($systemdata->category, api::get_effective_context_category($contextforum));
$this->assertEquals($systemdata->category, api::get_effective_context_category($contextuser));
// When actively set, user will use the specified value.
$userdata = $this->create_and_set_purpose_for_contextlevel('PT1S', CONTEXT_USER);
$this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextsystem));
$this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextcat));
$this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextsubcat));
$this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextcourse));
$this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextforum));
$this->assertEquals($userdata->purpose, api::get_effective_context_purpose($contextuser));
$this->assertEquals($systemdata->category, api::get_effective_context_category($contextsystem));
$this->assertEquals($systemdata->category, api::get_effective_context_category($contextcat));
$this->assertEquals($systemdata->category, api::get_effective_context_category($contextsubcat));
$this->assertEquals($systemdata->category, api::get_effective_context_category($contextcourse));
$this->assertEquals($systemdata->category, api::get_effective_context_category($contextforum));
$this->assertEquals($userdata->category, api::get_effective_context_category($contextuser));
// Set a context for the top category.
$catpurpose = new purpose(0, (object) [
'name' => 'Purpose',
'retentionperiod' => 'P1D',
'lawfulbases' => 'gdpr_art_6_1_a',
]);
$catpurpose->save();
$catcategory = new category(0, (object) ['name' => 'Category']);
$catcategory->save();
api::set_context_instance((object) [
'contextid' => $contextcat->id,
'purposeid' => $catpurpose->get('id'),
'categoryid' => $catcategory->get('id'),
]);
// Define course defaults.
list($purposevar, $categoryvar) = data_registry::var_names_from_context(
\context_helper::get_class_for_level(CONTEXT_COURSE)
);
set_config($purposevar, $purposes[1]->get('id'), 'tool_dataprivacy');
set_config($categoryvar, $categories[1]->get('id'), 'tool_dataprivacy');
$this->assertEquals($systemdata->purpose, api::get_effective_context_purpose($contextsystem));
$this->assertEquals($catpurpose, api::get_effective_context_purpose($contextcat));
$this->assertEquals($catpurpose, api::get_effective_context_purpose($contextsubcat));
$this->assertEquals($catpurpose, api::get_effective_context_purpose($contextcourse));
$this->assertEquals($catpurpose, api::get_effective_context_purpose($contextforum));
$this->assertEquals($systemdata->category, api::get_effective_context_category($contextsystem));
$this->assertEquals($catcategory, api::get_effective_context_category($contextcat));
$this->assertEquals($catcategory, api::get_effective_context_category($contextsubcat));
$this->assertEquals($catcategory, api::get_effective_context_category($contextcourse));
$this->assertEquals($catcategory, api::get_effective_context_category($contextforum));
// Set a context for the sub category.
$subcatpurpose = new purpose(0, (object) [
'name' => 'Purpose',
'retentionperiod' => 'P1D',
'lawfulbases' => 'gdpr_art_6_1_a',
]);
$subcatpurpose->save();
$subcatcategory = new category(0, (object) ['name' => 'Category']);
$subcatcategory->save();
api::set_context_instance((object) [
'contextid' => $contextsubcat->id,
'purposeid' => $subcatpurpose->get('id'),
'categoryid' => $subcatcategory->get('id'),
]);
$course0context = \context_course::instance($courses[0]->id);
$course1context = \context_course::instance($courses[1]->id);