@@ -1118,6 +1118,10 @@ private function restore_group(int $objectrecid, string $objectid, array $object
11181118 /**
11191119 * Update Teams cache.
11201120 *
1121+ * @deprecated Use \local_o365\utils::update_groups_cache() instead, which handles both
1122+ * groups and teams in the unified cache table. This function is no longer
1123+ * called by any production code and will be removed in a future version.
1124+ *
11211125 * @return bool
11221126 */
11231127 public function update_teams_cache (): bool {
@@ -1152,10 +1156,10 @@ public function update_teams_cache(): bool {
11521156 return false ;
11531157 }
11541158
1155- // Build existing teams records cache.
1159+ // Build existing teams records cache from unified groups cache table .
11561160 $ this ->mtrace ('Building existing teams cache records ' , 1 );
11571161 // Use recordset instead of get_records to reduce memory usage.
1158- $ existingcacherecordset = $ DB ->get_recordset ('local_o365_teams_cache ' );
1162+ $ existingcacherecordset = $ DB ->get_recordset ('local_o365_groups_cache ' , [ ' has_team ' => 1 ] );
11591163 $ existingcachebyoid = [];
11601164 foreach ($ existingcacherecordset as $ existingcacherecord ) {
11611165 $ existingcachebyoid [$ existingcacherecord ->objectid ] = $ existingcacherecord ;
@@ -1168,10 +1172,12 @@ public function update_teams_cache(): bool {
11681172 foreach ($ teams as $ team ) {
11691173 if (array_key_exists ($ team ['id ' ], $ existingcachebyoid )) {
11701174 // Update existing cache record.
1175+ $ lockstatuschecked = false ;
11711176 if (!$ existingcachebyoid [$ team ['id ' ]]->locked ) {
11721177 // Need to update lock status.
11731178 try {
11741179 [$ rawteam , $ teamurl , $ lockstatus ] = $ this ->graphclient ->get_team ($ team ['id ' ]);
1180+ $ lockstatuschecked = true ;
11751181 } catch (moodle_exception $ e ) {
11761182 continue ;
11771183 }
@@ -1183,7 +1189,10 @@ public function update_teams_cache(): bool {
11831189 $ cacherecord ->name = $ team ['displayName ' ];
11841190 $ cacherecord ->description = $ team ['description ' ];
11851191 $ cacherecord ->locked = $ lockstatus ;
1186- $ DB ->update_record ('local_o365_teams_cache ' , $ cacherecord );
1192+ if ($ lockstatuschecked ) {
1193+ $ cacherecord ->lock_status_last_checked = time ();
1194+ }
1195+ $ DB ->update_record ('local_o365_groups_cache ' , $ cacherecord );
11871196
11881197 unset($ existingcachebyoid [$ team ['id ' ]]);
11891198 } else {
@@ -1194,20 +1203,47 @@ public function update_teams_cache(): bool {
11941203 continue ;
11951204 }
11961205
1197- // Create new cache record.
1198- $ cacherecord = new stdClass ();
1199- $ cacherecord ->objectid = $ team ['id ' ];
1200- $ cacherecord ->name = $ team ['displayName ' ];
1201- $ cacherecord ->description = $ team ['description ' ];
1202- $ cacherecord ->url = $ teamurl ;
1203- $ cacherecord ->locked = $ lockstatus ;
1204- $ DB ->insert_record ('local_o365_teams_cache ' , $ cacherecord );
1206+ // Upsert: a group-only row (has_team=0) may already exist for this objectid.
1207+ // Inserting a second row would create duplicates and break get_record() calls.
1208+ $ cacherecord = $ DB ->get_record ('local_o365_groups_cache ' , ['objectid ' => $ team ['id ' ]]);
1209+ if ($ cacherecord ) {
1210+ // Promote the existing group row to a team row.
1211+ $ cacherecord ->name = $ team ['displayName ' ];
1212+ $ cacherecord ->description = $ team ['description ' ];
1213+ $ cacherecord ->has_team = 1 ;
1214+ $ cacherecord ->url = $ teamurl ;
1215+ $ cacherecord ->locked = $ lockstatus ;
1216+ $ cacherecord ->not_found_since = 0 ;
1217+ $ cacherecord ->team_details_last_attempted = time ();
1218+ $ cacherecord ->lock_status_last_checked = time ();
1219+ $ DB ->update_record ('local_o365_groups_cache ' , $ cacherecord );
1220+ } else {
1221+ $ cacherecord = new stdClass ();
1222+ $ cacherecord ->objectid = $ team ['id ' ];
1223+ $ cacherecord ->name = $ team ['displayName ' ];
1224+ $ cacherecord ->description = $ team ['description ' ];
1225+ $ cacherecord ->has_team = 1 ;
1226+ $ cacherecord ->url = $ teamurl ;
1227+ $ cacherecord ->locked = $ lockstatus ;
1228+ $ cacherecord ->not_found_since = 0 ;
1229+ $ cacherecord ->team_details_last_attempted = 0 ;
1230+ $ cacherecord ->lock_status_last_checked = 0 ;
1231+ $ DB ->insert_record ('local_o365_groups_cache ' , $ cacherecord );
1232+ }
12051233 }
12061234 }
12071235
1208- $ this ->mtrace ('Deleting old teams cache records ' , 1 );
1236+ // Demote groups that are no longer teams: set has_team=0 and clear team-only fields.
1237+ // Deleting the row would cause data loss if the underlying group still exists;
1238+ // this matches the demotion behaviour in utils::update_groups_cache().
1239+ $ this ->mtrace ('Demoting old teams cache records to group-only ' , 1 );
12091240 foreach ($ existingcachebyoid as $ oldcacherecord ) {
1210- $ DB ->delete_records ('local_o365_teams_cache ' , ['id ' => $ oldcacherecord ->id ]);
1241+ $ oldcacherecord ->has_team = 0 ;
1242+ $ oldcacherecord ->url = null ;
1243+ $ oldcacherecord ->locked = null ;
1244+ $ oldcacherecord ->team_details_last_attempted = 0 ;
1245+ $ oldcacherecord ->lock_status_last_checked = 0 ;
1246+ $ DB ->update_record ('local_o365_groups_cache ' , $ oldcacherecord );
12111247 }
12121248
12131249 $ this ->mtrace ('Finished updating teams cache. ' );
@@ -1222,15 +1258,17 @@ public function update_teams_cache(): bool {
12221258
12231259 /**
12241260 * Cleanup Teams connections records.
1225- * This function will delete all Teams connection records with object IDs not found in the Teams cache.
1226- * This function should only be called after Teams cache is updated - no cache update will be performed here.
1261+ * This function will delete all Teams connection records with object IDs not found in the cache.
1262+ * Teams are identified in the cache by has_team=1.
1263+ * This function should only be called after the cache is updated - no cache update will be performed here.
12271264 *
12281265 * @return void
12291266 */
12301267 public function cleanup_teams_connections () {
12311268 global $ DB ;
12321269
1233- $ teamobjectids = $ DB ->get_fieldset_select ('local_o365_teams_cache ' , 'objectid ' , '' );
1270+ // Get all team object IDs from the unified cache (has_team=1).
1271+ $ teamobjectids = $ DB ->get_fieldset_select ('local_o365_groups_cache ' , 'objectid ' , 'has_team = 1 ' );
12341272
12351273 $ this ->mtrace ('Clean up teams connection records... ' );
12361274 if ($ teamobjectids ) {
@@ -1679,12 +1717,12 @@ public function get_all_group_ids(): array {
16791717 public function is_team_locked (string $ groupobjectid ) {
16801718 global $ DB ;
16811719
1682- if ($ teamcacherecord = $ DB ->get_record ('local_o365_teams_cache ' , ['objectid ' => $ groupobjectid ])) {
1720+ if ($ teamcacherecord = $ DB ->get_record ('local_o365_groups_cache ' , ['objectid ' => $ groupobjectid, ' has_team ' => 1 ])) {
16831721 switch ($ teamcacherecord ->locked ) {
16841722 case TEAM_LOCKED_STATUS_UNKNOWN :
16851723 try {
16861724 [$ team , $ teamurl , $ lockstatus ] = $ this ->graphclient ->get_team ($ groupobjectid );
1687- $ DB ->set_field ('local_o365_teams_cache ' , 'locked ' , $ lockstatus , ['objectid ' => $ groupobjectid ]);
1725+ $ DB ->set_field ('local_o365_groups_cache ' , 'locked ' , $ lockstatus , ['objectid ' => $ groupobjectid ]);
16881726 } catch (moodle_exception $ e ) {
16891727 $ lockstatus = TEAM_UNLOCKED ;
16901728 }
@@ -1693,7 +1731,7 @@ public function is_team_locked(string $groupobjectid) {
16931731 try {
16941732 [$ team , $ teamurl , $ lockstatus ] = $ this ->graphclient ->get_team ($ groupobjectid );
16951733 if ($ lockstatus == TEAM_UNLOCKED ) {
1696- $ DB ->set_field ('local_o365_teams_cache ' , 'locked ' , $ lockstatus , ['objectid ' => $ groupobjectid ]);
1734+ $ DB ->set_field ('local_o365_groups_cache ' , 'locked ' , $ lockstatus , ['objectid ' => $ groupobjectid ]);
16971735 }
16981736 } catch (moodle_exception $ e ) {
16991737 $ lockstatus = TEAM_UNLOCKED ;
0 commit comments