-
-
Notifications
You must be signed in to change notification settings - Fork 1k
SAK-52355 Microsoft - Add the posibility to enable a new forced synchronization in Microsoft Team for the site #14514
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
8c59f82
c69f7c5
ad23132
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -118,8 +118,8 @@ public String autoConfig( | |
|
|
||
| if(!autoConfigSessionBean.isRunning()) { | ||
|
|
||
| //get all synchronizations | ||
| List<SiteSynchronization> ssList = microsoftSynchronizationService.getAllSiteSynchronizations(false); | ||
| //get all enabled synchronizations | ||
| List<SiteSynchronization> ssList = microsoftSynchronizationService.getAllEnabledSiteSynchronizations(false); | ||
|
|
||
|
Comment on lines
+121
to
123
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Auto-config should exclude existing synchronizations regardless of disabled state. This code now filters out only enabled synchronizations before building candidate site/team maps. Disabled synchronizations still exist in persistence, so they can be re-proposed and create duplicate/conflicting relationships. 🛠️ Suggested direction-List<SiteSynchronization> ssList = microsoftSynchronizationService.getAllEnabledSiteSynchronizations(false);
+// Use a method that returns all persisted synchronizations (enabled + disabled)
+List<SiteSynchronization> ssList = microsoftSynchronizationService.getAllSiteSynchronizations(false);If 🤖 Prompt for AI Agents |
||
| //get (filtered) sites | ||
| List<Site> sitesList = sakaiProxy.getSakaiSites(requestBody.getFilter()); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -113,6 +113,35 @@ <h1 th:text="#{index_title}"></h1> | |
| } | ||
| } | ||
|
|
||
| async function toggleDisabled(elem) { | ||
| let baseURL = "[(@{/setDisabled-siteSynchronization/})]"; | ||
| let rowId = elem.closest('.table-row').id.replace('row_', ''); | ||
| let icon = elem.querySelector('i'); | ||
| let isDisabled = icon.classList.contains('fa-toggle-off'); | ||
|
|
||
| let url = baseURL + rowId + "?disabled=" + !isDisabled; | ||
|
|
||
| let response = await fetch(url, { | ||
| method: 'POST', | ||
| headers: { | ||
| 'X-Requested-With': 'XMLHttpRequest' | ||
| } | ||
| }); | ||
| let data = await response.json(); | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
| if (data.status == false) { | ||
| showAjaxError(data.error); | ||
| } else { | ||
| if (!isDisabled) { | ||
| icon.classList.replace('fa-toggle-on', 'fa-toggle-off'); | ||
| elem.title = /*[[#{index.enable}]]*/ ''; | ||
| } else { | ||
| icon.classList.replace('fa-toggle-off', 'fa-toggle-on'); | ||
| elem.title = /*[[#{index.disable}]]*/ ''; | ||
| } | ||
|
Comment on lines
+135
to
+141
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Update the control’s accessible name when toggle state changes. On success, only ♿ Proposed fix if (data.status == false) {
showAjaxError(data.error);
} else {
+ let nextLabel;
if (!isDisabled) {
icon.classList.replace('fa-toggle-on', 'fa-toggle-off');
- elem.title = /*[[#{index.enable}]]*/ '';
+ nextLabel = /*[[#{index.enable}]]*/ '';
} else {
icon.classList.replace('fa-toggle-off', 'fa-toggle-on');
- elem.title = /*[[#{index.disable}]]*/ '';
+ nextLabel = /*[[#{index.disable}]]*/ '';
}
+ elem.title = nextLabel;
+ elem.setAttribute('aria-label', nextLabel);
+ const srText = elem.querySelector('.sr-only, .visually-hidden');
+ if (srText) srText.textContent = nextLabel;
}As per coding guidelines, 🤖 Prompt for AI Agents |
||
| } | ||
| } | ||
|
|
||
| var dateTimeout = null; | ||
| async function changeDate(input) { | ||
| if(dateTimeout != null){ | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -45,6 +45,7 @@ | |
| import com.google.gson.Gson; | ||
| import com.google.gson.JsonPrimitive; | ||
| import com.google.gson.JsonElement; | ||
| import com.google.gson.JsonObject; | ||
| import com.microsoft.graph.requests.GraphServiceClient; | ||
| import com.microsoft.graph.requests.UserCollectionPage; | ||
| import com.microsoft.graph.requests.GroupCollectionPage; | ||
|
|
@@ -147,7 +148,9 @@ | |
| import com.microsoft.graph.models.OnlineMeetingRole; | ||
| import com.microsoft.graph.models.Permission; | ||
| import com.microsoft.graph.models.PermissionGrantParameterSet; | ||
| import com.microsoft.graph.models.Site; | ||
| import com.microsoft.graph.models.Team; | ||
| import com.microsoft.graph.models.TeamArchiveParameterSet; | ||
| import com.microsoft.graph.models.TeamVisibilityType; | ||
| import com.microsoft.graph.models.ThumbnailSet; | ||
| import com.microsoft.graph.models.UploadSession; | ||
|
|
@@ -1010,6 +1013,87 @@ public boolean deleteTeam(String teamId) throws MicrosoftCredentialsException { | |
| return false; | ||
| } | ||
|
|
||
| public boolean archiveTeam(String teamId) throws MicrosoftCredentialsException { | ||
| try { | ||
| // 1. Archive team without shouldSetSpoSiteReadOnlyForMembers (no supported in app-only) | ||
| TeamArchiveParameterSet requestBody = TeamArchiveParameterSet | ||
| .newBuilder() | ||
| .withShouldSetSpoSiteReadOnlyForMembers(false) | ||
| .build(); | ||
|
|
||
| getGraphClient().teams(teamId) | ||
| .archive(requestBody) | ||
| .buildRequest() | ||
| .post(); | ||
|
|
||
| // 2. Obtain the associated SharePoint site to ensure the team is fully archived before setting it to read-only | ||
| Site site = getGraphClient().groups(teamId) | ||
| .sites("root") | ||
| .buildRequest() | ||
| .get(); | ||
|
|
||
| if (site == null || site.id == null) { | ||
| log.error("Could not retrieve SharePoint site for team: {}, site will not be set to read-only", teamId); | ||
| return false; | ||
| } | ||
|
|
||
| // 3. Set SharePoint site to read-only (as a backup in case the archive operation did not set it correctly) | ||
| JsonObject jsonBody = new JsonObject(); | ||
| jsonBody.addProperty("lockState", "readOnly"); | ||
|
|
||
| getGraphClient().customRequest("/sites/" + site.id) | ||
| .buildRequest() | ||
| .patch(jsonBody); | ||
|
|
||
| log.info("Team archived and SharePoint site set to read-only: teamId={}", teamId); | ||
|
|
||
| return true; | ||
| } catch(MicrosoftCredentialsException e) { | ||
| throw e; | ||
| } catch(Exception ex){ | ||
| log.error("Error archiving Microsoft team: id={}", teamId, ex); | ||
| } | ||
| return false; | ||
| } | ||
|
|
||
| public boolean unarchiveTeam(String teamId) throws MicrosoftCredentialsException { | ||
| try { | ||
| //1. Unarchive team | ||
| getGraphClient().teams(teamId) | ||
| .unarchive() | ||
| .buildRequest() | ||
| .post(); | ||
|
|
||
| // 2. Obtain the associated SharePoint site to ensure the team is fully unarchived before returning | ||
| Site site = getGraphClient().groups(teamId) | ||
| .sites("root") | ||
| .buildRequest() | ||
| .get(); | ||
|
|
||
| if (site == null || site.id == null) { | ||
| log.error("Could not retrieve SharePoint site for team: {}, site will remain read-only", teamId); | ||
| return false; | ||
| } | ||
|
|
||
| //3. Set SharePoint site to unlocked (as a backup in case the unarchive operation did not set it correctly) | ||
| JsonObject jsonBody = new JsonObject(); | ||
| jsonBody.addProperty("lockState", "unlocked"); | ||
|
|
||
| getGraphClient().customRequest("/sites/" + site.id) | ||
| .buildRequest() | ||
| .patch(jsonBody); | ||
|
|
||
| log.info("Team unarchived and SharePoint site unlocked: teamId={}", teamId); | ||
|
|
||
| return true; | ||
| } catch (MicrosoftCredentialsException e) { | ||
| throw e; | ||
| } catch (Exception ex) { | ||
| log.error("Error unarchiving Microsoft team: id={}", teamId, ex); | ||
| } | ||
| return false; | ||
| } | ||
|
Comment on lines
+1016
to
+1095
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Find archive/unarchive call sites and inspect whether return values are consumed
rg -nP --type=java '\b(?:archiveTeam|unarchiveTeam)\s*\(' -C3
# Find standalone invocation statements (likely ignored return value)
ast-grep --pattern '$OBJ.archiveTeam($ARG);'
ast-grep --pattern '$OBJ.unarchiveTeam($ARG);'Repository: sakaiproject/sakai Length of output: 6413 Return values from archiveTeam() and unarchiveTeam() are ignored, allowing silent failures to proceed as success. Callers in Either check the return value and handle failure ( 🤖 Prompt for AI Agents |
||
|
|
||
| @Override | ||
| public MicrosoftMembersCollection getTeamMembers(String id, MicrosoftUserIdentifier key) throws MicrosoftCredentialsException { | ||
| MicrosoftMembersCollection ret = new MicrosoftMembersCollection(); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct the documented default for forced site-property mapping.
Line 5353 currently states a default of
microsoft.synchronized=true, but runtime behavior treats this setting as optional/unset by default (no property is applied when blank). This can mislead admins during configuration.🛠️ Proposed doc-only fix
🤖 Prompt for AI Agents