diff --git a/microsoft-integration/admin-tool/src/main/java/org/sakaiproject/microsoft/controller/AutoConfigController.java b/microsoft-integration/admin-tool/src/main/java/org/sakaiproject/microsoft/controller/AutoConfigController.java index 40d5684479ec..5c9f2732d6a9 100644 --- a/microsoft-integration/admin-tool/src/main/java/org/sakaiproject/microsoft/controller/AutoConfigController.java +++ b/microsoft-integration/admin-tool/src/main/java/org/sakaiproject/microsoft/controller/AutoConfigController.java @@ -15,33 +15,28 @@ */ package org.sakaiproject.microsoft.controller; -import java.time.ZonedDateTime; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.function.Function; -import java.util.stream.Collectors; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpSession; - +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.sakaiproject.microsoft.api.MicrosoftCommonService; import org.sakaiproject.microsoft.api.MicrosoftConfigurationService; +import org.sakaiproject.microsoft.api.MicrosoftLoggingService; import org.sakaiproject.microsoft.api.MicrosoftSynchronizationService; import org.sakaiproject.microsoft.api.SakaiProxy; +import org.sakaiproject.microsoft.api.data.AutoConfigProcessStatus; +import org.sakaiproject.microsoft.api.data.CreationStatus; import org.sakaiproject.microsoft.api.data.MicrosoftChannel; import org.sakaiproject.microsoft.api.data.MicrosoftCredentials; +import org.sakaiproject.microsoft.api.data.MicrosoftLogInvokers; import org.sakaiproject.microsoft.api.data.MicrosoftTeam; -import org.sakaiproject.microsoft.api.exceptions.MicrosoftCredentialsException; +import org.sakaiproject.microsoft.api.data.SynchronizationStatus; import org.sakaiproject.microsoft.api.model.GroupSynchronization; +import org.sakaiproject.microsoft.api.model.MicrosoftLog; import org.sakaiproject.microsoft.api.model.SiteSynchronization; -import org.sakaiproject.microsoft.controller.auxiliar.AutoConfigSessionBean; +import org.sakaiproject.microsoft.api.persistence.MicrosoftLoggingRepository; import org.sakaiproject.microsoft.controller.auxiliar.AutoConfigConfirmRequest; import org.sakaiproject.microsoft.controller.auxiliar.AutoConfigRequest; +import org.sakaiproject.microsoft.controller.auxiliar.AutoConfigSessionBean; import org.sakaiproject.site.api.Group; import org.sakaiproject.site.api.Site; import org.sakaiproject.util.ResourceLoader; @@ -49,12 +44,28 @@ import org.springframework.http.MediaType; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; +import org.springframework.util.CollectionUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.ResponseBody; -import lombok.extern.slf4j.Slf4j; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Collectors; + +import static org.sakaiproject.microsoft.api.MicrosoftCommonService.MAX_ADD_CHANNELS; +import static org.sakaiproject.microsoft.api.MicrosoftCommonService.MAX_CHANNELS; /** @@ -68,7 +79,10 @@ public class AutoConfigController { private static ResourceLoader rb = new ResourceLoader("Messages"); - + @Setter + private MicrosoftLoggingRepository microsoftLoggingRepository; + @Autowired + private MicrosoftLoggingService microsoftLoggingService; @Autowired private MicrosoftSynchronizationService microsoftSynchronizationService; @@ -138,9 +152,9 @@ public String autoConfig( //just to display it in the confirmation table site.setTitle(title); } - final String finalTitle = title; - - //check if there is a, not-used Team that matched the Site + final String finalTitle = microsoftCommonService.processMicrosoftTeamName(title); + + //check if there is a, not-used Team that matched the Site MicrosoftTeam team = teamsMap.values().stream().filter(t -> t.getName().equalsIgnoreCase(finalTitle)).findAny().orElse(null); //match found @@ -203,9 +217,10 @@ public String autoConfig( public Boolean autoConfigConfirm( @RequestBody AutoConfigConfirmRequest payload, HttpServletRequest request, - Model model - ) throws Exception { - if(payload.getSiteIdList() == null || payload.getSiteIdList().size() == 0 || payload.getSyncDateFrom() == null || payload.getSyncDateTo() == null) { + Model model) throws Exception { + + if (CollectionUtils.isEmpty(payload.getSiteIdList()) || + payload.getSyncDateFrom() == null || payload.getSyncDateTo() == null) { return false; } @@ -214,159 +229,52 @@ public Boolean autoConfigConfirm( HttpSession session = request.getSession(); MicrosoftCredentials credentials = microsoftConfigurationService.getCredentials(); + new Thread(() -> { - AutoConfigSessionBean autoConfigSessionBean_aux = null; + AutoConfigSessionBean autoConfigSessionBean; synchronized (session) { - autoConfigSessionBean_aux = (AutoConfigSessionBean)session.getAttribute("AutoConfigSessionBean"); - if(autoConfigSessionBean_aux == null) { - autoConfigSessionBean_aux = new AutoConfigSessionBean(); - session.setAttribute("AutoConfigSessionBean", autoConfigSessionBean_aux); + autoConfigSessionBean = (AutoConfigSessionBean) session.getAttribute("AutoConfigSessionBean"); + if (autoConfigSessionBean == null) { + autoConfigSessionBean = new AutoConfigSessionBean(); + session.setAttribute("AutoConfigSessionBean", autoConfigSessionBean); } } - AutoConfigSessionBean autoConfigSessionBean = autoConfigSessionBean_aux; - - if(!autoConfigSessionBean.isRunning()) { - //start running + + if (!autoConfigSessionBean.isRunning()) { autoConfigSessionBean.startRunning(payload.getSiteIdList().size()); - + autoConfigSessionBean.addStatus(AutoConfigProcessStatus.START_RUNNING, ""); + Map map = autoConfigSessionBean.getConfirmMap(); - - for(String siteId : payload.getSiteIdList()) { - //get stored site from session bean + + for (String siteId : payload.getSiteIdList()) { Site site = autoConfigSessionBean.getSitesMap().get(siteId); - if(site != null) { - Object o = map.get(siteId); - if(o != null) { - if(o instanceof String) { - try { - //--> create NEW Team - String teamId = microsoftCommonService.createTeam((String)o, credentials.getEmail()); - if(teamId != null) { - //create relationship - SiteSynchronization ss = SiteSynchronization.builder() - .siteId(siteId) - .teamId(teamId) - .forced(false) - .syncDateFrom(syncDateFrom) - .syncDateTo(syncDateTo) - .build(); - - log.debug("saving NEW: siteId={}, teamId={}", siteId, teamId); - microsoftSynchronizationService.saveOrUpdateSiteSynchronization(ss); - - //check if given site has groups and configuration allows it - if(autoConfigSessionBean.isNewChannel() && site.getGroups().size() > 0) { - for(Group g : site.getGroups()) { - try { - //exclude automatic lesson groups - if(g.getTitle().startsWith("Access:")) { - continue; - } - - //as Team is new, create all Channels - String createdChannelId = microsoftCommonService.createChannel(teamId, g.getTitle(), credentials.getEmail()); - - if(StringUtils.isNotBlank(createdChannelId)) { - //create relationship - GroupSynchronization gs = GroupSynchronization.builder() - .siteSynchronization(ss) - .groupId(g.getId()) - .channelId(createdChannelId) - .build(); - - log.debug("saving NEW: groupId={}, channelId={}, title={}", g.getId(), createdChannelId, g.getTitle()); - microsoftSynchronizationService.saveOrUpdateGroupSynchronization(gs); - } - }catch(Exception e) { - log.error("Unexpected exception creating channel: {}", e.getMessage()); - } - } - } - autoConfigSessionBean.increaseCounter(); - } else { - //mark this site as error - autoConfigSessionBean.addError(siteId, site.getTitle(), rb.getString("error.creating_team")); - } - - } catch (MicrosoftCredentialsException e) { - autoConfigSessionBean.addError(siteId, site.getTitle(), rb.getString(e.getMessage())); - } - //Team already exists and matches Site's title - } else if(o instanceof SiteSynchronization) { - SiteSynchronization ss = (SiteSynchronization)o; - - SiteSynchronization aux_ss = microsoftSynchronizationService.getSiteSynchronization(ss); - //check if ss already exists (this should never happen) - if(aux_ss != null) { - //mark this site as error - autoConfigSessionBean.addError(siteId, site.getTitle(), rb.getString("error.site_synchronization_already_exists")); - continue; - } - - //not forced -> check if exists some forced synchronization - if(microsoftSynchronizationService.countSiteSynchronizationsByTeamId(ss.getTeamId(), true) > 0) { - //mark this site as error - autoConfigSessionBean.addError(siteId, site.getTitle(), rb.getString("error.site_synchronization_already_forced")); - continue; - } - - //set dates - ss.setSyncDateFrom(syncDateFrom); - ss.setSyncDateTo(syncDateTo); - - log.debug("saving site-team: siteId={}, teamId={}", siteId, ss.getTeamId()); - microsoftSynchronizationService.saveOrUpdateSiteSynchronization(ss); - - //check groups-channels - try { - if(site.getGroups().size() > 0) { - //get existing channels from Team - Map channelsMap = microsoftCommonService.getTeamPrivateChannels(ss.getTeamId(), true); - - //get existing groups from site - for(Group g : site.getGroups()) { - //exclude automatic lesson groups - if(g.getTitle().startsWith("Access:")) { - continue; - } - - //check if any group matches any channel - MicrosoftChannel channel = channelsMap.values().stream().filter(c -> c.getName().equalsIgnoreCase(g.getTitle())).findAny().orElse(null); - String channelId = (channel != null) ? channel.getId() : null; - - //match NOT found --> Create channel (if configuration allows it) - if(channel == null && autoConfigSessionBean.isNewChannel()) { - channelId = microsoftCommonService.createChannel(ss.getTeamId(), g.getTitle(), credentials.getEmail()); - } - - if(StringUtils.isNotBlank(channelId)) { - //create relationship - GroupSynchronization gs = GroupSynchronization.builder() - .siteSynchronization(ss) - .groupId(g.getId()) - .channelId(channelId) - .build(); - - //check if Group Synchronization does not exist - GroupSynchronization aux_gs = microsoftSynchronizationService.getGroupSynchronization(gs); - if(aux_gs == null) { - log.debug("saving group-channel: groupId={}, channelId={}", g.getId(), channelId); - microsoftSynchronizationService.saveOrUpdateGroupSynchronization(gs); - } - } - } - } - } catch (MicrosoftCredentialsException e) { - log.error("MicrosoftCredentialsException in confirm thread"); - } - autoConfigSessionBean.increaseCounter(); - } + if (site == null) { + continue; + } + + Object o = map.get(siteId); + if (o == null) { + continue; + } + + try { + if (o instanceof String) { + autoConfigSessionBean.addStatus(AutoConfigProcessStatus.CREATING_TEAM, site.getTitle()); + handleNewTeamCreation(autoConfigSessionBean, site, (String) o, syncDateFrom, syncDateTo, credentials); + } else if (o instanceof SiteSynchronization) { + autoConfigSessionBean.addStatus(AutoConfigProcessStatus.BINDING_TEAM, site.getTitle()); + handleExistingTeamBinding(autoConfigSessionBean, site, (SiteSynchronization) o, syncDateFrom, syncDateTo, credentials); } + } catch (Exception e) { + log.error("Error handling site " + siteId, e); + autoConfigSessionBean.addError(siteId, site.getTitle(), e.getMessage()); + } finally { + autoConfigSessionBean.increaseCounter(); } } - - //end running - if(autoConfigSessionBean.getCount() >= autoConfigSessionBean.getTotal()) { + + if (autoConfigSessionBean.getCount() >= autoConfigSessionBean.getTotal()) { + autoConfigSessionBean.addStatus(AutoConfigProcessStatus.END_RUNNING, ""); autoConfigSessionBean.finishRunning(); } } @@ -374,13 +282,206 @@ public Boolean autoConfigConfirm( return true; } - + + + public void handleNewTeamCreation(AutoConfigSessionBean autoConfigSessionBean, Site site, String teamName, ZonedDateTime syncDateFrom, ZonedDateTime syncDateTo, MicrosoftCredentials credentials) throws Exception { + String teamId = microsoftCommonService.createTeam(teamName, credentials.getEmail()); + SiteSynchronization ss = SiteSynchronization.builder() + .siteId(site.getId()) + .teamId(teamId != null ? teamId : "") + .forced(false) + .syncDateFrom(syncDateFrom) + .syncDateTo(syncDateTo) + .creationStatus(CreationStatus.OK) + .build(); + + if (teamId == null) { + ss.setStatus(SynchronizationStatus.NOT_AVAILABLE); + ss.setCreationStatus(CreationStatus.KO); + + autoConfigSessionBean.addError(site.getId(), site.getTitle(), rb.getString("error.creating_team")); + microsoftSynchronizationService.saveOrUpdateSiteSynchronization(ss); + microsoftLoggingService.saveLog(MicrosoftLog.builder() + .event(MicrosoftLog.ERROR_TEAM_ID_NULL) + .status(MicrosoftLog.Status.KO) + .addData("origin", MicrosoftLogInvokers.MANUAL.getCode()) + .addData("teamId", teamId) + .addData("siteId", site.getId()) + .addData("siteTitle", site.getTitle()) + .addData("teamTitle", teamName) + .build()); + return; + } + + microsoftLoggingService.saveLog(MicrosoftLog.builder() + .event(MicrosoftLog.EVENT_CREATE_TEAM_FROM_SITE) + .status(MicrosoftLog.Status.OK) + .addData("origin", MicrosoftLogInvokers.MANUAL.getCode()) + .addData("siteId", site.getId()) + .addData("siteTitle", site.getTitle()) + .addData("teamId", teamId) + .addData("teamTitle", teamName) + .build()); + + boolean limitExceeded = site.getGroups().size() > MAX_CHANNELS; + List groupsToProcess = limitGroups(site.getGroups().stream().filter(g -> !g.getTitle().startsWith("Access:")).collect(Collectors.toList())); + + if (limitExceeded) { + ss.setCreationStatus(CreationStatus.PARTIAL_OK); + } + + microsoftSynchronizationService.saveOrUpdateSiteSynchronization(ss); + + List channels = microsoftCommonService.createChannels(groupsToProcess, teamId, credentials.getEmail()); + + for (Group g : groupsToProcess) { + Optional channelOpt = channels.stream() + .filter(c -> c.getName().replace(" ", "").equalsIgnoreCase(microsoftCommonService.processMicrosoftChannelName(g.getTitle()).replace(" ", ""))).findFirst(); + + channelOpt.ifPresent(channel -> { + GroupSynchronization gs = GroupSynchronization.builder() + .siteSynchronization(ss) + .groupId(g.getId()) + .channelId(channel.getId()) + .build(); + microsoftSynchronizationService.saveOrUpdateGroupSynchronization(gs); + }); + } + String groupsIds = groupsToProcess.stream() + .map(group -> group.getId().toString()) + .collect(Collectors.joining(",")); + + String groupsNames = groupsToProcess.stream() + .map(group -> group.getTitle().toString()) + .collect(Collectors.joining(",")); + + String channelIds = channels.stream() + .map(channel -> channel.getId().toString()) + .collect(Collectors.joining(",")); + + String channelNames = channels.stream() + .map(channel -> channel.getName().toString()) + .collect(Collectors.joining(",")); + + microsoftLoggingService.saveLog(MicrosoftLog.builder() + .event(MicrosoftLog.EVENT_CHANNEL_PRESENT_ON_GROUP) + .status(MicrosoftLog.Status.OK) + .addData("origin", MicrosoftLogInvokers.MANUAL.getCode()) + .addData("siteId", site.getId()) + .addData("siteTitle", site.getTitle()) + .addData("processGroupsIds", groupsIds) + .addData("processGroupsNames", groupsNames) + .addData("numberGroupsOnSite", String.valueOf(site.getGroups().size())) + .addData("numberLimitedGroups", String.valueOf(groupsToProcess.size())) + .addData("numberNonProcessedGroups", String.valueOf(site.getGroups().size() - groupsToProcess.size())) + .addData("teamId", teamId) + .addData("teamTitle", teamName) + .addData("createChannelsId", channelIds) + .addData("createChannelsName", channelNames) + .build()); + + } + + public void handleExistingTeamBinding(AutoConfigSessionBean autoConfigSessionBean, Site site, SiteSynchronization ss, ZonedDateTime syncDateFrom, ZonedDateTime syncDateTo, MicrosoftCredentials credentials) throws Exception { + + microsoftLoggingService.saveLog(MicrosoftLog.builder() + .event(MicrosoftLog.BINDING_TEAM_FROM_SITE) + .status(!site.getId().isBlank() ? MicrosoftLog.Status.OK : MicrosoftLog.Status.KO) + .addData("origin", MicrosoftLogInvokers.MANUAL.getCode()) + .addData("siteId", site.getId()) + .addData("siteTitle", site.getTitle()) + .addData("teamId", ss.getTeamId()) + .build()); + + boolean limitExceeded = site.getGroups().size() > MAX_CHANNELS; + ss.setSyncDateFrom(syncDateFrom); + ss.setSyncDateTo(syncDateTo); + ss.setCreationStatus(limitExceeded ? CreationStatus.PARTIAL_OK : CreationStatus.OK); + + microsoftSynchronizationService.saveOrUpdateSiteSynchronization(ss); + + Map channelsMap = microsoftCommonService.getTeamPrivateChannels(ss.getTeamId(), true); + List groupsToProcess = limitGroups(site.getGroups().stream().filter(g -> !g.getTitle().startsWith("Access:")).collect(Collectors.toList())); + + List nonExistingGroups = groupsToProcess.stream() + .filter(g -> channelsMap.values().stream().noneMatch(c -> c.getName().equalsIgnoreCase(microsoftCommonService.processMicrosoftChannelName(g.getTitle())))) + .collect(Collectors.toList()); + + if (nonExistingGroups.size() > 0 && autoConfigSessionBean.isNewChannel()) { + List channels = microsoftCommonService.createChannels(nonExistingGroups, ss.getTeamId(), credentials.getEmail()); + channels.forEach(c -> channelsMap.put(c.getId(), c)); + } + + for (Group g : groupsToProcess) { + MicrosoftChannel channel = channelsMap.values().stream() + .filter(c -> c.getName().replace(" ", "").equalsIgnoreCase(microsoftCommonService.processMicrosoftChannelName(g.getTitle()).replace(" ", ""))) + .findAny() + .orElse(null); + + String channelId = (channel != null) ? channel.getId() : null; + + if (channel == null && autoConfigSessionBean.isNewChannel()) { + channelId = microsoftCommonService.createChannel(ss.getTeamId(), g.getTitle(), credentials.getEmail()); + } + + if (StringUtils.isNotBlank(channelId)) { + GroupSynchronization gs = GroupSynchronization.builder() + .siteSynchronization(ss) + .groupId(g.getId()) + .channelId(channelId) + .build(); + + if (microsoftSynchronizationService.getGroupSynchronization(gs) == null) { + microsoftSynchronizationService.saveOrUpdateGroupSynchronization(gs); + } + } + } + String groupsIds = groupsToProcess.stream() + .map(group -> group.getId().toString()) + .collect(Collectors.joining(",")); + + String groupsNames = groupsToProcess.stream() + .map(group -> group.getTitle().toString()) + .collect(Collectors.joining(",")); + + String channelIds = channelsMap.values().stream() + .map(channel -> channel.getId().toString()) + .collect(Collectors.joining(",")); + + String channelNames = channelsMap.values().stream() + .map(channel -> channel.getName().toString()) + .collect(Collectors.joining(",")); + + microsoftLoggingService.saveLog(MicrosoftLog.builder() + .event(MicrosoftLog.EVENT_CHANNEL_PRESENT_ON_GROUP) + .status(MicrosoftLog.Status.OK) + .addData("origin", MicrosoftLogInvokers.MANUAL.getCode()) + .addData("siteId", site.getId()) + .addData("siteTitle", site.getTitle()) + .addData("processGroupsIds", groupsIds) + .addData("processGroupsNames", groupsNames) + .addData("numberGroupsOnSite", String.valueOf(site.getGroups().size())) + .addData("numberLimitedGroups", String.valueOf(groupsToProcess.size())) + .addData("numberNonProcessedGroups", String.valueOf(site.getGroups().size() - groupsToProcess.size())) + .addData("teamId", ss.getTeamId()) + .addData("createChannelsId", channelIds) + .addData("createChannelsName", channelNames) + .build()); + } + + private List limitGroups(Collection groups) { + return groups.size() > MAX_CHANNELS ? + groups.stream().limit(MAX_ADD_CHANNELS).collect(Collectors.toList()) : + new ArrayList<>(groups); + } + + @GetMapping(value = {"/autoConfig-status"}, produces = MediaType.APPLICATION_JSON_VALUE) @ResponseBody public AutoConfigSessionBean autoConfigStatus(Model model, HttpServletRequest request) throws Exception { HttpSession session = request.getSession(); - AutoConfigSessionBean autoConfigSessionBean = (AutoConfigSessionBean)session.getAttribute("AutoConfigSessionBean"); - + AutoConfigSessionBean autoConfigSessionBean = (AutoConfigSessionBean) session.getAttribute("AutoConfigSessionBean"); + return autoConfigSessionBean; } } diff --git a/microsoft-integration/admin-tool/src/main/java/org/sakaiproject/microsoft/controller/GroupSynchronizationController.java b/microsoft-integration/admin-tool/src/main/java/org/sakaiproject/microsoft/controller/GroupSynchronizationController.java index c3d13c88cfe5..6b9927cd6869 100644 --- a/microsoft-integration/admin-tool/src/main/java/org/sakaiproject/microsoft/controller/GroupSynchronizationController.java +++ b/microsoft-integration/admin-tool/src/main/java/org/sakaiproject/microsoft/controller/GroupSynchronizationController.java @@ -15,15 +15,13 @@ */ package org.sakaiproject.microsoft.controller; -import java.text.MessageFormat; -import java.util.List; -import java.util.function.Function; -import java.util.stream.Collectors; - +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.sakaiproject.microsoft.api.MicrosoftCommonService; import org.sakaiproject.microsoft.api.MicrosoftConfigurationService; import org.sakaiproject.microsoft.api.MicrosoftSynchronizationService; +import org.sakaiproject.microsoft.api.SakaiProxy; +import org.sakaiproject.microsoft.api.data.MicrosoftChannel; import org.sakaiproject.microsoft.api.exceptions.MicrosoftGenericException; import org.sakaiproject.microsoft.api.model.GroupSynchronization; import org.sakaiproject.microsoft.api.model.SiteSynchronization; @@ -38,10 +36,18 @@ import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.servlet.mvc.support.RedirectAttributes; -import lombok.extern.slf4j.Slf4j; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +import static org.sakaiproject.microsoft.api.MicrosoftCommonService.MAX_CHANNELS; /** @@ -64,104 +70,133 @@ public class GroupSynchronizationController { @Autowired MicrosoftConfigurationService microsoftConfigurationService; - + + @Autowired + SakaiProxy sakaiProxy; + private static final String REDIRECT_INDEX = "redirect:/index"; private static final String REDIRECT_EDIT_GROUP_SYNCH = "redirect:/editGroupSynchronization"; private static final String EDIT_GROUP_SYNCH_TEMPLATE = "editGroupSynchronization"; private static final String NEW = "NEW"; - + @GetMapping(value = {"/editGroupSynchronization/{siteSynchronizationId}"}) public String editGroupSynchronization(@PathVariable String siteSynchronizationId, Model model, RedirectAttributes redirectAttributes) throws MicrosoftGenericException { SiteSynchronization ss = microsoftSynchronizationService.getSiteSynchronization(SiteSynchronization.builder().id(siteSynchronizationId).build(), true); - if(ss != null) { - model.addAttribute("siteSynchronizationId", siteSynchronizationId); - model.addAttribute("groupsMap", ss.getSite().getGroups().stream().collect(Collectors.toMap(Group::getId, Function.identity()))); - model.addAttribute("channelsMap", microsoftCommonService.getTeamPrivateChannels(ss.getTeamId())); - model.addAttribute("siteTitle", ss.getSite().getTitle()); - model.addAttribute("teamTitle", microsoftCommonService.getTeam(ss.getTeamId()).getName()); - - List list = microsoftSynchronizationService.getAllGroupSynchronizationsBySiteSynchronizationId(siteSynchronizationId); - if(list != null && list.size() > 0) { - model.addAttribute("groupSynchronizations", list); - } - - - return EDIT_GROUP_SYNCH_TEMPLATE; + + if (ss == null) { + redirectAttributes.addFlashAttribute("exception_error", rb.getString("error.site_synchronization_not_found")); + return REDIRECT_INDEX; + } + + List groups = (List) sakaiProxy.getSite(ss.getSiteId()).getGroups(); + + model.addAttribute("siteSynchronizationId", siteSynchronizationId); + model.addAttribute("groupsMap", groups.stream().collect(Collectors.toMap(Group::getId, Function.identity()))); + model.addAttribute("channelsMap", microsoftCommonService.getTeamPrivateChannels(ss.getTeamId())); + model.addAttribute("siteTitle", ss.getSite().getTitle()); + model.addAttribute("teamTitle", microsoftCommonService.getTeam(ss.getTeamId()).getName()); + + List list = microsoftSynchronizationService.getAllGroupSynchronizationsBySiteSynchronizationId(siteSynchronizationId); + groups.stream() + .filter(g -> list.stream().noneMatch(item -> item.getGroupId().equals(g.getId()))) + .map(g -> GroupSynchronization.builder() + .groupId(g.getId()) + .channelId("") + .siteSynchronization(ss) + .build()) + .forEach(list::add); + + List sortedList = new ArrayList<>(); + groups.forEach( + g -> sortedList.addAll( + list.stream().filter(element -> element.getGroupId().equals(g.getId())).collect(Collectors.toList()) + ) + ); + + if (list.size() > 0) { + model.addAttribute("groupSynchronizations", sortedList); } - - redirectAttributes.addFlashAttribute("exception_error", rb.getString("error.site_synchronization_not_found")); - return REDIRECT_INDEX; + + return EDIT_GROUP_SYNCH_TEMPLATE; } - + @PostMapping(path = {"/add-groupSynchronization/{siteSynchronizationId}"}, consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE}) - public String saveGroupSynchronization(@PathVariable String siteSynchronizationId, @ModelAttribute GroupSynchronizationRequest payload, Model model, RedirectAttributes redirectAttributes) throws MicrosoftGenericException { - String createdChannelId = null; - //get parent object + public String saveGroupSynchronization(@PathVariable String siteSynchronizationId, @ModelAttribute GroupSynchronizationRequest payload, Model model, RedirectAttributes redirectAttributes) throws MicrosoftGenericException { SiteSynchronization ss = microsoftSynchronizationService.getSiteSynchronization(SiteSynchronization.builder().id(siteSynchronizationId).build()); - if(ss != null) { - if(StringUtils.isNotBlank(payload.getSelectedGroupId()) && StringUtils.isNotBlank(payload.getSelectedChannelId())) { + if (ss != null) { + Map channelsMap = microsoftCommonService.getTeamPrivateChannels(ss.getTeamId()); + model.addAttribute("channelsMap", channelsMap); + + if (StringUtils.isNotBlank(payload.getSelectedGroupId()) && StringUtils.isNotBlank(payload.getSelectedChannelId())) { String groupId = payload.getSelectedGroupId(); String channelId = payload.getSelectedChannelId(); - - //TODO: do the same to create a site??? - if(channelId.equals(NEW) && createdChannelId == null) { - //create new channel - if(StringUtils.isBlank(payload.getNewChannelName())) { - redirectAttributes.addFlashAttribute("exception_error", rb.getString("error.new_channel_empty")); - - return REDIRECT_EDIT_GROUP_SYNCH + "/" + siteSynchronizationId; - } - createdChannelId = microsoftCommonService.createChannel(ss.getTeamId(), payload.getNewChannelName(), microsoftConfigurationService.getCredentials().getEmail()); - - if(createdChannelId == null) { - redirectAttributes.addFlashAttribute("exception_error", MessageFormat.format(rb.getString("error.creating_channel"), payload.getNewChannelName())); - - return REDIRECT_EDIT_GROUP_SYNCH + "/" + siteSynchronizationId; - } - } - + GroupSynchronization gs = GroupSynchronization.builder() - .siteSynchronization(ss) - .groupId(groupId) - .channelId(channelId.equals(NEW) ? createdChannelId : channelId) - .build(); - + .siteSynchronization(ss) + .groupId(groupId) + .channelId(channelId) + .build(); + GroupSynchronization aux_gs = microsoftSynchronizationService.getGroupSynchronization(gs); - if(aux_gs != null) { + if (aux_gs != null) { redirectAttributes.addFlashAttribute("exception_error", rb.getString("error.group_synchronization_already_exists")); - + return REDIRECT_EDIT_GROUP_SYNCH + "/" + siteSynchronizationId; } - + //check if parent is forced and selected channel is duplicated //TODO: at this point, if parent is not forcing, we will allow this relationship. But we don't check if the parent starts forcing after that - if(ss.isForced() && microsoftSynchronizationService.countGroupSynchronizationsByChannelId(gs.getChannelId()) > 0) { + if (ss.isForced() && microsoftSynchronizationService.countGroupSynchronizationsByChannelId(gs.getChannelId()) > 0) { redirectAttributes.addFlashAttribute("exception_error", rb.getString("error.group_synchronization_already_forced")); - + return REDIRECT_EDIT_GROUP_SYNCH + "/" + siteSynchronizationId; } - - log.debug("saving: groupId={}, channelId={}", groupId, channelId.equals(NEW) ? createdChannelId : channelId); + + log.debug("saving: groupId={}, channelId={}", groupId, channelId); microsoftSynchronizationService.saveOrUpdateGroupSynchronization(gs); } } else { - redirectAttributes.addFlashAttribute("exception_error", rb.getString("error.site_synchronization_not_found")); + redirectAttributes.addFlashAttribute("exception_error", rb.getString("error.channel_number_more_than_30")); } - + return REDIRECT_EDIT_GROUP_SYNCH + "/" + siteSynchronizationId; } - - + + @GetMapping(path = {"/delete-groupSynchronization/{groupSynchronizationId}"}, produces = MediaType.APPLICATION_JSON_VALUE) @ResponseBody public Boolean deleteGroupSynchronization(@PathVariable String groupSynchronizationId, Model model, RedirectAttributes redirectAttributes) throws MicrosoftGenericException { boolean ok = false; GroupSynchronization gs = microsoftSynchronizationService.getGroupSynchronization(GroupSynchronization.builder().id(groupSynchronizationId).build()); - if(gs != null) { + if (gs != null) { ok = microsoftSynchronizationService.deleteGroupSynchronization(groupSynchronizationId); } - + return ok; } + + @PostMapping(value = {"/channel"}) + public String createNewChannel(@RequestParam String siteId, @RequestParam String name, RedirectAttributes redirectAttributes) throws MicrosoftGenericException { + log.debug("NEW channel creating"); + SiteSynchronization ss = microsoftSynchronizationService.getSiteSynchronization(SiteSynchronization.builder().id(siteId).build()); + try { + Map channelsMap = microsoftCommonService.getTeamPrivateChannels(ss.getTeamId()); + Collection channels = channelsMap.values(); + boolean channelExists = channels.stream() + .anyMatch(channel -> channel.getName().equalsIgnoreCase(name)); + if (!channelExists) { + if (channels.size() < MAX_CHANNELS) { + microsoftCommonService.createChannel(ss.getTeamId(), name, microsoftConfigurationService.getCredentials().getEmail()); + } else { + redirectAttributes.addFlashAttribute("exception_error", rb.getString("error.channel_number_more_than_30")); + } + } else { + redirectAttributes.addFlashAttribute("exception_error", rb.getString("error.new_channel_with_same_name")); + } + } catch (NullPointerException e) { + log.error("MicrosoftCredentialsException in confirm thread"); + } + return REDIRECT_EDIT_GROUP_SYNCH + "/" + siteId; + } } diff --git a/microsoft-integration/admin-tool/src/main/java/org/sakaiproject/microsoft/controller/MainController.java b/microsoft-integration/admin-tool/src/main/java/org/sakaiproject/microsoft/controller/MainController.java index 1208ce6f6c54..6c0cf23db3fe 100644 --- a/microsoft-integration/admin-tool/src/main/java/org/sakaiproject/microsoft/controller/MainController.java +++ b/microsoft-integration/admin-tool/src/main/java/org/sakaiproject/microsoft/controller/MainController.java @@ -15,28 +15,25 @@ */ package org.sakaiproject.microsoft.controller; -import java.time.LocalDate; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.function.Function; -import java.util.stream.Collectors; - +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; - import org.sakaiproject.microsoft.api.MicrosoftCommonService; import org.sakaiproject.microsoft.api.MicrosoftConfigurationService; import org.sakaiproject.microsoft.api.MicrosoftSynchronizationService; import org.sakaiproject.microsoft.api.SakaiProxy; +import org.sakaiproject.microsoft.api.data.MicrosoftLogInvokers; import org.sakaiproject.microsoft.api.data.MicrosoftTeam; +import org.sakaiproject.microsoft.api.data.SakaiSiteFilter; +import org.sakaiproject.microsoft.api.data.SynchronizationStatus; import org.sakaiproject.microsoft.api.exceptions.MicrosoftCredentialsException; import org.sakaiproject.microsoft.api.exceptions.MicrosoftGenericException; import org.sakaiproject.microsoft.api.model.SiteSynchronization; import org.sakaiproject.microsoft.controller.auxiliar.AjaxResponse; +import org.sakaiproject.microsoft.controller.auxiliar.FilterRequest; import org.sakaiproject.microsoft.controller.auxiliar.MainSessionBean; import org.sakaiproject.site.api.Group; +import org.sakaiproject.tool.api.Session; import org.sakaiproject.util.ResourceLoader; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.http.MediaType; @@ -49,7 +46,17 @@ import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.servlet.mvc.support.RedirectAttributes; -import lombok.extern.slf4j.Slf4j; +import java.time.LocalDate; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.Function; +import java.util.stream.Collectors; /** @@ -87,7 +94,9 @@ public class MainController { @GetMapping(value = {"/", "/index"}) public String index(Model model) { - + Session session = sakaiProxy.getCurrentSession(); + session.setAttribute("origin", MicrosoftLogInvokers.MANUAL.getCode()); + return INDEX_TEMPLATE; } @@ -99,49 +108,72 @@ public String loadItems( @RequestParam(required = false) Integer pageNum, @RequestParam(required = false) Integer pageSize, @RequestParam(required = false) String search, + FilterRequest requestBody, Model model - ) throws MicrosoftGenericException { - if(sortBy == null) { + ) throws MicrosoftGenericException { + if (sortBy == null) { sortBy = mainSessionBean.getSortBy(); } - if(sortOrder == null) { + if (sortOrder == null) { sortOrder = mainSessionBean.getSortOrder(); } - if(pageNum == null) { + if (pageNum == null) { pageNum = mainSessionBean.getPageNum(); } - if(pageSize == null) { + if (pageSize == null) { pageSize = mainSessionBean.getPageSize(); } - if(search == null) { + if (search == null) { search = mainSessionBean.getSearch(); } + if (requestBody.getFromDate() == null || requestBody.getToDate() == null) { + requestBody = new FilterRequest(); + requestBody.setSiteProperty(mainSessionBean.getSiteProperty()); + requestBody.setFromDate(mainSessionBean.getFromDate()); + requestBody.setToDate(mainSessionBean.getToDate()); + } + mainSessionBean.setSortBy(sortBy); mainSessionBean.setSortOrder(sortOrder); mainSessionBean.setPageNum(pageNum); mainSessionBean.setPageSize(pageSize); mainSessionBean.setSearch(search); - - List list = microsoftSynchronizationService.getAllSiteSynchronizations(true); - Map map = microsoftCommonService.getTeams(); - + mainSessionBean.setFromDate(requestBody.getFromDate()); + mainSessionBean.setToDate(requestBody.getToDate()); + mainSessionBean.setSiteProperty(requestBody.getSiteProperty()); + + List list; + Map map; + ZonedDateTime fromDate = null; + ZonedDateTime toDate = null; + boolean filterByDate = !requestBody.getFromDate().isEmpty() && !requestBody.getToDate().isEmpty(); + + if (filterByDate) { + fromDate = LocalDate.parse(requestBody.getFromDate(), DateTimeFormatter.ofPattern("yyyy-MM-dd")).atStartOfDay(ZoneOffset.UTC); + toDate = LocalDate.parse(requestBody.getToDate(), DateTimeFormatter.ofPattern("yyyy-MM-dd")).atStartOfDay(ZoneOffset.UTC); + } + + list = microsoftSynchronizationService.getFilteredSiteSynchronizations(true, SakaiSiteFilter.builder().siteProperty(requestBody.getSiteProperty()).build(), fromDate, toDate); + + map = microsoftCommonService.retrieveCacheTeams(); + //filter elements - if(StringUtils.isNotBlank(search)) { + if (StringUtils.isNotBlank(search)) { String lcSearch = search.toLowerCase(); list = list.stream() .filter(ss -> ss.getSiteId().contains(lcSearch) || - ss.getTeamId().contains(lcSearch) || - ss.getSite().getTitle().toLowerCase().contains(lcSearch) || - map.get(ss.getTeamId()).getName().toLowerCase().contains(lcSearch)) + ss.getTeamId().contains(lcSearch) || + ss.getSite().getTitle().toLowerCase().contains(lcSearch) || + (Objects.nonNull(map.get(ss.getTeamId())) && map.get(ss.getTeamId()).getName().toLowerCase().contains(lcSearch))) .collect(Collectors.toList()); } //sort elements - if(StringUtils.isNotBlank(sortBy)) { + if (StringUtils.isNotBlank(sortBy)) { String finalSortBy = sortBy; String finalSortOrder = sortOrder; Collections.sort(list, (i1, i2) -> { - if("DESC".equals(finalSortOrder)) { + if ("DESC".equals(finalSortOrder)) { SiteSynchronization aux = i1; i1 = i2; i2 = aux; @@ -152,7 +184,9 @@ public String loadItems( case "teamId": return i1.getTeamId().compareTo(i2.getTeamId()); case "teamTitle": - return map.get(i1.getTeamId()).getName().compareToIgnoreCase(map.get(i2.getTeamId()).getName()); + String fromString = Objects.isNull(map.get(i1.getTeamId())) ? "_null" : map.get(i1.getTeamId()).getName(); + String toString = Objects.isNull(map.get(i2.getTeamId())) ? "_null" : map.get(i2.getTeamId()).getName(); + return fromString.compareToIgnoreCase(toString); case "siteTitle": return i1.getSite().getTitle().compareToIgnoreCase(i2.getSite().getTitle()); case "syncDateFrom": @@ -184,25 +218,35 @@ public String loadItems( model.addAttribute("siteSynchronizations", list); model.addAttribute("teamsMap", map); - model.addAttribute("sortBy", sortBy); model.addAttribute("sortOrder", sortOrder); model.addAttribute("pageSize", pageSize); model.addAttribute("pageNum", pageNum); model.addAttribute("maxPage", maxPage); model.addAttribute("search", search); + model.addAttribute("requestBody", requestBody); + model.addAttribute("fromDate", requestBody.getFromDate()); + model.addAttribute("toDate", requestBody.getToDate()); + model.addAttribute("siteProperty", requestBody.getSiteProperty()); + model.addAttribute("filterCount", requestBody.getFilterCount()); + + model.addAttribute("errorMembers", microsoftCommonService.getErrorUsers()); return BODY_TEMPLATE; } - + //called by AJAX @GetMapping(value = {"/listGroupSynchronizations/{siteSynchronizationId}"}) - public String groupSynchronizations(@PathVariable String siteSynchronizationId, Model model) throws MicrosoftGenericException { + public String groupSynchronizations(@PathVariable String siteSynchronizationId, Model model) throws MicrosoftGenericException { log.debug("List group synchronizations for siteSynchronizationId={}", siteSynchronizationId); SiteSynchronization ss = microsoftSynchronizationService.getSiteSynchronization(SiteSynchronization.builder().id(siteSynchronizationId).build(), true); - if(ss != null) { + if (ss != null) { model.addAttribute("groupsMap", ss.getSite().getGroups().stream().collect(Collectors.toMap(Group::getId, Function.identity()))); model.addAttribute("channelsMap", microsoftCommonService.getTeamPrivateChannels(ss.getTeamId())); + if (ss.getStatus().equals(SynchronizationStatus.ERROR) || ss.getStatus().equals(SynchronizationStatus.PARTIAL_OK)) { + model.addAttribute("errorMembers", microsoftCommonService.getErrorUsers()); + model.addAttribute("errorGroupMembers", microsoftCommonService.getErrorGroupsUsers()); + } model.addAttribute("siteRow", ss); } @@ -234,12 +278,38 @@ public String runSiteSynchronization(@PathVariable String id, Model model) throw if(ss != null) { microsoftSynchronizationService.runSiteSynchronization(ss); + if (ss.getGroupSynchronizationsList().stream().anyMatch(group -> group.getStatus().equals(SynchronizationStatus.OK)) && ss.getStatus().equals(SynchronizationStatus.ERROR)) { + ss.setStatus(SynchronizationStatus.PARTIAL_OK); + microsoftSynchronizationService.saveOrUpdateSiteSynchronization(ss); + } model.addAttribute("row", ss); model.addAttribute("teamsMap", microsoftCommonService.getTeams()); + + if (ss.getStatus().equals(SynchronizationStatus.ERROR)) { + model.addAttribute("errorMembers", microsoftCommonService.getErrorUsers()); + model.addAttribute("errorGroupMembers", microsoftCommonService.getErrorGroupsUsers()); + } else if (ss.getStatus().equals(SynchronizationStatus.PARTIAL_OK)) { + model.addAttribute("errorMembers", microsoftCommonService.getErrorUsers()); + } } return ROW_SITE_SYNCH_FRAGMENT; } + //called by AJAX - returns FRAGMENT + @GetMapping(value = {"/refreshSite/{id}"}) + public String refreshRow(@PathVariable String id, Model model) throws Exception { + SiteSynchronization ss = microsoftSynchronizationService.getSiteSynchronization(SiteSynchronization.builder().id(id).build(), true); + + if (ss != null) { + Map teams = model.getAttribute("teamsMap") == null ? new HashMap<>() : (Map) model.getAttribute("teamsMap"); + teams.put(ss.getTeamId(), microsoftCommonService.getTeam(ss.getTeamId(), true)); + model.addAttribute("row", ss); + model.addAttribute("teamsMap", teams); + } + + return ROW_SITE_SYNCH_FRAGMENT; + } + //called by AJAX - returns JSON @GetMapping(path = {"/setForced-siteSynchronization/{id}"}, produces = MediaType.APPLICATION_JSON_VALUE) @ResponseBody diff --git a/microsoft-integration/admin-tool/src/main/java/org/sakaiproject/microsoft/controller/auxiliar/AutoConfigSessionBean.java b/microsoft-integration/admin-tool/src/main/java/org/sakaiproject/microsoft/controller/auxiliar/AutoConfigSessionBean.java index 472973904cbc..cc54f870eb13 100644 --- a/microsoft-integration/admin-tool/src/main/java/org/sakaiproject/microsoft/controller/auxiliar/AutoConfigSessionBean.java +++ b/microsoft-integration/admin-tool/src/main/java/org/sakaiproject/microsoft/controller/auxiliar/AutoConfigSessionBean.java @@ -20,6 +20,7 @@ import java.util.List; import java.util.Map; +import org.sakaiproject.microsoft.api.data.AutoConfigProcessStatus; import org.sakaiproject.site.api.Site; import com.fasterxml.jackson.annotation.JsonIgnore; @@ -32,7 +33,9 @@ public class AutoConfigSessionBean { private int total = 0; private int count = -1; private List errorList = new ArrayList<>(); - + private List statusList = new ArrayList<>(); + private AutoConfigStatus status; + private boolean newChannel = false; @JsonIgnore @@ -45,6 +48,8 @@ public void startRunning(int total) { this.total = total; this.count = 0; this.errorList = new ArrayList<>(); + this.status = AutoConfigStatus.builder().status(AutoConfigProcessStatus.START_RUNNING).build(); + this.statusList = new ArrayList<>(); } public void finishRunning() { @@ -66,4 +71,9 @@ public void addError(String siteId, String siteTitle, String errorMessage) { errorList.add(error); increaseCounter(); } + + public void addStatus(AutoConfigProcessStatus status, String siteTitle) { + this.status = AutoConfigStatus.builder().status(status).siteTitle(siteTitle).build(); + statusList.add(this.status); + } } diff --git a/microsoft-integration/admin-tool/src/main/java/org/sakaiproject/microsoft/controller/auxiliar/AutoConfigStatus.java b/microsoft-integration/admin-tool/src/main/java/org/sakaiproject/microsoft/controller/auxiliar/AutoConfigStatus.java new file mode 100644 index 000000000000..08b8437cb068 --- /dev/null +++ b/microsoft-integration/admin-tool/src/main/java/org/sakaiproject/microsoft/controller/auxiliar/AutoConfigStatus.java @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2024 The Apereo Foundation + * + * Licensed under the Educational Community License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://opensource.org/licenses/ecl2 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sakaiproject.microsoft.controller.auxiliar; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.sakaiproject.microsoft.api.data.AutoConfigProcessStatus; + +@Data +@NoArgsConstructor +@Builder +@AllArgsConstructor +public class AutoConfigStatus { + private AutoConfigProcessStatus status; + private String siteTitle; +} diff --git a/microsoft-integration/admin-tool/src/main/java/org/sakaiproject/microsoft/controller/auxiliar/FilterRequest.java b/microsoft-integration/admin-tool/src/main/java/org/sakaiproject/microsoft/controller/auxiliar/FilterRequest.java new file mode 100644 index 000000000000..6ae519c55fb1 --- /dev/null +++ b/microsoft-integration/admin-tool/src/main/java/org/sakaiproject/microsoft/controller/auxiliar/FilterRequest.java @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2024 The Apereo Foundation + * + * Licensed under the Educational Community License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://opensource.org/licenses/ecl2 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sakaiproject.microsoft.controller.auxiliar; + +import lombok.Data; +@Data +public class FilterRequest { + private String siteProperty; + private String fromDate; + private String toDate; + + public int getFilterCount() { + int filterCount = 0; + if (!siteProperty.isEmpty()) { + filterCount++; + } + if (!fromDate.isEmpty() && !toDate.isEmpty()) { + filterCount++; + } + return filterCount; + } + +} diff --git a/microsoft-integration/admin-tool/src/main/java/org/sakaiproject/microsoft/controller/auxiliar/MainSessionBean.java b/microsoft-integration/admin-tool/src/main/java/org/sakaiproject/microsoft/controller/auxiliar/MainSessionBean.java index b4485c16b192..bce4c80754f5 100644 --- a/microsoft-integration/admin-tool/src/main/java/org/sakaiproject/microsoft/controller/auxiliar/MainSessionBean.java +++ b/microsoft-integration/admin-tool/src/main/java/org/sakaiproject/microsoft/controller/auxiliar/MainSessionBean.java @@ -17,6 +17,9 @@ import lombok.Data; +import java.time.LocalDate; +import java.util.Calendar; + @Data public class MainSessionBean { private static final Integer DEFAULT_PAGE_SIZE = 50; @@ -26,4 +29,7 @@ public class MainSessionBean { private Integer pageNum = 0; private Integer pageSize = DEFAULT_PAGE_SIZE; private String search; + private String siteProperty = ""; + private String fromDate = ""; + private String toDate = ""; } diff --git a/microsoft-integration/admin-tool/src/main/resources/Messages.properties b/microsoft-integration/admin-tool/src/main/resources/Messages.properties index 362c307d390b..fb8c6721a8cf 100644 --- a/microsoft-integration/admin-tool/src/main/resources/Messages.properties +++ b/microsoft-integration/admin-tool/src/main/resources/Messages.properties @@ -33,6 +33,7 @@ status.ERROR=Error status.PARTIAL_OK=Partial OK status.PARTIAL_KO=Partial KO status.ERROR_GUEST=Error Guest +status.NOT_AVAILABLE=Not available empty_table = No active synchronizations forced=Forced save=Save @@ -43,11 +44,25 @@ delete=Delete back=Back confirm=Confirm refresh=Refresh +none=None start=Start new=NEW +new_channel= New Channel +new_group= New Group found=FOUND sync_start_date=Start date: sync_end_date=End date: +from=From +to=To +filter_by_period=Filter by period: +user_details=ID: {0}\nName: {1} {2}\nEmail: {3} +filters=Filters ({0}) +clean_filters=Clean + +creation.status_started=Process started +creation.status_create=Creating team's channels... +creation.status_bind=Binding existing team's channels... +creation.status_finished=Process finished ​ #Index index_title=Microsoft Admin Tool @@ -118,11 +133,13 @@ site_synch.filter.clear=Clear #New/Edit group synchronization messages group_synch.new_title=New Group Synchronization +group_synch.remove_title= Remove Group Synchronization group_synch.groups_from_site=Groups from: {0} group_synch.channels_from_team=Channels from: {0} group_synch.no_groups=No groups group_synch.no_channels=No private channels group_synch.add_synch=Add synchronization +group_synch.limit_reach=Limit of groups to create reached, please manage it here: #Config messages config_title=Microsoft Config @@ -199,6 +216,7 @@ error.creating_team=Error creating new Team. error.creating_team_param=Error creating new Team: {0}. error.creating_channel=Error creating new Channel: {0}. error.new_channel_empty=New channel name can not be empty +error.new_channel_with_same_name= New channel name can not have the same name of one of the channels created error.delete_group_synchronization=Error removing selected Group Synchronization error.set_forced_synchronization=Error setting forced error.deleting_site_synchronizations=Error removing one or more selected synchronizations @@ -206,3 +224,8 @@ error.cleaning_team=Error cleaning one or more selected synchronizations error.set_dates=Error setting dates error.dates=Invalid dates provided error.dates_order=Synchronization start date cannot be later than the end date +error.channel_number_more_than_30=You cannot add another channel because the team already has more than 30 channels. +error.general_failure=General failure +error.channel_number_limit_reached=Channel limitation has been reached +error.user_synchronization=The following users could not be found in Microsoft: +error.users_not_found=No users were found for this group, or Microsoft has not refreshed the data yet. Rerunning synchronization may resolve this issue. diff --git a/microsoft-integration/admin-tool/src/main/resources/Messages_ca.properties b/microsoft-integration/admin-tool/src/main/resources/Messages_ca.properties index 38925072e06e..06a06a46a8c5 100644 --- a/microsoft-integration/admin-tool/src/main/resources/Messages_ca.properties +++ b/microsoft-integration/admin-tool/src/main/resources/Messages_ca.properties @@ -1,12 +1,12 @@ # Common messages -common_danger= Necessites permisos d\u0027administrador per utilitzar aquesta eina, per favor contacte amb el seu Administrador +common_danger= Necessites permisos d\u2019administrador per utilitzar aquesta eina, per favor contacte amb el seu Administrador # Menu messages menu_main = Administrador menu_new = Nou -menu_new.title= Crear una nova sincronitzaci\u00F3 de l\u0027espai +menu_new.title= Crear una nova sincronitzaci\u00F3 de l\u2019espai menu_edit = Editar -menu_edit.title = Editar la sincronitzaci\u00F3 de l\u0027espai +menu_edit.title = Editar la sincronitzaci\u00F3 de l\u2019espai menu_config = Configuraci\u00F3 menu_config.title = Editar la configuraci\u00F3 de Microsoft menu_credentials = Credencials @@ -28,11 +28,12 @@ channel.id= Canal Id: {0} status= Estat status.OK=OK status.KO=KO -status.NONE= Res +status.NONE= Ning\u00FAn status.ERROR= Error status.PARTIAL_OK= Parcial OK status.PARTIAL_KO= Parcial KO status.ERROR_GUEST= Error de convidat +status.NOT_AVAILABLE=No disponible empty_table = Sincronitzaci\u00F3 no activada forced= For\u00E7at save= Guardar @@ -41,16 +42,30 @@ close= Cerrar search= Buscar delete= Eliminar back= Tornar +none=Ning\u00FAn confirm= Confirmar refresh= Recargar start= Comen\u00E7ar new= Nou +new_channel= Nou Canal +new_group= Nou Grup found= Trobar sync_start_date = Data inici: sync_end_date = Data fi: +from=Des de +to=Fins a +filter_by_period=Filtrar per per\u00EDode: +user_details=ID: {0}\nNom: {1} {2}\nEmail: {3} +filters=Filtres ({0}) +clean_filters=Netejar + +creation.status_started=Proc\u00E9s inicialitzat +creation.status_create=Creant canals dels teams... +creation.status_bind=Vinculant canals existents dels teams... +creation.status_finished=Proc\u00E9s finalitzat ​ #Index -index_title= Eina d\u0027Administrador de Microsoft +index_title= Eina d\u2019Administrador de Microsoft index.status_updated_at= Estat actualitzat al: {0} index.status_not_date= Estat no actualitzat index.run_updated_at= \u00DAltima execuci\u00F3: {0} @@ -77,9 +92,9 @@ index.end_date = Data fi #Auto-Config autoconfig_title= Auto-Configuraci\u00F3 autoconfig.count_sites= N\u00BA (filtered) espai disponibles -autoconfig.count_teams= N\u00BA disponible d\u0027equips -autoconfig.count_link= N\u00BA d\u0027espais i equips vinculats -autoconfig.count_new= N\u00BA d\u0027equips nous +autoconfig.count_teams= N\u00BA disponible d\u2019equips +autoconfig.count_link= N\u00BA d\u2019espais i equips vinculats +autoconfig.count_new= N\u00BA d\u2019equips nous autoconfig.confirm_title= Titol autoconfig.confirm_status= Estat autoconfig.confirm_confirm= Confirmar @@ -99,7 +114,7 @@ filter.site_property= Propietats del espai: filter.site_property_info= Valors acceptats:
  • SITE-PROPERTY-NAME
  • SITE-PROPERTY-NAME=VALUE
filter.new_team= Crear un nou Teams filter.new_channel= Crear un nou canal -filter.team_pattern= Patr\u00F3 del nom de l\u0027equip: +filter.team_pattern= Patr\u00F3 del nom de l\u2019equip: #New/Edit site synchronization messages @@ -118,27 +133,29 @@ site_synch.filter.clear= Netejar #New/Edit group synchronization messages group_synch.new_title= Nova sincronitzaci\u00F3 de grup +group_synch.remove_title= Eliminar sincronitzaci\u00F3 de grup group_synch.groups_from_site= Grups de: {0} group_synch.channels_from_team= Canals de: {0} group_synch.no_groups= No hi ha grups group_synch.no_channels=No hi ha canals privats group_synch.add_synch= Afegir sincronitzaci\u00F3 +group_synch.limit_reach=L\u00EDmit de grups a crear alcan\u00E7at, per favor gestiona-ho ac\u00ED: #Config messages config_title= Configuraci\u00F3 de Microsoft config.synchronization_config=Configuraci\u00F3 de sincronitzaci\u00F3 config.SYNCH\:CREATE_TEAM=Crear un nou Teams quan es crea un espai a Sakai -config.SYNCH\:DELETE_SYNCH = Elimina la sincronitzaci\u00F3 Espai-Teams quan s\u0027elimina l\u0027espai de Sakai -config.SYNCH\:DELETE_TEAM=Elimina el Teams quan s\u0027elimina l\u0027espai de Sakai (requereix una sincronitzaci\u00F3 espai-Teams v\u00E0lida) -config.SYNCH\:ADD_USER_TO_TEAM=Quan l\u0027usuari s\u0027agrega a un espai, afegiu-lo als Teams relacionats (requereix una sincronitzaci\u00F3 espai-Teams v\u00E0lida) -config.SYNCH\:REMOVE_USER_FROM_TEAM=Quan l\u0027usuari s\u0027elimina de l\u0027espai, suprimiu-lo dels Teams relacionats(requereix una sincronitzaci\u00F3 espai-Teams v\u00E0lida i for\u00E7ada) +config.SYNCH\:DELETE_SYNCH = Elimina la sincronitzaci\u00F3 Espai-Teams quan s\u2019elimina l\u2019espai de Sakai +config.SYNCH\:DELETE_TEAM=Elimina el Teams quan s\u2019elimina l\u2019espai de Sakai (requereix una sincronitzaci\u00F3 espai-Teams v\u00E0lida) +config.SYNCH\:ADD_USER_TO_TEAM=Quan l\u2019usuari s\u2019agrega a un espai, afegiu-lo als Teams relacionats (requereix una sincronitzaci\u00F3 espai-Teams v\u00E0lida) +config.SYNCH\:REMOVE_USER_FROM_TEAM=Quan l\u2019usuari s\u2019elimina de l\u2019espai, suprimiu-lo dels Teams relacionats(requereix una sincronitzaci\u00F3 espai-Teams v\u00E0lida i for\u00E7ada) config.SYNCH\:CREATE_CHANNEL=Crear un nou canal quan es crea un grup a Sakai (requereix una sincronitzaci\u00F3 espai-Teams v\u00E0lida) -config.SYNCH\:DELETE_CHANNEL=Eliminar canal quan s\u0027elimine un grup a Sakai (requereix una sincronitzaci\u00F3 grup-canal v\u00E0lida) -config.SYNCH\:ADD_USER_TO_CHANNEL=Quan s\u0027agrega un usuari a un grup, s\u0027agrega als canals relacionats (requereix una sincronitzaci\u00F3 grup-canal v\u00E0lida) -config.SYNCH\:REMOVE_USER_FROM_CHANNEL=Quan s\u0027elimine un usuari d\u0027un grup, eliminar-lo dels canals relacionats (requereix una sincronitzaci\u00F3 grup-canal v\u00E0lida i for\u00E7ada) -config.SYNCH\:REMOVE_USERS_WHEN_UNPUBLISH = Quan es despublica un espai, s\u0027eliminaran tots els usuaris dels Team(s) i canal(s) relacionats (requereix una sincronitzaci\u00F3 Espai-Teams v\u00E0lida) -config.SYNCH\:CREATE_INVITATION=Si l\u0027usuari de Microsoft no existeis, crea una nova invitaci\u00F3 +config.SYNCH\:DELETE_CHANNEL=Eliminar canal quan s\u2019elimine un grup a Sakai (requereix una sincronitzaci\u00F3 grup-canal v\u00E0lida) +config.SYNCH\:ADD_USER_TO_CHANNEL=Quan s\u2019agrega un usuari a un grup, s\u2019agrega als canals relacionats (requereix una sincronitzaci\u00F3 grup-canal v\u00E0lida) +config.SYNCH\:REMOVE_USER_FROM_CHANNEL=Quan s\u2019elimine un usuari d\u2019un grup, eliminar-lo dels canals relacionats (requereix una sincronitzaci\u00F3 grup-canal v\u00E0lida i for\u00E7ada) +config.SYNCH\:REMOVE_USERS_WHEN_UNPUBLISH = Quan es despublica un espai, s\u2019eliminaran tots els usuaris dels Team(s) i canal(s) relacionats (requereix una sincronitzaci\u00F3 Espai-Teams v\u00E0lida) +config.SYNCH\:CREATE_INVITATION=Si l\u2019usuari de Microsoft no existeis, crea una nova invitaci\u00F3 config.sync_duration=Duraci\u00F3 sincro (mesos): @@ -150,26 +167,26 @@ config.onedrive_enabled=OneDrive habilitat config.collaborative_documents_config=Configuraci\u00F3 dels Documents Col\u00B7laboratius config.max_upload_size=Grand\u00E0ria m\u00E0xima de pujada (MB) -config.id_mapping= Mapeig d\u0027Id -config.sakai_user_id=Id d\u0027usuari de Sakai -config.microsoft_user_id=Id d\u0027usuari de Microsoft -config.map_user_id=Id d\u0027usuari -config.map_user_property=Propietat de l\u0027usuari +config.id_mapping= Mapeig d\u2019Id +config.sakai_user_id=Id d\u2019usuari de Sakai +config.microsoft_user_id=Id d\u2019usuari de Microsoft +config.map_user_id=Id d\u2019usuari +config.map_user_property=Propietat de l\u2019usuari config.map_user_eid=Usuari Eid config.map_user_email=Correu electr\u00F2nic #Credentials messages credentials_title=Credencials de Microsoft credentials.test=Provar credencials -credentials.ok=Credentials d\u0027OK +credentials.ok=Credentials d\u2019OK credentials.authority=Autoritat credentials.clientId=Id de client credentials.secret=Secret credentials.scope=Scope credentials.delegated_scope = Scope Delegat credentials.scope_alt=Per defecte: {0} -credentials.email=Correu electr\u00F2nic d\u0027administrador de Microsoft -credentials.email_info=S\u0027utilitza en accions que requereixen un usuari. Per exemple: creeu un Teams nou (aquest usuari s\u0027inclour\u00E0 com a propietari inicial). +credentials.email=Correu electr\u00F2nic d\u2019administrador de Microsoft +credentials.email_info=S\u2019utilitza en accions que requereixen un usuari. Per exemple: creeu un Teams nou (aquest usuari s\u2019inclour\u00E0 com a propietari inicial). #Errors - frontend validation error.microsoft_teams=Selecciona almenys un equip de Microsoft @@ -178,31 +195,37 @@ error.groups=Selecciona almenys un grup error.channel=Selecciona almenys un canal #Errors - from exceptions -error.no_credentials_provided=No s\u0027han proporcionat credencials -error.invalid_credentials_provided=S\u0027ha proporcionat credencials inv\u00E0lides +error.no_credentials_provided=No s\u2019han proporcionat credencials +error.invalid_credentials_provided=S\u2019ha proporcionat credencials inv\u00E0lides error.invalid_token_provided=Token no v\u00E0lid -error.invalid_invitation=S\u0027ha enviat una invitaci\u00F3 inv\u00E0lida +error.invalid_invitation=S\u2019ha enviat una invitaci\u00F3 inv\u00E0lida error.invalid_team=Equip inv\u00E0lid -error.invalid_email=S\u0027ha proporcionat un correu electr\u00F2nic de Microsoft inv\u00E0lid +error.invalid_email=S\u2019ha proporcionat un correu electr\u00F2nic de Microsoft inv\u00E0lid error.no_admin=No sou administrador #Errors - backend -error.no_site_selected=No s\u0027ha seleccionat un espai -error.no_team_selected=No s\u0027ha seleccionat un equip -error.site_synchronization_not_found=No s\u0027ha trobat la sincronitzaci\u00F3 de l\u0027espai -error.site_synchronization_already_exists=Ja existeix una sincronitzaci\u00F3 amb el espai i l\u0027equip seleccionats. -error.site_synchronization_already_forced=Una sincronitzaci\u00F3 est\u00E0 for\u00E7ant l\u0027equip seleccionat. -error.site_synchronization_impossible_forced=Ja hi ha una sincronitzaci\u00F3 amb l\u0027equip seleccionat. Impossible for\u00E7ar l\u0027espai i l\u0027equip seleccionats. +error.no_site_selected=No s\u2019ha seleccionat un espai +error.no_team_selected=No s\u2019ha seleccionat un equip +error.site_synchronization_not_found=No s\u2019ha trobat la sincronitzaci\u00F3 de l\u2019espai +error.site_synchronization_already_exists=Ja existeix una sincronitzaci\u00F3 amb el espai i l\u2019equip seleccionats. +error.site_synchronization_already_forced=Una sincronitzaci\u00F3 est\u00E0 for\u00E7ant l\u2019equip seleccionat. +error.site_synchronization_impossible_forced=Ja hi ha una sincronitzaci\u00F3 amb l\u2019equip seleccionat. Impossible for\u00E7ar l\u2019espai i l\u2019equip seleccionats. error.group_synchronization_already_exists=La sincronitzaci\u00F3 del grup ja existeix amb el grup i el canal seleccionats. error.group_synchronization_already_forced= La sincronitzaci\u00F3 dels pares \u00E9s for\u00E7ada. No es pot enlla\u00E7ar el canal seleccionat. -error.creating_team= S\u0027ha produit un error creant l\u0027equip nou. -error.creating_team_param=S\u0027ha produit un error creant l\u0027equip nou: {0}. -error.creating_channel=s\u0027ha produit un error creant el nou canal: {0}. +error.creating_team= S\u2019ha produit un error creant l\u2019equip nou. +error.creating_team_param=S\u2019ha produit un error creant l\u2019equip nou: {0}. +error.creating_channel=s\u2019ha produit un error creant el nou canal: {0}. error.new_channel_empty=El nom del canal nou no pot estar buit -error.delete_group_synchronization=S\u0027ha produ\u00EFt un error en eliminar la sincronitzaci\u00F3 del grup seleccionat -error.set_forced_synchronization=Configuraci\u00F3 d\u0027error for\u00E7ada -error.deleting_site_synchronizations=S\u0027ha produ\u00EFt un error en eliminar una o m\u00E9s sincronitzacions seleccionades -error.cleaning_team=S\u0027ha produ\u00EFt un error en netejar una o m\u00E9s sincronitzacions seleccionades +error.delete_group_synchronization=S\u2019ha produ\u00EFt un error en eliminar la sincronitzaci\u00F3 del grup seleccionat +error.set_forced_synchronization=Configuraci\u00F3 d\u2019error for\u00E7ada +error.deleting_site_synchronizations=S\u2019ha produ\u00EFt un error en eliminar una o m\u00E9s sincronitzacions seleccionades +error.cleaning_team=S\u2019ha produ\u00EFt un error en netejar una o m\u00E9s sincronitzacions seleccionades error.set_dates = Error en establir les dates error.dates = Dates inv\u00E0lides -error.dates_order = La data d\u0027inici no pot ser posterior a la data de finalitzaci\u00F3 +error.dates_order = La data d\u2019inici no pot ser posterior a la data de finalitzaci\u00F3 +error.new_channel_with_same_name= = El nom del nou canal no pot tindr\u00E9 el mateix nom que un dels canals ja creats. +error.channel_number_more_than_30=No pots afegir altre canal perqu\u00E8 el equip ja t\u00E9 m\u00E9s de 30 canals. +error.general_failure=Error general +error.user_synchronization=No s\u2019han pogut trobar els seg\u00FCents usuaris a Microsoft: +error.channel_number_limit_reached=S\u2019ha aplegat al l\u00EDmit de canals +error.users_not_found=No s\u2019han trobat usuaris per a aquest grup o Microsoft no ha actualitzat les dades encara. Si torna a executar la sincronitzaci\u00F3, \u00E9s possible que es resolgui el problema. diff --git a/microsoft-integration/admin-tool/src/main/resources/Messages_es.properties b/microsoft-integration/admin-tool/src/main/resources/Messages_es.properties index 012835f437b8..b014e1fd0d18 100644 --- a/microsoft-integration/admin-tool/src/main/resources/Messages_es.properties +++ b/microsoft-integration/admin-tool/src/main/resources/Messages_es.properties @@ -33,6 +33,7 @@ status.ERROR = Error status.PARTIAL_OK = Parcialmente OK status.PARTIAL_KO = Parcialmente KO status.ERROR_GUEST = Error de invitado +status.NOT_AVAILABLE=No disponible empty_table = No existen sincronizaciones activas forced = Forzado save = Guardar @@ -41,13 +42,27 @@ close = Cerrar search = Buscar delete = Borrar back = Atr\u00E1s +none=Ninguno confirm = Confirmar refresh = Actualizar start = Iniciar new = Nuevo +new_channel= Nuevo Canal +new_group= Nuevo Grupo found = Encontrado sync_start_date = Fecha inicio: sync_end_date = Fecha fin: +from=Desde +to=Hasta +filter_by_period=Filtrar por periodo: +user_details=ID: {0}\nNombre: {1} {2}\nEmail: {3} +filters=Filtros ({0}) +clean_filters=Limpiar + +creation.status_started=Proceso iniciado +creation.status_create=Creando canales de los teams... +creation.status_bind=Vinculando canales existentes de los teams... +creation.status_finished=Proceso finalizado ​ #Índice index_title = Herramienta de administraci\u00F3n de Microsoft @@ -118,11 +133,14 @@ site_synch.filter.clear = Borrar #Nuevo/Editar mensajes de sincronización de grupo group_synch.new_title = Sincronizaci\u00F3n de nuevo Grupo +group_synch.remove_title= Eliminar sincronizaci\u00F3n de Grupo group_synch.groups_from_site = Grupos de: {0} group_synch.channels_from_team = Canales de: {0} group_synch.no_groups = No hay grupos group_synch.no_channels = No hay canales privados group_synch.add_synch = A\u00F1adir sincronizaci\u00F3n +group_synch.limit_reach=L\u00EDmite de grupos a crear alcanzado, por favor gestionalo aqu\u00ED: + #Mensajes configurados config_title = Configuraci\u00F3n de Microsoft @@ -199,6 +217,7 @@ error.creating_team = Error al crear un nuevo Teams. error.creating_team_param = Error al crear un nuevo Teams: {0}. error.creating_channel = Error creando un canal nuevo: {0}. error.new_channel_empty = El nombre del nuevo canal no puede estar vac\u00EDo +error.new_channel_with_same_name= = El nombre del nuevo canal no puede tener el mismo nombre que uno de los canales ya creados. error.delete_group_synchronization = Error eliminando la sincronizaci\u00F3n de grupo seleccionada error.set_forced_synchronization = Error de configuraci\u00F3n forzado error.deleting_site_synchronizations = Error al eliminar una o varias sincronizaciones seleccionadas @@ -206,3 +225,8 @@ error.cleaning_team = Error al limpiar una o m\u00E1s sincronizaciones seleccion error.set_dates = Error al establecer las fechas error.dates = Fechas inv\u00E1lidas error.dates_order = La fecha de inicio no puede ser posterior a la fecha de finalizaci\u00F3n +error.channel_number_more_than_30=No puedes agregar otro canal porque el equipo ya tiene ms de 30 canales. +error.general_failure=Error general +error.channel_number_limit_reached=L\u00EDmite de grupos alcanzado +error.user_synchronization=No se han podido encontrar los siguientes usuarios en Microsoft: +error.users_not_found=No se han encontrado usuarios para este grupo o Microsoft a\u00fan no ha actualizado los datos. Si vuelve a ejecutar la sincronizaci\u00F3n, es posible que se resuelva el problema. diff --git a/microsoft-integration/admin-tool/src/main/resources/Messages_eu.properties b/microsoft-integration/admin-tool/src/main/resources/Messages_eu.properties index 062237eb50d4..e2fb9ff3332e 100644 --- a/microsoft-integration/admin-tool/src/main/resources/Messages_eu.properties +++ b/microsoft-integration/admin-tool/src/main/resources/Messages_eu.properties @@ -33,6 +33,7 @@ status.ERROR=Error status.PARTIAL_OK=Partial OK status.PARTIAL_KO=Partial KO status.ERROR_GUEST=Error Guest +status.NOT_AVAILABLE=Not available empty_table = No active synchronizations forced=Forced save=Save @@ -43,11 +44,25 @@ delete=Delete back=Back confirm=Confirm refresh=Refresh +none=None start=Start new=NEW +new_channel= New Channel +new_group= New Group found=FOUND sync_start_date=Start date: sync_end_date=End date: +from=From +to=To +filter_by_period=Filter by period: +user_details=ID: {0}\nName: {1} {2}\nEmail: {3} +filters=Filters ({0}) +clean_filters=Clean + +creation.status_started=Process started +creation.status_create=Creating team's channels... +creation.status_bind=Binding existing team's channels... +creation.status_finished=Process finished ​ #Index index_title=Microsoft Admin Tool @@ -118,11 +133,14 @@ site_synch.filter.clear=Clear #New/Edit group synchronization messages group_synch.new_title=New Group Synchronization +group_synch.remove_title= Remove Group Synchronization group_synch.groups_from_site=Groups from: {0} group_synch.channels_from_team=Channels from: {0} group_synch.no_groups=No groups group_synch.no_channels=No private channels group_synch.add_synch=Add synchronization +group_synch.limit_reach=Limit of groups to create reached, please manage it here: + #Config messages config_title=Microsoft Config @@ -199,7 +217,13 @@ error.creating_team=Error creating new Team. error.creating_team_param=Error creating new Team: {0}. error.creating_channel=Error creating new Channel: {0}. error.new_channel_empty=New channel name can not be empty +error.new_channel_with_same_name= New channel name can not have the same name of one of the channels created error.delete_group_synchronization=Error removing selected Group Synchronization error.set_forced_synchronization=Error setting forced error.deleting_site_synchronizations=Error removing one or more selected synchronizations error.cleaning_team=Error cleaning one or more selected synchronizations +error.channel_number_more_than_30=Ezin duzu beste kanalik gehitu gailuak dagoeneko 30 kanal baino gehiago dituelako. +error.general_failure=General failure +error.channel_number_limit_reached=Channel limitation has been reached +error.user_synchronization=The following users could not be found in Microsoft: +error.users_not_found=No users were found for this group, or Microsoft has not refreshed the data yet. Rerunning synchronization may resolve this issue. diff --git a/microsoft-integration/admin-tool/src/main/webapp/WEB-INF/templates/body.html b/microsoft-integration/admin-tool/src/main/webapp/WEB-INF/templates/body.html index 7147d0747050..6a3386c35fd6 100644 --- a/microsoft-integration/admin-tool/src/main/webapp/WEB-INF/templates/body.html +++ b/microsoft-integration/admin-tool/src/main/webapp/WEB-INF/templates/body.html @@ -1,11 +1,14 @@
-
+
+ + +
@@ -20,7 +23,7 @@
- -
@@ -52,7 +55,7 @@ - +
@@ -114,4 +117,64 @@
-
[[#{empty_table}]]
\ No newline at end of file +
[[#{empty_table}]]
+ + +
+ +
+
diff --git a/microsoft-integration/admin-tool/src/main/webapp/WEB-INF/templates/editGroupSynchronization.html b/microsoft-integration/admin-tool/src/main/webapp/WEB-INF/templates/editGroupSynchronization.html index b192ddd2a07b..e6c9afa3c128 100644 --- a/microsoft-integration/admin-tool/src/main/webapp/WEB-INF/templates/editGroupSynchronization.html +++ b/microsoft-integration/admin-tool/src/main/webapp/WEB-INF/templates/editGroupSynchronization.html @@ -8,14 +8,27 @@
- +
- +
- +
+ +
- +
+ +
@@ -129,6 +133,15 @@