Skip to content

Commit

Permalink
MSFTMPP-632: Soft-delete course groups when disabled, and restore whe…
Browse files Browse the repository at this point in the history
…n re-enabled
  • Loading branch information
jamesmcq committed May 10, 2017
1 parent b833867 commit b7c66e5
Show file tree
Hide file tree
Showing 4 changed files with 153 additions and 19 deletions.
107 changes: 92 additions & 15 deletions classes/feature/usergroups/coursegroups.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,44 @@ public function create_groups_for_new_courses() {
return false;
}

if (is_array($coursesenabled)) {
list($coursesinsql, $coursesparams) = $this->DB->get_in_or_equal($coursesenabled);
} else {
$coursesinsql = '';
$coursesparams = [];
}

// First process courses with groups that have been "soft-deleted".
$sql = 'SELECT crs.id as courseid,
obj.*
FROM {course} crs
JOIN {local_o365_objects} obj ON obj.type = ? AND obj.subtype = ? AND obj.moodleid = crs.id';
$params = ['group', 'course'];
if (!empty($coursesinsql)) {
$sql .= ' WHERE crs.id '.$coursesinsql;
$params = array_merge($params, $coursesparams);
}
$objectrecs = $this->DB->get_recordset_sql($sql, $params);
foreach ($objectrecs as $objectrec) {
$metadata = (!empty($objectrec->metadata)) ? @json_decode($objectrec->metadata, true) : [];
if (is_array($metadata) && !empty($metadata['softdelete'])) {
$this->mtrace('Attempting to restore group for course #'.$objectrec->courseid);
$result = $this->restore_group($objectrec->id, $objectrec->objectid, $metadata);
if ($result === true) {
$this->mtrace('....success!');
} else {
$this->mtrace('....failed. Group may have been deleted for too long.');
}
}
}

// Process courses without an associated group.
$sql = 'SELECT crs.*
FROM {course} crs
LEFT JOIN {local_o365_objects} obj ON obj.type = ? AND obj.subtype = ? AND obj.moodleid = crs.id
WHERE obj.id IS NULL AND crs.id != ?';
$params = ['group', 'course', SITEID];
if (is_array($coursesenabled)) {
list($coursesinsql, $coursesparams) = $this->DB->get_in_or_equal($coursesenabled);
if (!empty($coursesinsql)) {
$sql .= ' AND crs.id '.$coursesinsql;
$params = array_merge($params, $coursesparams);
}
Expand Down Expand Up @@ -109,6 +140,35 @@ public function create_groups_for_new_courses() {
$courses->close();
}

/**
* Restore a deleted group.
*
* @param int $objectrecid The id of the local_o365_objects record.
* @param string $objectid The O365 object id of the group.
* @param array $objectrecmetadata The metadata of the object database record.
*/
public function restore_group($objectrecid, $objectid, $objectrecmetadata) {
global $DB;
$deletedgroups = $this->graphclient->list_deleted_groups();
if (is_array($deletedgroups) && !empty($deletedgroups['value'])) {
foreach ($deletedgroups['value'] as $deletedgroup) {
if (!empty($deletedgroup) && isset($deletedgroup['id']) && $deletedgroup['id'] == $objectid) {
// Deleted group found.
$this->graphclient->restore_deleted_group($objectid);
$updatedobjectrec = new \stdClass;
$updatedobjectrec->id = $objectrecid;
unset($objectrecmetadata['softdelete']);
$updatedobjectrec->metadata = json_encode($objectrecmetadata);
$DB->update_record('local_o365_objects', $updatedobjectrec);
return true;
}
}
}
// No deleted group found. May have expired. Delete our record.
$DB->delete_records('local_o365_objects', ['id' => $objectrecid]);
return false;
}

/**
* Create an Office 365 unified group for a Moodle course.
*
Expand Down Expand Up @@ -332,6 +392,11 @@ public function replace_group_notebook_job() {
$notebookclient = new \local_o365\rest\notebook($notebooktoken, $httpclient);
$groups = $this->get_all_officegroupids_classnotebook_notpresent();

if (empty($groups)) {
$this->mtrace('No groups waiting to have class notebook created.');
return true;
}

foreach ($groups as $group) {
$o365groupid = $group->objectid;
$dbcoursegroupid = $group->id;
Expand Down Expand Up @@ -376,29 +441,41 @@ public function replace_group_notebook_job() {
* The function will fetch and return all the o365 groups for whom notebook is not replaced with class notebook.
*/
public function get_all_officegroupids_classnotebook_notpresent() {
$sql = 'SELECT cg.id, cg.groupid, obj.objectid, obj.moodleid
FROM {local_o365_objects} obj
INNER JOIN {local_o365_coursegroupdata} cg ON obj.moodleid = cg.courseid
WHERE obj.type = ? AND obj.subtype = ? AND cg.classnotebook = 0 AND cg.groupid = 0';
$notebookcourses = \local_o365\feature\usergroups\utils::get_enabled_courses_with_feature('notebook');
$allgroups = [];

$sql = 'SELECT cg.id, cg.groupid, obj.objectid, obj.moodleid
FROM {local_o365_objects} obj
INNER JOIN {local_o365_coursegroupdata} cg ON obj.moodleid = cg.courseid
WHERE obj.type = ? AND obj.subtype = ? AND cg.classnotebook = 0 AND cg.groupid = 0';
$params = ['group', 'course'];
if (is_array($notebookcourses)) {
list($coursesinsql, $coursesparams) = $this->DB->get_in_or_equal($notebookcourses);
$sql .= ' AND cg.courseid '.$coursesinsql;
$params = array_merge($params, $coursesparams);
}
$coursegroups = $this->DB->get_recordset_sql($sql, $params);
foreach ($coursegroups as $group) {
array_push($allgroups, $group);
}
$coursegroups->close();

$sql = 'SELECT cg.id, cg.groupid, obj.objectid, obj.moodleid
FROM {local_o365_objects} obj
INNER JOIN {local_o365_coursegroupdata} cg ON obj.moodleid = cg.groupid
WHERE obj.type = ? AND obj.subtype = ? AND cg.classnotebook = 0';

FROM {local_o365_objects} obj
INNER JOIN {local_o365_coursegroupdata} cg ON obj.moodleid = cg.groupid
WHERE obj.type = ? AND obj.subtype = ? AND cg.classnotebook = 0';
$params = ['group', 'usergroup'];
$incourseusergroups = $this->DB->get_recordset_sql($sql, $params);

$allgroups = array();
foreach ($coursegroups as $group) {
array_push($allgroups, $group);
if (is_array($notebookcourses)) {
list($coursesinsql, $coursesparams) = $this->DB->get_in_or_equal($notebookcourses);
$sql .= ' AND cg.courseid '.$coursesinsql;
$params = array_merge($params, $coursesparams);
}
$incourseusergroups = $this->DB->get_recordset_sql($sql, $params);
foreach ($incourseusergroups as $group) {
array_push($allgroups, $group);
}
$incourseusergroups->close();

return $allgroups;
}

Expand Down
31 changes: 29 additions & 2 deletions classes/feature/usergroups/utils.php
Original file line number Diff line number Diff line change
Expand Up @@ -574,8 +574,34 @@ public static function get_group_urls($cache, $courseid, $groupid) {
}
}

/**
* Change whether groups are enabled for a course.
/**
* (Soft) delete a course group.
*
* @param int $courseid The ID of the course.
*/
public static function delete_course_group($courseid) {
global $DB;
$params = ['type' => 'group', 'subtype' => 'course', 'moodleid' => $courseid];
$objectrec = $DB->get_record('local_o365_objects', $params);
if (!empty($objectrec)) {
$graphclient = \local_o365\rest\unified::instance_for_user(null);
$result = $graphclient->delete_group($objectrec->objectid);
if ($result === true) {
$metadata = (!empty($objectrec->metadata)) ? @json_decode($objectrec->metadata, true) : [];
if (empty($metadata) || !is_array($metadata)) {
$metadata = [];
}
$metadata['softdelete'] = true;
$updatedobject = new \stdClass;
$updatedobject->id = $objectrec->id;
$updatedobject->metadata = json_encode($metadata);
$DB->update_record('local_o365_objects', $updatedobject);
}
}
}

/**
* Change whether groups are enabled for a course.
*
* @param int $courseid The ID of the course.
* @param bool $enabled Whether to enable or disable.
Expand All @@ -592,6 +618,7 @@ public static function set_course_group_enabled($courseid, $enabled = true) {
} else {
if (isset($usergroupconfig[$courseid])) {
unset($usergroupconfig[$courseid]);
static::delete_course_group($courseid);
}
static::set_course_group_feature_enabled($courseid, ['onedrive', 'calendar', 'conversations'], $enabled);
}
Expand Down
8 changes: 6 additions & 2 deletions classes/rest/o365api.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,15 @@ public static function use_chinese_api() {
* @param int $userid The Moodle user ID to construct the API for.
* @return \local_o365\rest\o365api An instance of the requested API class with dependencies met for a given user.
*/
public static function instance_for_user($userid) {
public static function instance_for_user($userid = null) {
$httpclient = new \local_o365\httpclient();
$clientdata = \local_o365\oauth2\clientdata::instance_from_oidc();
$resource = static::get_resource();
$token = \local_o365\oauth2\token::instance($userid, $resource, $clientdata, $httpclient);
if (!empty($userid)) {
$token = \local_o365\oauth2\token::instance($userid, $resource, $clientdata, $httpclient);
} else {
$token = \local_o365\oauth2\systemtoken::instance(null, $resource, $clientdata, $httpclient);
}
if (!empty($token)) {
return new static($token, $httpclient);
} else {
Expand Down
26 changes: 26 additions & 0 deletions classes/rest/unified.php
Original file line number Diff line number Diff line change
Expand Up @@ -380,10 +380,36 @@ public function get_group_by_name($name) {
* @return bool|string True if group successfully deleted, otherwise returned string (may contain error info, etc).
*/
public function delete_group($objectid) {
if (empty($objectid)) {
return null;
}
$response = $this->apicall('delete', '/groups/'.$objectid);
return ($response === '') ? true : $response;
}

/**
* Get a list of recently deleted groups.
*
* @return array Array of returned information.
*/
public function list_deleted_groups() {
$response = $this->betaapicall('get', '/directory/deleteditems/Microsoft.Graph.Group');
$response = $this->process_apicall_response($response);
return $response;
}

/**
* Restore a recently deleted group.
*
* @param string $objectid The Object ID of the group to be restored.
* @return array Array of returned information.
*/
public function restore_deleted_group($objectid) {
$response = $this->betaapicall('post', '/directory/deleteditems/'.$objectid.'/restore');
$response = $this->process_apicall_response($response);
return $response;
}

/**
* Get a list of group members.
*
Expand Down

0 comments on commit b7c66e5

Please sign in to comment.