Skip to content

Commit

Permalink
feat: Allow to retrieve I18N keys for Sites Name and Description in U…
Browse files Browse the repository at this point in the history
…I - MEED-3342 - Meeds-io/meeds#1629 (#217)

After the enhanced behavior made on Meeds-io/gatein-portal#969 and
Meeds-io/social#4028, this change will allow to provide sites translated
names and descriptions coming from crowdin synchronized properties
files.
  • Loading branch information
boubaker committed Sep 16, 2024
1 parent 14355fd commit b99dd7f
Show file tree
Hide file tree
Showing 10 changed files with 468 additions and 159 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import org.exoplatform.portal.mop.SiteKey;
import org.exoplatform.social.rest.entity.SiteEntity;

import io.meeds.layout.model.NodeLabel;
import io.meeds.layout.model.PermissionUpdateModel;
import io.meeds.layout.model.SiteCreateModel;
import io.meeds.layout.model.SiteUpdateModel;
Expand Down Expand Up @@ -237,4 +238,46 @@ public ResponseEntity<SiteEntity> createSite(
}
}

@GetMapping("{siteId}/labels")
@Operation(summary = "Retrieve site I18N labels", method = "GET", description = "This retrieves site labels")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Request fulfilled"),
@ApiResponse(responseCode = "403", description = "Forbidden"),
@ApiResponse(responseCode = "404", description = "Not found"),
})
public NodeLabel getSiteLabels(
HttpServletRequest request,
@Parameter(description = "Site id", required = true)
@PathVariable("siteId")
Long siteId) {
try {
return siteLayoutService.getSiteLabels(siteId, request.getRemoteUser());
} catch (ObjectNotFoundException e) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, e.getMessage());
} catch (IllegalAccessException e) {
throw new ResponseStatusException(HttpStatus.FORBIDDEN, e.getMessage());
}
}

@GetMapping("{siteId}/descriptions")
@Operation(summary = "Retrieve site I18N descriptions", method = "GET", description = "This retrieves site descriptions")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Request fulfilled"),
@ApiResponse(responseCode = "403", description = "Forbidden"),
@ApiResponse(responseCode = "404", description = "Not found"),
})
public NodeLabel getSiteDescriptions(
HttpServletRequest request,
@Parameter(description = "Site id", required = true)
@PathVariable("siteId")
Long siteId) {
try {
return siteLayoutService.getSiteDescriptions(siteId, request.getRemoteUser());
} catch (ObjectNotFoundException e) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, e.getMessage());
} catch (IllegalAccessException e) {
throw new ResponseStatusException(HttpStatus.FORBIDDEN, e.getMessage());
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,31 @@
*/
package io.meeds.layout.service;

import java.util.Collections;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.stream.Collectors;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import org.exoplatform.commons.ObjectAlreadyExistsException;
import org.exoplatform.commons.exception.ObjectNotFoundException;
import org.exoplatform.portal.config.UserPortalConfig;
import org.exoplatform.portal.config.UserPortalConfigService;
import org.exoplatform.portal.config.model.PortalConfig;
import org.exoplatform.portal.mop.SiteKey;
import org.exoplatform.portal.mop.SiteType;
import org.exoplatform.portal.mop.service.LayoutService;
import org.exoplatform.portal.mop.user.UserPortal;
import org.exoplatform.services.resources.LocaleConfig;
import org.exoplatform.services.resources.LocaleConfigService;
import org.exoplatform.services.resources.LocaleContextInfo;

import io.meeds.layout.model.NodeLabel;
import io.meeds.layout.model.PermissionUpdateModel;
import io.meeds.layout.model.SiteCreateModel;
import io.meeds.layout.model.SiteUpdateModel;
Expand All @@ -41,12 +55,19 @@ public class SiteLayoutService {
@Autowired
private LayoutService layoutService;

@Autowired
private LocaleConfigService localeConfigService;

@Autowired
private UserPortalConfigService portalConfigService;

@Autowired
private LayoutAclService aclService;

private Map<String, String> supportedLanguages;

private Locale defaultConfiguredLocale;

public PortalConfig getSite(long siteId, String username) throws ObjectNotFoundException, IllegalAccessException {
PortalConfig portalConfig = layoutService.getPortalConfig(siteId);
if (portalConfig == null) {
Expand Down Expand Up @@ -153,6 +174,76 @@ public void updateSitePermissions(PermissionUpdateModel permissionUpdateModel,
layoutService.save(portalConfig);
}

public NodeLabel getSiteLabels(Long siteId, String username) throws ObjectNotFoundException, IllegalAccessException {
return getSiteLabel(siteId, username, true);
}

public NodeLabel getSiteDescriptions(Long siteId, String username) throws ObjectNotFoundException, IllegalAccessException {
return getSiteLabel(siteId, username, false);
}

private NodeLabel getSiteLabel(long siteId, String username, boolean isLabel) throws ObjectNotFoundException,
IllegalAccessException {
PortalConfig portalConfig = layoutService.getPortalConfig(siteId);
if (portalConfig == null) {
throw new ObjectNotFoundException(String.format("Site with id %s doesn't exists", siteId));
} else if (!aclService.canViewSite(new SiteKey(portalConfig.getType(), portalConfig.getName()), username)) {
throw new IllegalAccessException();
}
Locale defaultLocale = getDefaultLocale();

NodeLabel nodeLabel = new NodeLabel();
nodeLabel.setDefaultLanguage(defaultLocale.getLanguage());
nodeLabel.setSupportedLanguages(getSupportedLanguages(defaultLocale));
Map<String, String> labels = getLabels(portalConfig, defaultLocale, username, isLabel);
nodeLabel.setLabels(labels);
return nodeLabel;
}

private Map<String, String> getLabels(PortalConfig portalConfig, Locale defaultLocale, String username, boolean isLabel) {
SiteKey siteKey = new SiteKey(SiteType.valueOf(portalConfig.getType().toUpperCase()), portalConfig.getName());
UserPortalConfig userPortalConfig = portalConfigService.getUserPortalConfig(siteKey.getName(), username);
UserPortal userPortal = userPortalConfig.getUserPortal();

Map<String, String> labels = new HashMap<>();
localeConfigService.getLocalConfigs()
.stream()
.map(LocaleConfig::getLocale)
.forEach(locale -> {
String translatedLabel = isLabel ? userPortal.getPortalLabel(siteKey, locale) :
userPortal.getPortalDescription(siteKey, locale);
labels.put(LocaleContextInfo.getLocaleAsString(locale), translatedLabel);
});
if (!labels.containsKey(defaultLocale.getLanguage()) && !labels.isEmpty()) {
labels.put(defaultLocale.getLanguage(), labels.values().iterator().next());
}
return labels;
}

private Map<String, String> getSupportedLanguages(Locale defaultLocale) {
if (supportedLanguages == null || !defaultConfiguredLocale.equals(defaultLocale)) {
supportedLanguages = CollectionUtils.isEmpty(localeConfigService.getLocalConfigs()) ?
Collections.singletonMap(defaultLocale.toLanguageTag(),
defaultLocale.getDisplayName()) :
localeConfigService.getLocalConfigs()
.stream()
.filter(localeConfig -> !StringUtils.equals(localeConfig.getLocale()
.toLanguageTag(),
"ma"))
.map(LocaleConfig::getLocale)
.collect(Collectors.toMap(Locale::toLanguageTag,
Locale::getDisplayName));
defaultConfiguredLocale = defaultLocale;
}
return supportedLanguages;
}

private Locale getDefaultLocale() {
return localeConfigService.getDefaultLocaleConfig() == null ? Locale.ENGLISH :
localeConfigService.getDefaultLocaleConfig()
.getLocale();
}

private String getAdministratorsPermission() {
return "*:" + aclService.getAdministratorsGroup();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*/
package io.meeds.layout.rest;

import static io.meeds.layout.util.JsonUtils.toJsonString;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
Expand Down Expand Up @@ -51,12 +52,6 @@
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

import com.fasterxml.jackson.core.json.JsonReadFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;

import org.exoplatform.commons.exception.ObjectNotFoundException;

import io.meeds.layout.model.PageTemplate;
Expand All @@ -65,7 +60,6 @@
import io.meeds.spring.web.security.WebSecurityConfiguration;

import jakarta.servlet.Filter;
import lombok.SneakyThrows;

@SpringBootTest(classes = { PageTemplateRest.class, PortalAuthenticationManager.class, })
@ContextConfiguration(classes = { WebSecurityConfiguration.class })
Expand All @@ -80,18 +74,6 @@ public class PageTemplateRestTest {

private static final String TEST_PASSWORD = "testPassword";

static final ObjectMapper OBJECT_MAPPER;

static {
// Workaround when Jackson is defined in shared library with different
// version and without artifact jackson-datatype-jsr310
OBJECT_MAPPER = JsonMapper.builder()
.configure(JsonReadFeature.ALLOW_MISSING_VALUES, true)
.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
.build();
OBJECT_MAPPER.registerModule(new JavaTimeModule());
}

@MockBean
private PageTemplateService pageTemplateService;

Expand Down Expand Up @@ -140,7 +122,7 @@ void getPageTemplateWithUser() throws Exception {

@Test
void createPageTemplateAnonymously() throws Exception {
ResultActions response = mockMvc.perform(post(REST_PATH).content(asJsonString(new PageTemplate()))
ResultActions response = mockMvc.perform(post(REST_PATH).content(toJsonString(new PageTemplate()))
.contentType(MediaType.APPLICATION_JSON));
response.andExpect(status().isForbidden());
verifyNoInteractions(pageTemplateService);
Expand All @@ -150,7 +132,7 @@ void createPageTemplateAnonymously() throws Exception {
void createPageTemplateWithUser() throws Exception {
PageTemplate pageTemplate = new PageTemplate();
ResultActions response = mockMvc.perform(post(REST_PATH).with(testSimpleUser())
.content(asJsonString(pageTemplate))
.content(toJsonString(pageTemplate))
.contentType(MediaType.APPLICATION_JSON));
response.andExpect(status().isOk());
verify(pageTemplateService).createPageTemplate(pageTemplate, SIMPLE_USER);
Expand All @@ -162,14 +144,14 @@ void createPageTemplateWithUserForbidden() throws Exception {
when(pageTemplateService.createPageTemplate(pageTemplate, SIMPLE_USER)).thenThrow(IllegalAccessException.class);

ResultActions response = mockMvc.perform(post(REST_PATH).with(testSimpleUser())
.content(asJsonString(pageTemplate))
.content(toJsonString(pageTemplate))
.contentType(MediaType.APPLICATION_JSON));
response.andExpect(status().isForbidden());
}

@Test
void updatePageTemplateAnonymously() throws Exception {
ResultActions response = mockMvc.perform(put(REST_PATH + "/1").content(asJsonString(new PageTemplate()))
ResultActions response = mockMvc.perform(put(REST_PATH + "/1").content(toJsonString(new PageTemplate()))
.contentType(MediaType.APPLICATION_JSON));
response.andExpect(status().isForbidden());
verifyNoInteractions(pageTemplateService);
Expand All @@ -179,7 +161,7 @@ void updatePageTemplateAnonymously() throws Exception {
void updatePageTemplateWithUser() throws Exception {
PageTemplate pageTemplate = new PageTemplate();
ResultActions response = mockMvc.perform(put(REST_PATH + "/1").with(testSimpleUser())
.content(asJsonString(pageTemplate))
.content(toJsonString(pageTemplate))
.contentType(MediaType.APPLICATION_JSON));
response.andExpect(status().isOk());

Expand All @@ -194,7 +176,7 @@ void updatePageTemplateWithUserForbidden() throws Exception {
when(pageTemplateService.updatePageTemplate(pageTemplate, SIMPLE_USER)).thenThrow(IllegalAccessException.class);

ResultActions response = mockMvc.perform(put(REST_PATH + "/1").with(testSimpleUser())
.content(asJsonString(pageTemplate))
.content(toJsonString(pageTemplate))
.contentType(MediaType.APPLICATION_JSON));
response.andExpect(status().isForbidden());
}
Expand All @@ -206,7 +188,7 @@ void updatePageTemplateWithUserNotFound() throws Exception {
when(pageTemplateService.updatePageTemplate(pageTemplate, SIMPLE_USER)).thenThrow(ObjectNotFoundException.class);

ResultActions response = mockMvc.perform(put(REST_PATH + "/1").with(testSimpleUser())
.content(asJsonString(pageTemplate))
.content(toJsonString(pageTemplate))
.contentType(MediaType.APPLICATION_JSON));
response.andExpect(status().isNotFound());
}
Expand Down Expand Up @@ -253,9 +235,4 @@ private RequestPostProcessor testSimpleUser() {
.authorities(new SimpleGrantedAuthority("users"));
}

@SneakyThrows
public static String asJsonString(final Object obj) {
return OBJECT_MAPPER.writeValueAsString(obj);
}

}
Loading

0 comments on commit b99dd7f

Please sign in to comment.