KEYCLOAK-14855 Added realm-specific localization texts which affect texts in every part of the UI (admin console / login page / personal info page / email templates). Also new API endpoints and a new UI screen to manage the realm-specific localization texts were introduced.
Co-authored-by: Daniel Fesenmeyer <daniel.fesenmeyer@bosch.io>
This commit is contained in:
parent
785f2e78bc
commit
e131de9574
41 changed files with 1428 additions and 46 deletions
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||||
|
* and other contributors as indicated by the @author tags.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache 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://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* 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.keycloak.admin.client.resource;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.ws.rs.Consumes;
|
||||||
|
import javax.ws.rs.DELETE;
|
||||||
|
import javax.ws.rs.GET;
|
||||||
|
import javax.ws.rs.PUT;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.PathParam;
|
||||||
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
|
|
||||||
|
public interface RealmLocalizationResource {
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
List<String> getRealmSpecificLocales();
|
||||||
|
|
||||||
|
@Path("{locale}")
|
||||||
|
@GET
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
Map<String, String> getRealmLocalizationTexts(final @PathParam("locale") String locale);
|
||||||
|
|
||||||
|
|
||||||
|
@Path("{locale}/{key}")
|
||||||
|
@GET
|
||||||
|
@Produces(MediaType.TEXT_PLAIN)
|
||||||
|
String getRealmLocalizationText(final @PathParam("locale") String locale, final @PathParam("key") String key);
|
||||||
|
|
||||||
|
|
||||||
|
@Path("{locale}")
|
||||||
|
@DELETE
|
||||||
|
void deleteRealmLocalizationTexts(@PathParam("locale") String locale);
|
||||||
|
|
||||||
|
@Path("{locale}/{key}")
|
||||||
|
@DELETE
|
||||||
|
void deleteRealmLocalizationText(@PathParam("locale") String locale, @PathParam("key") String key);
|
||||||
|
|
||||||
|
@Path("{locale}/{key}")
|
||||||
|
@PUT
|
||||||
|
@Consumes(MediaType.TEXT_PLAIN)
|
||||||
|
void saveRealmLocalizationText(@PathParam("locale") String locale, @PathParam("key") String key, String text);
|
||||||
|
}
|
|
@ -279,4 +279,7 @@ public interface RealmResource {
|
||||||
@Path("keys")
|
@Path("keys")
|
||||||
KeyResource keys();
|
KeyResource keys();
|
||||||
|
|
||||||
|
@Path("localization")
|
||||||
|
RealmLocalizationResource localization();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1645,6 +1645,35 @@ public class RealmAdapter implements CachedRealmModel {
|
||||||
return cached.getAttributes();
|
return cached.getAttributes();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void patchRealmLocalizationTexts(String locale, Map<String, String> localizationTexts) {
|
||||||
|
getDelegateForUpdate();
|
||||||
|
updated.patchRealmLocalizationTexts(locale, localizationTexts);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeRealmLocalizationTexts(String locale) {
|
||||||
|
getDelegateForUpdate();
|
||||||
|
return updated.removeRealmLocalizationTexts(locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Map<String, String>> getRealmLocalizationTexts() {
|
||||||
|
if (isUpdated()) return updated.getRealmLocalizationTexts();
|
||||||
|
return cached.getRealmLocalizationTexts();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, String> getRealmLocalizationTextsByLocale(String locale) {
|
||||||
|
if (isUpdated()) return updated.getRealmLocalizationTextsByLocale(locale);
|
||||||
|
|
||||||
|
Map<String, String> localizationTexts = Collections.emptyMap();
|
||||||
|
if (cached.getRealmLocalizationTexts() != null && cached.getRealmLocalizationTexts().containsKey(locale)) {
|
||||||
|
localizationTexts = cached.getRealmLocalizationTexts().get(locale);
|
||||||
|
}
|
||||||
|
return Collections.unmodifiableMap(localizationTexts);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return String.format("%s@%08x", getId(), hashCode());
|
return String.format("%s@%08x", getId(), hashCode());
|
||||||
|
|
|
@ -171,7 +171,6 @@ public class RealmCacheSession implements CacheRealmProvider {
|
||||||
return groupDelegate;
|
return groupDelegate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void registerRealmInvalidation(String id, String name) {
|
public void registerRealmInvalidation(String id, String name) {
|
||||||
cache.realmUpdated(id, name, invalidations);
|
cache.realmUpdated(id, name, invalidations);
|
||||||
|
@ -1247,4 +1246,51 @@ public class RealmCacheSession implements CacheRealmProvider {
|
||||||
getRealmDelegate().decreaseRemainingCount(realm, clientInitialAccess);
|
getRealmDelegate().decreaseRemainingCount(realm, clientInitialAccess);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveLocalizationText(RealmModel realm, String locale, String key, String text) {
|
||||||
|
getRealmDelegate().saveLocalizationText(realm, locale, key, text);
|
||||||
|
registerRealmInvalidation(realm.getId(), locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveLocalizationTexts(RealmModel realm, String locale, Map<String, String> localizationTexts) {
|
||||||
|
getRealmDelegate().saveLocalizationTexts(realm, locale, localizationTexts);
|
||||||
|
registerRealmInvalidation(realm.getId(), locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean updateLocalizationText(RealmModel realm, String locale, String key, String text) {
|
||||||
|
boolean wasFound = getRealmDelegate().updateLocalizationText(realm, locale, key, text);
|
||||||
|
if (wasFound) {
|
||||||
|
registerRealmInvalidation(realm.getId(), locale);
|
||||||
|
}
|
||||||
|
return wasFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean deleteLocalizationTextsByLocale(RealmModel realm, String locale) {
|
||||||
|
boolean wasDeleted = getRealmDelegate().deleteLocalizationTextsByLocale(realm, locale);
|
||||||
|
if(wasDeleted) {
|
||||||
|
registerRealmInvalidation(realm.getId(), locale);
|
||||||
|
}
|
||||||
|
return wasDeleted;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean deleteLocalizationText(RealmModel realm, String locale, String key) {
|
||||||
|
boolean wasFound = getRealmDelegate().deleteLocalizationText(realm, locale, key);
|
||||||
|
if (wasFound) {
|
||||||
|
registerRealmInvalidation(realm.getId(), locale);
|
||||||
|
}
|
||||||
|
return wasFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getLocalizationTextsById(RealmModel realm, String locale, String key) {
|
||||||
|
Map<String, String> localizationTexts = getRealm(realm.getId()).getRealmLocalizationTextsByLocale(locale);
|
||||||
|
if(localizationTexts != null) {
|
||||||
|
return localizationTexts.get(key);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -163,6 +163,8 @@ public class CachedRealm extends AbstractExtendableRevisioned {
|
||||||
|
|
||||||
private Map<String, Integer> userActionTokenLifespans;
|
private Map<String, Integer> userActionTokenLifespans;
|
||||||
|
|
||||||
|
protected Map<String, Map<String,String>> realmLocalizationTexts;
|
||||||
|
|
||||||
public CachedRealm(Long revision, RealmModel model) {
|
public CachedRealm(Long revision, RealmModel model) {
|
||||||
super(revision, model.getId());
|
super(revision, model.getId());
|
||||||
name = model.getName();
|
name = model.getName();
|
||||||
|
@ -301,6 +303,7 @@ public class CachedRealm extends AbstractExtendableRevisioned {
|
||||||
} catch (UnsupportedOperationException ex) {
|
} catch (UnsupportedOperationException ex) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
realmLocalizationTexts = model.getRealmLocalizationTexts();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void cacheClientScopes(RealmModel model) {
|
protected void cacheClientScopes(RealmModel model) {
|
||||||
|
@ -718,4 +721,8 @@ public class CachedRealm extends AbstractExtendableRevisioned {
|
||||||
public boolean isAllowUserManagedAccess() {
|
public boolean isAllowUserManagedAccess() {
|
||||||
return allowUserManagedAccess;
|
return allowUserManagedAccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Map<String, Map<String, String>> getRealmLocalizationTexts() {
|
||||||
|
return realmLocalizationTexts;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
82
model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java
Executable file → Normal file
82
model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java
Executable file → Normal file
|
@ -39,12 +39,16 @@ import org.keycloak.models.jpa.entities.ClientInitialAccessEntity;
|
||||||
import org.keycloak.models.jpa.entities.ClientScopeEntity;
|
import org.keycloak.models.jpa.entities.ClientScopeEntity;
|
||||||
import org.keycloak.models.jpa.entities.GroupEntity;
|
import org.keycloak.models.jpa.entities.GroupEntity;
|
||||||
import org.keycloak.models.jpa.entities.RealmEntity;
|
import org.keycloak.models.jpa.entities.RealmEntity;
|
||||||
|
import org.keycloak.models.jpa.entities.RealmLocalizationTextsEntity;
|
||||||
import org.keycloak.models.jpa.entities.RoleEntity;
|
import org.keycloak.models.jpa.entities.RoleEntity;
|
||||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
|
|
||||||
import javax.persistence.EntityManager;
|
import javax.persistence.EntityManager;
|
||||||
import javax.persistence.LockModeType;
|
import javax.persistence.LockModeType;
|
||||||
import javax.persistence.TypedQuery;
|
import javax.persistence.TypedQuery;
|
||||||
|
import javax.persistence.criteria.CriteriaBuilder;
|
||||||
|
import javax.persistence.criteria.CriteriaDelete;
|
||||||
|
import javax.persistence.criteria.Root;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
@ -858,6 +862,84 @@ public class JpaRealmProvider implements RealmProvider, ClientProvider, GroupPro
|
||||||
.executeUpdate();
|
.executeUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private RealmLocalizationTextsEntity getRealmLocalizationTextsEntity(String locale, String realmId) {
|
||||||
|
RealmLocalizationTextsEntity.RealmLocalizationTextEntityKey key = new RealmLocalizationTextsEntity.RealmLocalizationTextEntityKey();
|
||||||
|
key.setRealmId(realmId);
|
||||||
|
key.setLocale(locale);
|
||||||
|
return em.find(RealmLocalizationTextsEntity.class, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean updateLocalizationText(RealmModel realm, String locale, String key, String text) {
|
||||||
|
RealmLocalizationTextsEntity entity = getRealmLocalizationTextsEntity(locale, realm.getId());
|
||||||
|
if (entity != null && entity.getTexts() != null && entity.getTexts().containsKey(key)) {
|
||||||
|
entity.getTexts().put(key, text);
|
||||||
|
|
||||||
|
em.persist(entity);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveLocalizationText(RealmModel realm, String locale, String key, String text) {
|
||||||
|
RealmLocalizationTextsEntity entity = getRealmLocalizationTextsEntity(locale, realm.getId());
|
||||||
|
if(entity == null) {
|
||||||
|
entity = new RealmLocalizationTextsEntity();
|
||||||
|
entity.setRealmId(realm.getId());
|
||||||
|
entity.setLocale(locale);
|
||||||
|
entity.setTexts(new HashMap<>());
|
||||||
|
}
|
||||||
|
entity.getTexts().put(key, text);
|
||||||
|
em.persist(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveLocalizationTexts(RealmModel realm, String locale, Map<String, String> localizationTexts) {
|
||||||
|
RealmLocalizationTextsEntity entity = new RealmLocalizationTextsEntity();
|
||||||
|
entity.setTexts(localizationTexts);
|
||||||
|
entity.setLocale(locale);
|
||||||
|
entity.setRealmId(realm.getId());
|
||||||
|
em.merge(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean deleteLocalizationTextsByLocale(RealmModel realm, String locale) {
|
||||||
|
CriteriaBuilder builder = em.getCriteriaBuilder();
|
||||||
|
CriteriaDelete<RealmLocalizationTextsEntity> criteriaDelete =
|
||||||
|
builder.createCriteriaDelete(RealmLocalizationTextsEntity.class);
|
||||||
|
Root<RealmLocalizationTextsEntity> root = criteriaDelete.from(RealmLocalizationTextsEntity.class);
|
||||||
|
|
||||||
|
criteriaDelete.where(builder.and(
|
||||||
|
builder.equal(root.get("realmId"), realm.getId()),
|
||||||
|
builder.equal(root.get("locale"), locale)));
|
||||||
|
int linesUpdated = em.createQuery(criteriaDelete).executeUpdate();
|
||||||
|
return linesUpdated == 1?true:false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getLocalizationTextsById(RealmModel realm, String locale, String key) {
|
||||||
|
RealmLocalizationTextsEntity entity = getRealmLocalizationTextsEntity(locale, realm.getId());
|
||||||
|
if (entity != null && entity.getTexts() != null && entity.getTexts().containsKey(key)) {
|
||||||
|
return entity.getTexts().get(key);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean deleteLocalizationText(RealmModel realm, String locale, String key) {
|
||||||
|
RealmLocalizationTextsEntity entity = getRealmLocalizationTextsEntity(locale, realm.getId());
|
||||||
|
if (entity != null && entity.getTexts() != null && entity.getTexts().containsKey(key)) {
|
||||||
|
entity.getTexts().remove(key);
|
||||||
|
|
||||||
|
em.persist(entity);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private ClientInitialAccessModel entityToModel(ClientInitialAccessEntity entity) {
|
private ClientInitialAccessModel entityToModel(ClientInitialAccessEntity entity) {
|
||||||
ClientInitialAccessModel model = new ClientInitialAccessModel();
|
ClientInitialAccessModel model = new ClientInitialAccessModel();
|
||||||
model.setId(entity.getId());
|
model.setId(entity.getId());
|
||||||
|
|
|
@ -2225,6 +2225,53 @@ public class RealmAdapter implements RealmModel, JpaModel<RealmEntity> {
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void patchRealmLocalizationTexts(String locale, Map<String, String> localizationTexts) {
|
||||||
|
Map<String, RealmLocalizationTextsEntity> currentLocalizationTexts = realm.getRealmLocalizationTexts();
|
||||||
|
if(currentLocalizationTexts.containsKey(locale)) {
|
||||||
|
RealmLocalizationTextsEntity localizationTextsEntity = currentLocalizationTexts.get(locale);
|
||||||
|
localizationTextsEntity.getTexts().putAll(localizationTexts);
|
||||||
|
|
||||||
|
em.persist(localizationTextsEntity);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
RealmLocalizationTextsEntity realmLocalizationTextsEntity = new RealmLocalizationTextsEntity();
|
||||||
|
realmLocalizationTextsEntity.setRealmId(realm.getId());
|
||||||
|
realmLocalizationTextsEntity.setLocale(locale);
|
||||||
|
realmLocalizationTextsEntity.setTexts(localizationTexts);
|
||||||
|
|
||||||
|
em.persist(realmLocalizationTextsEntity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeRealmLocalizationTexts(String locale) {
|
||||||
|
if (locale == null) return false;
|
||||||
|
if (realm.getRealmLocalizationTexts().containsKey(locale))
|
||||||
|
{
|
||||||
|
em.remove(realm.getRealmLocalizationTexts().get(locale));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Map<String, String>> getRealmLocalizationTexts() {
|
||||||
|
Map<String, Map<String, String>> localizationTexts = new HashMap<>();
|
||||||
|
realm.getRealmLocalizationTexts().forEach((locale, localizationTextsEntity) -> {
|
||||||
|
localizationTexts.put(localizationTextsEntity.getLocale(), localizationTextsEntity.getTexts());
|
||||||
|
});
|
||||||
|
return localizationTexts;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, String> getRealmLocalizationTextsByLocale(String locale) {
|
||||||
|
if (realm.getRealmLocalizationTexts().containsKey(locale)) {
|
||||||
|
return realm.getRealmLocalizationTexts().get(locale).getTexts();
|
||||||
|
}
|
||||||
|
return Collections.emptyMap();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return String.format("%s@%08x", getId(), hashCode());
|
return String.format("%s@%08x", getId(), hashCode());
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||||
|
* and other contributors as indicated by the @author tags.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache 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://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* 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.keycloak.models.jpa.converter;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Map;
|
||||||
|
import javax.persistence.AttributeConverter;
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
import org.keycloak.util.JsonSerialization;
|
||||||
|
|
||||||
|
public class MapStringConverter implements AttributeConverter<Map<String, String>, String> {
|
||||||
|
private static final Logger logger = Logger.getLogger(MapStringConverter.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String convertToDatabaseColumn(Map<String, String> attribute) {
|
||||||
|
try {
|
||||||
|
return JsonSerialization.writeValueAsString(attribute);
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.error("Error while converting Map to JSON String: ", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, String> convertToEntityAttribute(String dbData) {
|
||||||
|
try {
|
||||||
|
return JsonSerialization.readValue(dbData, Map.class);
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.error("Error while converting JSON String to Map: ", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -28,6 +28,7 @@ import javax.persistence.FetchType;
|
||||||
import javax.persistence.Id;
|
import javax.persistence.Id;
|
||||||
import javax.persistence.JoinColumn;
|
import javax.persistence.JoinColumn;
|
||||||
import javax.persistence.JoinTable;
|
import javax.persistence.JoinTable;
|
||||||
|
import javax.persistence.MapKey;
|
||||||
import javax.persistence.MapKeyColumn;
|
import javax.persistence.MapKeyColumn;
|
||||||
import javax.persistence.NamedQueries;
|
import javax.persistence.NamedQueries;
|
||||||
import javax.persistence.NamedQuery;
|
import javax.persistence.NamedQuery;
|
||||||
|
@ -242,6 +243,9 @@ public class RealmEntity {
|
||||||
@Column(name="ALLOW_USER_MANAGED_ACCESS")
|
@Column(name="ALLOW_USER_MANAGED_ACCESS")
|
||||||
private boolean allowUserManagedAccess;
|
private boolean allowUserManagedAccess;
|
||||||
|
|
||||||
|
@OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "realmId")
|
||||||
|
@MapKey(name="locale")
|
||||||
|
Map<String, RealmLocalizationTextsEntity> realmLocalizationTexts;
|
||||||
|
|
||||||
public String getId() {
|
public String getId() {
|
||||||
return id;
|
return id;
|
||||||
|
@ -834,6 +838,17 @@ public class RealmEntity {
|
||||||
return allowUserManagedAccess;
|
return allowUserManagedAccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Map<String, RealmLocalizationTextsEntity> getRealmLocalizationTexts() {
|
||||||
|
if (realmLocalizationTexts == null) {
|
||||||
|
realmLocalizationTexts = new HashMap<>();
|
||||||
|
}
|
||||||
|
return realmLocalizationTexts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRealmLocalizationTexts(Map<String, RealmLocalizationTextsEntity> realmLocalizationTexts) {
|
||||||
|
this.realmLocalizationTexts = realmLocalizationTexts;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) return true;
|
if (this == o) return true;
|
||||||
|
|
|
@ -0,0 +1,129 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||||
|
* and other contributors as indicated by the @author tags.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache 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://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* 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.keycloak.models.jpa.entities;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Convert;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.IdClass;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
import org.keycloak.models.jpa.converter.MapStringConverter;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@IdClass(RealmLocalizationTextsEntity.RealmLocalizationTextEntityKey.class)
|
||||||
|
@Table(name = "REALM_LOCALIZATIONS")
|
||||||
|
public class RealmLocalizationTextsEntity {
|
||||||
|
static public class RealmLocalizationTextEntityKey implements Serializable {
|
||||||
|
private String realmId;
|
||||||
|
private String locale;
|
||||||
|
|
||||||
|
public String getRealmId() {
|
||||||
|
return realmId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRealmId(String realmId) {
|
||||||
|
this.realmId = realmId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLocale() {
|
||||||
|
return locale;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLocale(String locale) {
|
||||||
|
this.locale = locale;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
RealmLocalizationTextEntityKey that = (RealmLocalizationTextEntityKey) o;
|
||||||
|
return Objects.equals(realmId, that.realmId) &&
|
||||||
|
Objects.equals(locale, that.locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(realmId, locale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@Column(name = "REALM_ID")
|
||||||
|
private String realmId;
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@Column(name = "LOCALE")
|
||||||
|
private String locale;
|
||||||
|
|
||||||
|
@Column(name = "TEXTS")
|
||||||
|
@Convert(converter = MapStringConverter.class)
|
||||||
|
private Map<String,String> texts;
|
||||||
|
|
||||||
|
public Map<String,String> getTexts() {
|
||||||
|
return texts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTexts(Map<String,String> texts) {
|
||||||
|
this.texts = texts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLocale() {
|
||||||
|
return locale;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLocale(String locale) {
|
||||||
|
this.locale = locale;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRealmId() {
|
||||||
|
return realmId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRealmId(String realmId) {
|
||||||
|
this.realmId = realmId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "LocalizationTextEntity{" +
|
||||||
|
", text='" + texts + '\'' +
|
||||||
|
", locale='" + locale + '\'' +
|
||||||
|
", realmId='" + realmId + '\'' +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
RealmLocalizationTextsEntity that = (RealmLocalizationTextsEntity) o;
|
||||||
|
return Objects.equals(realmId, that.realmId) &&
|
||||||
|
Objects.equals(locale, that.locale) &&
|
||||||
|
Objects.equals(texts, that.texts);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(realmId, locale, texts);
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,4 +26,20 @@
|
||||||
<dropForeignKeyConstraint baseTableName="SCOPE_MAPPING" constraintName="FK_P3RH9GRKU11KQFRS4FLTT7RNQ"/>
|
<dropForeignKeyConstraint baseTableName="SCOPE_MAPPING" constraintName="FK_P3RH9GRKU11KQFRS4FLTT7RNQ"/>
|
||||||
</changeSet>
|
</changeSet>
|
||||||
|
|
||||||
|
<changeSet author="keycloak" id="12.1.0-add-realm-localization-table">
|
||||||
|
<createTable tableName="REALM_LOCALIZATIONS">
|
||||||
|
<column name="REALM_ID" type="VARCHAR(255)">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column name="LOCALE" type="VARCHAR(255)">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column name="TEXTS" type="CLOB">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
</createTable>
|
||||||
|
|
||||||
|
<addPrimaryKey columnNames="REALM_ID, LOCALE" tableName="REALM_LOCALIZATIONS"/>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
</databaseChangeLog>
|
</databaseChangeLog>
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
<class>org.keycloak.models.jpa.entities.FederatedIdentityEntity</class>
|
<class>org.keycloak.models.jpa.entities.FederatedIdentityEntity</class>
|
||||||
<class>org.keycloak.models.jpa.entities.MigrationModelEntity</class>
|
<class>org.keycloak.models.jpa.entities.MigrationModelEntity</class>
|
||||||
<class>org.keycloak.models.jpa.entities.UserEntity</class>
|
<class>org.keycloak.models.jpa.entities.UserEntity</class>
|
||||||
|
<class>org.keycloak.models.jpa.entities.RealmLocalizationTextsEntity</class>
|
||||||
<class>org.keycloak.models.jpa.entities.UserRequiredActionEntity</class>
|
<class>org.keycloak.models.jpa.entities.UserRequiredActionEntity</class>
|
||||||
<class>org.keycloak.models.jpa.entities.UserAttributeEntity</class>
|
<class>org.keycloak.models.jpa.entities.UserAttributeEntity</class>
|
||||||
<class>org.keycloak.models.jpa.entities.UserRoleMappingEntity</class>
|
<class>org.keycloak.models.jpa.entities.UserRoleMappingEntity</class>
|
||||||
|
|
|
@ -770,6 +770,15 @@ public interface RealmModel extends RoleContainerModel {
|
||||||
void addDefaultClientScope(ClientScopeModel clientScope, boolean defaultScope);
|
void addDefaultClientScope(ClientScopeModel clientScope, boolean defaultScope);
|
||||||
void removeDefaultClientScope(ClientScopeModel clientScope);
|
void removeDefaultClientScope(ClientScopeModel clientScope);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Patches the realm-specific localization texts. This method will not delete any text.
|
||||||
|
* It updates texts, which are already stored or create new ones if the key does not exist yet.
|
||||||
|
*/
|
||||||
|
void patchRealmLocalizationTexts(String locale, Map<String, String> localizationTexts);
|
||||||
|
boolean removeRealmLocalizationTexts(String locale);
|
||||||
|
Map<String, Map<String, String>> getRealmLocalizationTexts();
|
||||||
|
Map<String, String> getRealmLocalizationTextsByLocale(String locale);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated Use {@link #getDefaultClientScopesStream(boolean) getDefaultClientScopesStream} instead.
|
* @deprecated Use {@link #getDefaultClientScopesStream(boolean) getDefaultClientScopesStream} instead.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
package org.keycloak.models;
|
package org.keycloak.models;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
import org.keycloak.migration.MigrationModel;
|
import org.keycloak.migration.MigrationModel;
|
||||||
import org.keycloak.provider.Provider;
|
import org.keycloak.provider.Provider;
|
||||||
|
|
||||||
|
@ -80,6 +81,18 @@ public interface RealmProvider extends Provider /* TODO: Remove in future versio
|
||||||
void removeExpiredClientInitialAccess();
|
void removeExpiredClientInitialAccess();
|
||||||
void decreaseRemainingCount(RealmModel realm, ClientInitialAccessModel clientInitialAccess); // Separate provider method to ensure we decrease remainingCount atomically instead of doing classic update
|
void decreaseRemainingCount(RealmModel realm, ClientInitialAccessModel clientInitialAccess); // Separate provider method to ensure we decrease remainingCount atomically instead of doing classic update
|
||||||
|
|
||||||
|
void saveLocalizationText(RealmModel realm, String locale, String key, String text);
|
||||||
|
|
||||||
|
void saveLocalizationTexts(RealmModel realm, String locale, Map<String, String> localizationTexts);
|
||||||
|
|
||||||
|
boolean updateLocalizationText(RealmModel realm, String locale, String key, String text);
|
||||||
|
|
||||||
|
boolean deleteLocalizationTextsByLocale(RealmModel realm, String locale);
|
||||||
|
|
||||||
|
boolean deleteLocalizationText(RealmModel realm, String locale, String key);
|
||||||
|
|
||||||
|
String getLocalizationTextsById(RealmModel realm, String locale, String key);
|
||||||
|
|
||||||
// The methods below are going to be removed in future version of Keycloak
|
// The methods below are going to be removed in future version of Keycloak
|
||||||
// Sadly, we have to copy-paste the declarations from the respective interfaces
|
// Sadly, we have to copy-paste the declarations from the respective interfaces
|
||||||
// including the "default" body to be able to add a note on deprecation
|
// including the "default" body to be able to add a note on deprecation
|
||||||
|
|
|
@ -28,7 +28,6 @@ import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
|
||||||
import org.keycloak.broker.provider.BrokeredIdentityContext;
|
import org.keycloak.broker.provider.BrokeredIdentityContext;
|
||||||
import org.keycloak.common.util.ObjectUtil;
|
import org.keycloak.common.util.ObjectUtil;
|
||||||
import org.keycloak.email.EmailException;
|
import org.keycloak.email.EmailException;
|
||||||
|
@ -209,6 +208,8 @@ public class FreeMarkerEmailTemplateProvider implements EmailTemplateProvider {
|
||||||
Locale locale = session.getContext().resolveLocale(user);
|
Locale locale = session.getContext().resolveLocale(user);
|
||||||
attributes.put("locale", locale);
|
attributes.put("locale", locale);
|
||||||
Properties rb = theme.getMessages(locale);
|
Properties rb = theme.getMessages(locale);
|
||||||
|
Map<String, String> localizationTexts = realm.getRealmLocalizationTextsByLocale(locale.toLanguageTag());
|
||||||
|
rb.putAll(localizationTexts);
|
||||||
attributes.put("msg", new MessageFormatterMethod(locale, rb));
|
attributes.put("msg", new MessageFormatterMethod(locale, rb));
|
||||||
attributes.put("properties", theme.getProperties());
|
attributes.put("properties", theme.getProperties());
|
||||||
String subject = new MessageFormat(rb.getProperty(subjectKey, subjectKey), locale).format(subjectAttributes.toArray());
|
String subject = new MessageFormat(rb.getProperty(subjectKey, subjectKey), locale).format(subjectAttributes.toArray());
|
||||||
|
|
|
@ -129,6 +129,8 @@ public class FreeMarkerAccountProvider implements AccountProvider {
|
||||||
|
|
||||||
Locale locale = session.getContext().resolveLocale(user);
|
Locale locale = session.getContext().resolveLocale(user);
|
||||||
Properties messagesBundle = handleThemeResources(theme, locale, attributes);
|
Properties messagesBundle = handleThemeResources(theme, locale, attributes);
|
||||||
|
Map<String, String> localizationTexts = realm.getRealmLocalizationTextsByLocale(locale.toLanguageTag());
|
||||||
|
messagesBundle.putAll(localizationTexts);
|
||||||
|
|
||||||
URI baseUri = uriInfo.getBaseUri();
|
URI baseUri = uriInfo.getBaseUri();
|
||||||
UriBuilder baseUriBuilder = uriInfo.getBaseUriBuilder();
|
UriBuilder baseUriBuilder = uriInfo.getBaseUriBuilder();
|
||||||
|
|
|
@ -182,6 +182,8 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
||||||
|
|
||||||
Locale locale = session.getContext().resolveLocale(user);
|
Locale locale = session.getContext().resolveLocale(user);
|
||||||
Properties messagesBundle = handleThemeResources(theme, locale);
|
Properties messagesBundle = handleThemeResources(theme, locale);
|
||||||
|
Map<String, String> localizationTexts = realm.getRealmLocalizationTextsByLocale(locale.toLanguageTag());
|
||||||
|
messagesBundle.putAll(localizationTexts);
|
||||||
|
|
||||||
handleMessages(locale, messagesBundle);
|
handleMessages(locale, messagesBundle);
|
||||||
|
|
||||||
|
@ -248,6 +250,8 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
||||||
|
|
||||||
Locale locale = session.getContext().resolveLocale(user);
|
Locale locale = session.getContext().resolveLocale(user);
|
||||||
Properties messagesBundle = handleThemeResources(theme, locale);
|
Properties messagesBundle = handleThemeResources(theme, locale);
|
||||||
|
Map<String, String> localizationTexts = realm.getRealmLocalizationTextsByLocale(locale.getCountry());
|
||||||
|
messagesBundle.putAll(localizationTexts);
|
||||||
|
|
||||||
handleMessages(locale, messagesBundle);
|
handleMessages(locale, messagesBundle);
|
||||||
|
|
||||||
|
@ -353,6 +357,8 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
||||||
|
|
||||||
Locale locale = session.getContext().resolveLocale(user);
|
Locale locale = session.getContext().resolveLocale(user);
|
||||||
Properties messagesBundle = handleThemeResources(theme, locale);
|
Properties messagesBundle = handleThemeResources(theme, locale);
|
||||||
|
Map<String, String> localizationTexts = realm.getRealmLocalizationTextsByLocale(locale.getCountry());
|
||||||
|
messagesBundle.putAll(localizationTexts);
|
||||||
FormMessage msg = new FormMessage(null, message);
|
FormMessage msg = new FormMessage(null, message);
|
||||||
return formatMessage(msg, messagesBundle, locale);
|
return formatMessage(msg, messagesBundle, locale);
|
||||||
}
|
}
|
||||||
|
@ -369,6 +375,8 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
||||||
|
|
||||||
Locale locale = session.getContext().resolveLocale(user);
|
Locale locale = session.getContext().resolveLocale(user);
|
||||||
Properties messagesBundle = handleThemeResources(theme, locale);
|
Properties messagesBundle = handleThemeResources(theme, locale);
|
||||||
|
Map<String, String> localizationTexts = realm.getRealmLocalizationTextsByLocale(locale.getCountry());
|
||||||
|
messagesBundle.putAll(localizationTexts);
|
||||||
FormMessage msg = new FormMessage(message, (Object[]) parameters);
|
FormMessage msg = new FormMessage(message, (Object[]) parameters);
|
||||||
return formatMessage(msg, messagesBundle, locale);
|
return formatMessage(msg, messagesBundle, locale);
|
||||||
}
|
}
|
||||||
|
|
|
@ -220,6 +220,15 @@ public class RealmAdminResource {
|
||||||
return clientScopesResource;
|
return clientScopesResource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base path for managing localization under this realm.
|
||||||
|
*/
|
||||||
|
@Path("localization")
|
||||||
|
public RealmLocalizationResource getLocalization() {
|
||||||
|
RealmLocalizationResource resource = new RealmLocalizationResource(realm, auth);
|
||||||
|
ResteasyProviderFactory.getInstance().injectProperties(resource);
|
||||||
|
return resource;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get realm default client scopes. Only name and ids are returned.
|
* Get realm default client scopes. Only name and ids are returned.
|
||||||
|
|
|
@ -0,0 +1,164 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||||
|
* and other contributors as indicated by the @author tags.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache 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://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* 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.keycloak.services.resources.admin;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.jboss.resteasy.plugins.providers.multipart.InputPart;
|
||||||
|
import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput;
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.ModelDuplicateException;
|
||||||
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.ws.rs.BadRequestException;
|
||||||
|
import javax.ws.rs.Consumes;
|
||||||
|
import javax.ws.rs.DELETE;
|
||||||
|
import javax.ws.rs.GET;
|
||||||
|
import javax.ws.rs.NotFoundException;
|
||||||
|
import javax.ws.rs.PATCH;
|
||||||
|
import javax.ws.rs.POST;
|
||||||
|
import javax.ws.rs.PUT;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.PathParam;
|
||||||
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.core.Context;
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
|
import org.keycloak.util.JsonSerialization;
|
||||||
|
|
||||||
|
public class RealmLocalizationResource {
|
||||||
|
private final RealmModel realm;
|
||||||
|
private final AdminPermissionEvaluator auth;
|
||||||
|
|
||||||
|
@Context
|
||||||
|
protected KeycloakSession session;
|
||||||
|
|
||||||
|
public RealmLocalizationResource(RealmModel realm, AdminPermissionEvaluator auth) {
|
||||||
|
this.realm = realm;
|
||||||
|
this.auth = auth;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Path("{locale}/{key}")
|
||||||
|
@PUT
|
||||||
|
@Consumes(MediaType.TEXT_PLAIN)
|
||||||
|
public void saveRealmLocalizationText(@PathParam("locale") String locale, @PathParam("key") String key,
|
||||||
|
String text) {
|
||||||
|
this.auth.realm().requireManageRealm();
|
||||||
|
try {
|
||||||
|
session.realms().saveLocalizationText(realm, locale, key, text);
|
||||||
|
} catch (ModelDuplicateException e) {
|
||||||
|
throw new BadRequestException(
|
||||||
|
String.format("Localization text %s for the locale %s and realm %s already exists.",
|
||||||
|
key, locale, realm.getId()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Import localization from uploaded JSON file
|
||||||
|
*/
|
||||||
|
@POST
|
||||||
|
@Path("{locale}")
|
||||||
|
@Consumes(MediaType.MULTIPART_FORM_DATA)
|
||||||
|
public void patchRealmLocalizationTextsFromFile(@PathParam("locale") String locale, MultipartFormDataInput input)
|
||||||
|
throws IOException {
|
||||||
|
this.auth.realm().requireManageRealm();
|
||||||
|
|
||||||
|
Map<String, List<InputPart>> formDataMap = input.getFormDataMap();
|
||||||
|
if (!formDataMap.containsKey("file")) {
|
||||||
|
throw new BadRequestException();
|
||||||
|
}
|
||||||
|
InputPart file = formDataMap.get("file").get(0);
|
||||||
|
try (InputStream inputStream = file.getBody(InputStream.class, null)) {
|
||||||
|
TypeReference<HashMap<String, String>> typeRef = new TypeReference<HashMap<String, String>>() {
|
||||||
|
};
|
||||||
|
Map<String, String> rep = JsonSerialization.readValue(inputStream, typeRef);
|
||||||
|
realm.patchRealmLocalizationTexts(locale, rep);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new BadRequestException("Could not read file.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@PATCH
|
||||||
|
@Path("{locale}")
|
||||||
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
|
public void patchRealmLocalizationTexts(@PathParam("locale") String locale, Map<String, String> loclizationTexts) {
|
||||||
|
this.auth.realm().requireManageRealm();
|
||||||
|
realm.patchRealmLocalizationTexts(locale, loclizationTexts);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Path("{locale}")
|
||||||
|
@DELETE
|
||||||
|
public void deleteRealmLocalizationTexts(@PathParam("locale") String locale) {
|
||||||
|
this.auth.realm().requireManageRealm();
|
||||||
|
if(!realm.removeRealmLocalizationTexts(locale)) {
|
||||||
|
throw new NotFoundException("No localization texts for locale " + locale + " found.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Path("{locale}/{key}")
|
||||||
|
@DELETE
|
||||||
|
public void deleteRealmLocalizationText(@PathParam("locale") String locale, @PathParam("key") String key) {
|
||||||
|
this.auth.realm().requireManageRealm();
|
||||||
|
if (!session.realms().deleteLocalizationText(realm, locale, key)) {
|
||||||
|
throw new NotFoundException("Localization text not found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
public List<String> getRealmLocalizationLocales() {
|
||||||
|
this.auth.realm().requireViewRealm();
|
||||||
|
|
||||||
|
List<String> realmLocalesList = new ArrayList<>(realm.getRealmLocalizationTexts().keySet());
|
||||||
|
Collections.sort(realmLocalesList);
|
||||||
|
|
||||||
|
return realmLocalesList;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Path("{locale}")
|
||||||
|
@GET
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
public Map<String, String> getRealmLocalizationTexts(@PathParam("locale") String locale) {
|
||||||
|
this.auth.realm().requireViewRealm();
|
||||||
|
return realm.getRealmLocalizationTextsByLocale(locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Path("{locale}/{key}")
|
||||||
|
@GET
|
||||||
|
@Produces(MediaType.TEXT_PLAIN)
|
||||||
|
public String getRealmLocalizationText(@PathParam("locale") String locale, @PathParam("key") String key) {
|
||||||
|
this.auth.realm().requireViewRealm();
|
||||||
|
String text = session.realms().getLocalizationTextsById(realm, locale, key);
|
||||||
|
if (text != null) {
|
||||||
|
return text;
|
||||||
|
} else {
|
||||||
|
throw new NotFoundException("Localization text not found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,9 +26,8 @@ import org.keycloak.admin.client.resource.RealmResource;
|
||||||
import org.keycloak.common.util.ConcurrentMultivaluedHashMap;
|
import org.keycloak.common.util.ConcurrentMultivaluedHashMap;
|
||||||
import org.keycloak.testsuite.arquillian.TestContext;
|
import org.keycloak.testsuite.arquillian.TestContext;
|
||||||
import com.google.common.collect.Streams;
|
import com.google.common.collect.Streams;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.concurrent.ConcurrentLinkedDeque;
|
import java.util.concurrent.ConcurrentLinkedDeque;
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enlist resources to be cleaned after test method
|
* Enlist resources to be cleaned after test method
|
||||||
|
@ -46,6 +45,7 @@ public class TestCleanup {
|
||||||
private static final String GROUP_IDS = "GROUP_IDS";
|
private static final String GROUP_IDS = "GROUP_IDS";
|
||||||
private static final String AUTH_FLOW_IDS = "AUTH_FLOW_IDS";
|
private static final String AUTH_FLOW_IDS = "AUTH_FLOW_IDS";
|
||||||
private static final String AUTH_CONFIG_IDS = "AUTH_CONFIG_IDS";
|
private static final String AUTH_CONFIG_IDS = "AUTH_CONFIG_IDS";
|
||||||
|
private static final String LOCALIZATION_LANGUAGES = "LOCALIZATION_LANGUAGES";
|
||||||
|
|
||||||
private final TestContext testContext;
|
private final TestContext testContext;
|
||||||
private final String realmName;
|
private final String realmName;
|
||||||
|
@ -115,6 +115,9 @@ public class TestCleanup {
|
||||||
entities.add(AUTH_FLOW_IDS, flowId);
|
entities.add(AUTH_FLOW_IDS, flowId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addLocalization(String language) {
|
||||||
|
entities.add(LOCALIZATION_LANGUAGES, language);
|
||||||
|
}
|
||||||
|
|
||||||
public void addAuthenticationConfigId(String executionConfigId) {
|
public void addAuthenticationConfigId(String executionConfigId) {
|
||||||
entities.add(AUTH_CONFIG_IDS, executionConfigId);
|
entities.add(AUTH_CONFIG_IDS, executionConfigId);
|
||||||
|
@ -225,6 +228,17 @@ public class TestCleanup {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<String> localizationLanguages = entities.get(LOCALIZATION_LANGUAGES);
|
||||||
|
if (localizationLanguages != null) {
|
||||||
|
for (String localizationLanguage : localizationLanguages) {
|
||||||
|
try {
|
||||||
|
realm.localization().deleteRealmLocalizationTexts(localizationLanguage);
|
||||||
|
} catch (NotFoundException nfe) {
|
||||||
|
// Localization texts might be already deleted in the test
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Keycloak getAdminClient() {
|
private Keycloak getAdminClient() {
|
||||||
|
|
|
@ -0,0 +1,130 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||||
|
* and other contributors as indicated by the @author tags.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache 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://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* 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.keycloak.testsuite.admin;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
|
||||||
|
import org.hamcrest.CoreMatchers;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.keycloak.admin.client.resource.RealmLocalizationResource;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.ws.rs.NotFoundException;
|
||||||
|
|
||||||
|
|
||||||
|
public class RealmRealmLocalizationResourceTest extends AbstractAdminTest {
|
||||||
|
|
||||||
|
private RealmLocalizationResource resource;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void before() {
|
||||||
|
adminClient.realm(REALM_NAME).localization().saveRealmLocalizationText("en", "key-a", "text-a_en");
|
||||||
|
adminClient.realm(REALM_NAME).localization().saveRealmLocalizationText("en", "key-b", "text-b_en");
|
||||||
|
adminClient.realm(REALM_NAME).localization().saveRealmLocalizationText("de", "key-a", "text-a_de");
|
||||||
|
|
||||||
|
getCleanup().addLocalization("en");
|
||||||
|
getCleanup().addLocalization("de");
|
||||||
|
|
||||||
|
resource = adminClient.realm(REALM_NAME).localization();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getRealmSpecificLocales() {
|
||||||
|
List<String> languages = resource.getRealmSpecificLocales();
|
||||||
|
assertEquals(2, languages.size());
|
||||||
|
assertThat(languages, CoreMatchers.hasItems("en", "de"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getRealmLocalizationTexts() {
|
||||||
|
Map<String, String> localizations = resource.getRealmLocalizationTexts("en");
|
||||||
|
assertNotNull(localizations);
|
||||||
|
assertEquals(2, localizations.size());
|
||||||
|
|
||||||
|
assertEquals("text-a_en", localizations.get("key-a"));
|
||||||
|
assertEquals("text-b_en", localizations.get("key-b"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getRealmLocalizationsNotExists() {
|
||||||
|
Map<String, String> localizations = resource.getRealmLocalizationTexts("zz");
|
||||||
|
assertNotNull(localizations);
|
||||||
|
assertEquals(0, localizations.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getRealmLocalizationText() {
|
||||||
|
String localizationText = resource.getRealmLocalizationText("en", "key-a");
|
||||||
|
assertNotNull(localizationText);
|
||||||
|
assertEquals("text-a_en", localizationText);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = NotFoundException.class)
|
||||||
|
public void getRealmLocalizationTextNotExists() {
|
||||||
|
resource.getRealmLocalizationText("en", "key-zz");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void addRealmLocalizationText() {
|
||||||
|
resource.saveRealmLocalizationText("en", "key-c", "text-c");
|
||||||
|
|
||||||
|
String localizationText = resource.getRealmLocalizationText("en", "key-c");
|
||||||
|
|
||||||
|
assertNotNull(localizationText);
|
||||||
|
assertEquals("text-c", localizationText);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void updateRealmLocalizationText() {
|
||||||
|
resource.saveRealmLocalizationText("en", "key-b", "text-b-new");
|
||||||
|
|
||||||
|
String localizationText = resource.getRealmLocalizationText("en", "key-b");
|
||||||
|
|
||||||
|
assertNotNull(localizationText);
|
||||||
|
assertEquals("text-b-new", localizationText);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void deleteRealmLocalizationText() {
|
||||||
|
resource.deleteRealmLocalizationText("en", "key-a");
|
||||||
|
|
||||||
|
Map<String, String> localizations = resource.getRealmLocalizationTexts("en");
|
||||||
|
assertEquals(1, localizations.size());
|
||||||
|
assertEquals("text-b_en", localizations.get("key-b"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = NotFoundException.class)
|
||||||
|
public void deleteRealmLocalizationTextNotExists() {
|
||||||
|
resource.deleteRealmLocalizationText("en", "zz");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void deleteRealmLocalizationTexts() {
|
||||||
|
resource.deleteRealmLocalizationTexts("en");
|
||||||
|
|
||||||
|
List<String> localizations = resource.getRealmSpecificLocales();
|
||||||
|
assertEquals(1, localizations.size());
|
||||||
|
|
||||||
|
assertThat(localizations, CoreMatchers.hasItems("de"));
|
||||||
|
}
|
||||||
|
}
|
|
@ -62,6 +62,17 @@ i18n-enabled=Internacionalitzaci\u00F3 activa
|
||||||
supported-locales=Idiomes suportats
|
supported-locales=Idiomes suportats
|
||||||
supported-locales.placeholder=Indica l''idioma i prem Intro
|
supported-locales.placeholder=Indica l''idioma i prem Intro
|
||||||
default-locale=Idioma per defecte
|
default-locale=Idioma per defecte
|
||||||
|
#missing-locale=Missing locale.
|
||||||
|
#missing-file=Missing file. Please select a file to upload.
|
||||||
|
#localization-file.upload.success=The localization data has been loaded from file.
|
||||||
|
#localization-file.upload.error=The file can not be uploaded. Please verify the file.
|
||||||
|
#localization-show=Show realm specific localizations
|
||||||
|
#no-localizations-configured=No realm specific localizations configured
|
||||||
|
#add-localization-text=Add localization text
|
||||||
|
#locale.create.success=The Locale has been created.
|
||||||
|
#localization-text.create.success=The localization text has been created.
|
||||||
|
#localization-text.update.success=The localization text has been updated.
|
||||||
|
#localization-text.remove.success=The localization text has been deleted.
|
||||||
realm-cache-enabled=Cach\u00E9 de domini habilitada
|
realm-cache-enabled=Cach\u00E9 de domini habilitada
|
||||||
realm-cache-enabled.tooltip=Activar/desactivar la cach\u00E9 per al domini, client i dades de rols.
|
realm-cache-enabled.tooltip=Activar/desactivar la cach\u00E9 per al domini, client i dades de rols.
|
||||||
user-cache-enabled=Cach\u00E9 d''usuari habilitada
|
user-cache-enabled=Cach\u00E9 d''usuari habilitada
|
||||||
|
@ -107,6 +118,7 @@ realm-tab-login=Inici de sessi\u00F3
|
||||||
realm-tab-keys=Claus
|
realm-tab-keys=Claus
|
||||||
realm-tab-email=Email
|
realm-tab-email=Email
|
||||||
realm-tab-themes=Temes
|
realm-tab-themes=Temes
|
||||||
|
#realm-tab-localization=Localization
|
||||||
realm-tab-cache=Cach\u00E9
|
realm-tab-cache=Cach\u00E9
|
||||||
realm-tab-tokens=Tokens
|
realm-tab-tokens=Tokens
|
||||||
realm-tab-security-defenses=Defenses de seguretat
|
realm-tab-security-defenses=Defenses de seguretat
|
||||||
|
|
|
@ -91,6 +91,18 @@ i18n-enabled=Internationalisierung aktiv
|
||||||
supported-locales=Unterst\u00FCtzte Sprachen
|
supported-locales=Unterst\u00FCtzte Sprachen
|
||||||
#supported-locales.placeholder=Type a locale and enter
|
#supported-locales.placeholder=Type a locale and enter
|
||||||
#default-locale=Default Locale
|
#default-locale=Default Locale
|
||||||
|
localization-upload-file=Hochladen einer JSON Datei mit Lokalisierungstexten
|
||||||
|
missing-locale=Locale fehlt.
|
||||||
|
missing-file=Datei fehlt. Bitte eine Datei f\u00FCr den Upload ausw\u00E4hlen.
|
||||||
|
localization-file.upload.success=Die Internationalisierungstexte wurden importiert.
|
||||||
|
localization-file.upload.error=Die Datei konnte nicht hochgeladen werden. Bitte \u00FCberpr\u00FCfen Sie die Datei.
|
||||||
|
localization-show=Realm-spezifische Lokalisierungstexte
|
||||||
|
no-localizations-configured=Es sind zur Zeit keine realm-spezifischen Lokalisierungstexte vorhanden.
|
||||||
|
add-localization-text=Lokalisierungstext hinzuf\u00FCgen
|
||||||
|
locale.create.success=Die Locale wurde ertellt.
|
||||||
|
localization-text.create.success=Der Lokalisierungstext wurde erstellt.
|
||||||
|
localization-text.update.success=Der Lokalisierungstext wurde aktualisiert.
|
||||||
|
localization-text.remove.success=Der Lokalisierungstext wurde gel\u00F6scht.
|
||||||
#realm-cache-clear=Realm Cache
|
#realm-cache-clear=Realm Cache
|
||||||
#realm-cache-clear.tooltip=Clears all entries from the realm cache (this will clear entries for all realms)
|
#realm-cache-clear.tooltip=Clears all entries from the realm cache (this will clear entries for all realms)
|
||||||
#user-cache-clear=User Cache
|
#user-cache-clear=User Cache
|
||||||
|
@ -175,6 +187,7 @@ days=Tage
|
||||||
#realm-tab-keys=Keys
|
#realm-tab-keys=Keys
|
||||||
#realm-tab-email=Email
|
#realm-tab-email=Email
|
||||||
#realm-tab-themes=Themes
|
#realm-tab-themes=Themes
|
||||||
|
realm-tab-localization=Internationalisierung
|
||||||
#realm-tab-cache=Cache
|
#realm-tab-cache=Cache
|
||||||
#realm-tab-tokens=Tokens
|
#realm-tab-tokens=Tokens
|
||||||
#realm-tab-client-registration=Client Registration
|
#realm-tab-client-registration=Client Registration
|
||||||
|
@ -318,7 +331,7 @@ add-client=Client hinzuf\u00FCgen
|
||||||
#idp-sso-relay-state=IDP Initiated SSO Relay State
|
#idp-sso-relay-state=IDP Initiated SSO Relay State
|
||||||
#idp-sso-relay-state.tooltip=Relay state you want to send with SAML request when you want to do IDP Initiated SSO.
|
#idp-sso-relay-state.tooltip=Relay state you want to send with SAML request when you want to do IDP Initiated SSO.
|
||||||
web-origins=Web Origins
|
web-origins=Web Origins
|
||||||
web-origins.tooltip=Erlaubte CORS Origins. Um alle Origins der Valid Redirect URIs zu erlauben, fügen Sie ein '+' hinzu. Dabei wird der '*' Platzhalter nicht mit übernommen. Um alle Origins zu erlauben, geben Sie explizit einen Eintrag mit '*' an.
|
web-origins.tooltip=Erlaubte CORS Origins. Um alle Origins der Valid Redirect URIs zu erlauben, f\u00FCgen Sie ein '+' hinzu. Dabei wird der '*' Platzhalter nicht mit \u00FCbernommen. Um alle Origins zu erlauben, geben Sie explizit einen Eintrag mit '*' an.
|
||||||
#fine-oidc-endpoint-conf=Fine Grain OpenID Connect Configuration
|
#fine-oidc-endpoint-conf=Fine Grain OpenID Connect Configuration
|
||||||
#fine-oidc-endpoint-conf.tooltip=Expand this section to configure advanced settings of this client related to OpenID Connect protocol
|
#fine-oidc-endpoint-conf.tooltip=Expand this section to configure advanced settings of this client related to OpenID Connect protocol
|
||||||
#user-info-signed-response-alg=User Info Signed Response Algorithm
|
#user-info-signed-response-alg=User Info Signed Response Algorithm
|
||||||
|
@ -505,13 +518,13 @@ last-refresh=Letzte Aktualisierung
|
||||||
#first-broker-login-flow=First Login Flow
|
#first-broker-login-flow=First Login Flow
|
||||||
#post-broker-login-flow=Post Login Flow
|
#post-broker-login-flow=Post Login Flow
|
||||||
sync-mode=Synchronisationsmodus
|
sync-mode=Synchronisationsmodus
|
||||||
sync-mode.tooltip=Standardsyncmodus für alle Mapper. Mögliche Werte sind: 'Legacy' um das alte Verhalten beizubehalten, 'Importieren' um den Nutzer einmalig zu importieren, 'Erzwingen' um den Nutzer immer zu importieren.
|
sync-mode.tooltip=Standardsyncmodus f\u00FCr alle Mapper. M\u00F6gliche Werte sind: 'Legacy' um das alte Verhalten beizubehalten, 'Importieren' um den Nutzer einmalig zu importieren, 'Erzwingen' um den Nutzer immer zu importieren.
|
||||||
sync-mode.inherit=Standard erben
|
sync-mode.inherit=Standard erben
|
||||||
sync-mode.legacy=Legacy
|
sync-mode.legacy=Legacy
|
||||||
sync-mode.import=Importieren
|
sync-mode.import=Importieren
|
||||||
sync-mode.force=Erzwingen
|
sync-mode.force=Erzwingen
|
||||||
sync-mode-override=Ãœberschriebene Synchronisation
|
sync-mode-override=\u00DCberschriebene Synchronisation
|
||||||
sync-mode-override.tooltip=Überschreibt den normalen Synchronisationsmodus des IDP für diesen Mapper. Were sind 'Legacy' um das alte Verhalten beizubehalten, 'Importieren' um den Nutzer einmalig zu importieren, 'Erzwingen' um den Nutzer immer zu updaten.
|
sync-mode-override.tooltip=\u00DCberschreibt den normalen Synchronisationsmodus des IDP f\u00FCr diesen Mapper. Were sind 'Legacy' um das alte Verhalten beizubehalten, 'Importieren' um den Nutzer einmalig zu importieren, 'Erzwingen' um den Nutzer immer zu updaten.
|
||||||
#redirect-uri=Redirect URI
|
#redirect-uri=Redirect URI
|
||||||
#redirect-uri.tooltip=The redirect uri to use when configuring the identity provider.
|
#redirect-uri.tooltip=The redirect uri to use when configuring the identity provider.
|
||||||
#alias=Alias
|
#alias=Alias
|
||||||
|
@ -706,16 +719,16 @@ group.assigned-roles.tooltip=Realm-Rollen die zur Gruppe zugeordnet sind
|
||||||
#group.effective-roles-client.tooltip=Role mappings for this client. Some roles here might be inherited from a mapped composite role.
|
#group.effective-roles-client.tooltip=Role mappings for this client. Some roles here might be inherited from a mapped composite role.
|
||||||
|
|
||||||
group.move.success=Gruppe verschoben.
|
group.move.success=Gruppe verschoben.
|
||||||
group.remove.confirm.title=Gruppe löschen
|
group.remove.confirm.title=Gruppe l\u00F6schen
|
||||||
group.remove.confirm.message=Sind Sie sicher, dass Sie die Gruppe \u201E{{name}}\u201C löschen möchten?
|
group.remove.confirm.message=Sind Sie sicher, dass Sie die Gruppe \u201E{{name}}\u201C l\u00F6schen m\u00F6chten?
|
||||||
group.remove.success=Die Gruppe wurde gelöscht.
|
group.remove.success=Die Gruppe wurde gel\u00F6scht.
|
||||||
group.fetch.fail=Fehler beim Laden: {{params}}
|
group.fetch.fail=Fehler beim Laden: {{params}}
|
||||||
group.create.success=Gruppe erstellt.
|
group.create.success=Gruppe erstellt.
|
||||||
group.edit.success=Die Änderungen wurde gespeichert.
|
group.edit.success=Die \u00C4nderungen wurde gespeichert.
|
||||||
group.roles.add.success=Rollenzuweisung hinzugefügt.
|
group.roles.add.success=Rollenzuweisung hinzugef\u00FCgt.
|
||||||
group.roles.remove.success=Rollenzuweisung entfernt.
|
group.roles.remove.success=Rollenzuweisung entfernt.
|
||||||
group.default.add.error=Bitte eine Gruppe auswählen.
|
group.default.add.error=Bitte eine Gruppe ausw\u00E4hlen.
|
||||||
group.default.add.success=Standardgruppe hinzugefügt.
|
group.default.add.success=Standardgruppe hinzugef\u00FCgt.
|
||||||
group.default.remove.success=Standardgruppe entfernt.
|
group.default.remove.success=Standardgruppe entfernt.
|
||||||
|
|
||||||
default-roles=Standardrollen
|
default-roles=Standardrollen
|
||||||
|
@ -729,49 +742,49 @@ user.effective-roles.tooltip=Alle Realm-Rollen-Zuweisungen. Einige Rollen hier k
|
||||||
#user.assigned-roles-client.tooltip=Role mappings for this client.
|
#user.assigned-roles-client.tooltip=Role mappings for this client.
|
||||||
#user.effective-roles-client.tooltip=Role mappings for this client. Some roles here might be inherited from a mapped composite role.
|
#user.effective-roles-client.tooltip=Role mappings for this client. Some roles here might be inherited from a mapped composite role.
|
||||||
|
|
||||||
user.roles.add.success=Rollenzuweisung hinzugefügt.
|
user.roles.add.success=Rollenzuweisung hinzugef\u00FCgt.
|
||||||
user.roles.remove.success=Rollenzuweisung entfernt.
|
user.roles.remove.success=Rollenzuweisung entfernt.
|
||||||
user.logout.all.success=Benutzer von allen Sitzungen abgemeldet.
|
user.logout.all.success=Benutzer von allen Sitzungen abgemeldet.
|
||||||
user.logout.session.success=Benutzer von Sitzung abgemeldet.
|
user.logout.session.success=Benutzer von Sitzung abgemeldet.
|
||||||
user.fedid.link.remove.confirm.title=Verknüpfung mit Identity Provider entfernen
|
user.fedid.link.remove.confirm.title=Verkn\u00FCpfung mit Identity Provider entfernen
|
||||||
user.fedid.link.remove.confirm.message=Sind Sie sicher, dass Sie die Verknüpfung mit dem Identity Provider \u201E{{name}}\u201C entfernen möchten?
|
user.fedid.link.remove.confirm.message=Sind Sie sicher, dass Sie die Verkn\u00FCpfung mit dem Identity Provider \u201E{{name}}\u201C entfernen m\u00F6chten?
|
||||||
user.fedid.link.remove.success=Verknüpfung mit Identity Provider entfernt.
|
user.fedid.link.remove.success=Verkn\u00FCpfung mit Identity Provider entfernt.
|
||||||
user.fedid.link.add.success=Verknüpfung mit Identity Provider angelegt.
|
user.fedid.link.add.success=Verkn\u00FCpfung mit Identity Provider angelegt.
|
||||||
user.consent.revoke.success=Einwilligung widerrufen.
|
user.consent.revoke.success=Einwilligung widerrufen.
|
||||||
user.consent.revoke.error=Einwilligung konnte nicht widerrufen werden.
|
user.consent.revoke.error=Einwilligung konnte nicht widerrufen werden.
|
||||||
user.unlock.success=Alle vorübergehend gesperrten Benutzer wurden entsperrt.
|
user.unlock.success=Alle vor\u00FCbergehend gesperrten Benutzer wurden entsperrt.
|
||||||
user.remove.confirm.title=Benutzer löschen
|
user.remove.confirm.title=Benutzer l\u00F6schen
|
||||||
user.remove.confirm.message=Sind Sie sicher, dass Sie den Benutzer \u201E{{name}}\u201C löschen möchten?
|
user.remove.confirm.message=Sind Sie sicher, dass Sie den Benutzer \u201E{{name}}\u201C l\u00F6schen m\u00F6chten?
|
||||||
user.remove.success=Der Benutzer wurde gelöscht.
|
user.remove.success=Der Benutzer wurde gel\u00F6scht.
|
||||||
user.remove.error=Der Benutzer konnte nicht gelöscht werden.
|
user.remove.error=Der Benutzer konnte nicht gel\u00F6scht werden.
|
||||||
user.create.success=Der Benutzer wurde angelegt.
|
user.create.success=Der Benutzer wurde angelegt.
|
||||||
user.edit.success=Die Änderungen wurden gespeichert.
|
user.edit.success=Die \u00C4nderungen wurden gespeichert.
|
||||||
user.credential.update.success=Die Zugangsdaten wurdern gespeichert.
|
user.credential.update.success=Die Zugangsdaten wurdern gespeichert.
|
||||||
user.credential.update.error=Beim Speichern der Zugangsdaten ist ein Fehler aufgetreten.
|
user.credential.update.error=Beim Speichern der Zugangsdaten ist ein Fehler aufgetreten.
|
||||||
user.credential.remove.confirm.title=Zugangsdaten löschen
|
user.credential.remove.confirm.title=Zugangsdaten l\u00F6schen
|
||||||
user.credential.remove.confirm.message=Sind Sie sicher, dass Sie die Zugangsdaten löschen löschen möchten?
|
user.credential.remove.confirm.message=Sind Sie sicher, dass Sie die Zugangsdaten l\u00F6schen m\u00F6chten?
|
||||||
user.credential.remove.success=Die Zugangsdaten wurden gelöscht.
|
user.credential.remove.success=Die Zugangsdaten wurden gel\u00F6scht.
|
||||||
user.credential.remove.error=Beim Löschen der Zugangsdaten ist ein Fehler aufgetreten.
|
user.credential.remove.error=Beim L\u00F6schen der Zugangsdaten ist ein Fehler aufgetreten.
|
||||||
user.credential.move-top.error=Beim Verschieben der Zugangsdaten ist ein Fehler aufgetreten.
|
user.credential.move-top.error=Beim Verschieben der Zugangsdaten ist ein Fehler aufgetreten.
|
||||||
user.credential.move-up.error=Beim Verschieben der Zugangsdaten ist ein Fehler aufgetreten.
|
user.credential.move-up.error=Beim Verschieben der Zugangsdaten ist ein Fehler aufgetreten.
|
||||||
user.credential.move-down.error=Beim Verschieben der Zugangsdaten ist ein Fehler aufgetreten.
|
user.credential.move-down.error=Beim Verschieben der Zugangsdaten ist ein Fehler aufgetreten.
|
||||||
user.credential.fetch.error=Beim Laden der Zugangsdaten ist ein Fehler aufgetreten.
|
user.credential.fetch.error=Beim Laden der Zugangsdaten ist ein Fehler aufgetreten.
|
||||||
#user.credential.storage.fetch.error=Error while loading user storage credentials. See console for more information.
|
#user.credential.storage.fetch.error=Error while loading user storage credentials. See console for more information.
|
||||||
user.password.error.not-matching=Die Passwörter stimmen nicht überein.
|
user.password.error.not-matching=Die Passw\u00F6rter stimmen nicht \u00FCberein.
|
||||||
user.password.reset.confirm.title=Passwort zurücksetzen
|
user.password.reset.confirm.title=Passwort zur\u00FCcksetzen
|
||||||
user.password.reset.confirm.message=Sind Sie sicher, dass Sie das Passwort für diesen Benutzer zurücksetzen möchten?
|
user.password.reset.confirm.message=Sind Sie sicher, dass Sie das Passwort f\u00FCr diesen Benutzer zur\u00FCcksetzen m\u00F6chten?
|
||||||
user.password.reset.success=Das Passwort wurde zurückgesetzt.
|
user.password.reset.success=Das Passwort wurde zur\u00FCckgesetzt.
|
||||||
user.password.set.confirm.title=Passwort setzen
|
user.password.set.confirm.title=Passwort setzen
|
||||||
user.password.set.confirm.message=Sind Sie sicher, dass Sie ein Passwort für diesen Benutzer setzen möchten?
|
user.password.set.confirm.message=Sind Sie sicher, dass Sie ein Passwort f\u00FCr diesen Benutzer setzen m\u00F6chten?
|
||||||
user.password.set.success=Das Passwort wurde gesetzt.
|
user.password.set.success=Das Passwort wurde gesetzt.
|
||||||
user.credential.disable.confirm.title=Zugangsdaten deaktivieren
|
user.credential.disable.confirm.title=Zugangsdaten deaktivieren
|
||||||
user.credential.disable.confirm.message=Sind Sie sicher, dass Sie diese Zugangsdaten deaktivieren möchten?
|
user.credential.disable.confirm.message=Sind Sie sicher, dass Sie diese Zugangsdaten deaktivieren m\u00F6chten?
|
||||||
user.credential.disable.confirm.success=Zugangsdaten deaktiviert.
|
user.credential.disable.confirm.success=Zugangsdaten deaktiviert.
|
||||||
user.credential.disable.confirm.error=Fehler beim Deaktivieren der Zugangsdaten
|
user.credential.disable.confirm.error=Fehler beim Deaktivieren der Zugangsdaten
|
||||||
user.actions-email.send.pending-changes.title=E-Mail kann nicht gesendet werden.
|
user.actions-email.send.pending-changes.title=E-Mail kann nicht gesendet werden.
|
||||||
user.actions-email.send.pending-changes.message=Bitte speichern Sie Ihre Änderungen bevor Sie die E-Mail senden.
|
user.actions-email.send.pending-changes.message=Bitte speichern Sie Ihre \u00C4nderungen bevor Sie die E-Mail senden.
|
||||||
user.actions-email.send.confirm.title=E-Mail senden
|
user.actions-email.send.confirm.title=E-Mail senden
|
||||||
user.actions-email.send.confirm.message=Sind Sie sicher, dass Sie die E-Mail an den Benutzer senden möchten?
|
user.actions-email.send.confirm.message=Sind Sie sicher, dass Sie die E-Mail an den Benutzer senden m\u00F6chten?
|
||||||
user.actions-email.send.confirm.success=E-Mail an Benutzer gesendet.
|
user.actions-email.send.confirm.success=E-Mail an Benutzer gesendet.
|
||||||
user.actions-email.send.confirm.error=Fehler beim Senden der E-Mail
|
user.actions-email.send.confirm.error=Fehler beim Senden der E-Mail
|
||||||
#user.storage.remove.confirm.title=Delete User storage provider
|
#user.storage.remove.confirm.title=Delete User storage provider
|
||||||
|
@ -787,10 +800,10 @@ user.actions-email.send.confirm.error=Fehler beim Senden der E-Mail
|
||||||
#user.storage.unlink.error=Error during unlink
|
#user.storage.unlink.error=Error during unlink
|
||||||
user.groups.fetch.all.error=Fehler beim Laden alle Gruppen: {{params}}
|
user.groups.fetch.all.error=Fehler beim Laden alle Gruppen: {{params}}
|
||||||
user.groups.fetch.error=Fehler beim Laden: {{params}}
|
user.groups.fetch.error=Fehler beim Laden: {{params}}
|
||||||
user.groups.join.error.no-group-selected=Bitte wählen Sie eine Gruppe aus!
|
user.groups.join.error.no-group-selected=Bitte w\u00E4hlen Sie eine Gruppe aus!
|
||||||
user.groups.join.error.already-added=Benutzer gehört der Gruppe bereits an.
|
user.groups.join.error.already-added=Benutzer geh\u00F6rt der Gruppe bereits an.
|
||||||
user.groups.join.success=Zur Gruppe hinzugefügt.
|
user.groups.join.success=Zur Gruppe hinzugef\u00FCgt.
|
||||||
user.groups.leave.error.no-group-selected=Bitte wählen Sie eine Gruppe aus!
|
user.groups.leave.error.no-group-selected=Bitte w\u00E4hlen Sie eine Gruppe aus!
|
||||||
user.groups.leave.success=Aus Gruppe entfernt.
|
user.groups.leave.success=Aus Gruppe entfernt.
|
||||||
|
|
||||||
#default.available-roles.tooltip=Realm level roles that can be assigned.
|
#default.available-roles.tooltip=Realm level roles that can be assigned.
|
||||||
|
@ -1608,8 +1621,8 @@ notifications.success.header=Erfolg!
|
||||||
notifications.error.header=Fehler!
|
notifications.error.header=Fehler!
|
||||||
notifications.warn.header=Warnung!
|
notifications.warn.header=Warnung!
|
||||||
|
|
||||||
dialogs.delete.title={{type}} löschen
|
dialogs.delete.title={{type}} l\u00F6schen
|
||||||
dialogs.delete.message=Sind Sie sicher, dass Sie {{type}} {{name}} löschen möchten?
|
dialogs.delete.message=Sind Sie sicher, dass Sie {{type}} {{name}} l\u00F6schen m\u00F6chten?
|
||||||
dialogs.delete.confirm=Löschen
|
dialogs.delete.confirm=L\u00F6schen
|
||||||
dialogs.cancel=Abbrechen
|
dialogs.cancel=Abbrechen
|
||||||
dialogs.ok=OK
|
dialogs.ok=OK
|
||||||
|
|
|
@ -62,6 +62,18 @@ i18n-enabled=Internacionalizaci\u00F3n activa
|
||||||
supported-locales=Idiomas soportados
|
supported-locales=Idiomas soportados
|
||||||
supported-locales.placeholder=Indica el idioma y pulsa Intro
|
supported-locales.placeholder=Indica el idioma y pulsa Intro
|
||||||
default-locale=Idioma por defecto
|
default-locale=Idioma por defecto
|
||||||
|
#localization-upload-file=Upload localization JSON file
|
||||||
|
#missing-locale=Missing locale.
|
||||||
|
#missing-file=Missing file. Please select a file to upload.
|
||||||
|
#localization-file.upload.success=The localization data has been loaded from file.
|
||||||
|
#localization-file.upload.error=The file can not be uploaded. Please verify the file.
|
||||||
|
#localization-show=Show realm specific localizations
|
||||||
|
#no-localizations-configured=No realm specific localizations configured
|
||||||
|
#add-localization-text=Add localization text
|
||||||
|
#locale.create.success=The Locale has been created.
|
||||||
|
#localization-text.create.success=The localization text has been created.
|
||||||
|
#localization-text.update.success=The localization text has been updated.
|
||||||
|
#localization-text.remove.success=The localization text has been deleted.
|
||||||
realm-cache-enabled=Cach\u00E9 de dominio habilitada
|
realm-cache-enabled=Cach\u00E9 de dominio habilitada
|
||||||
realm-cache-enabled.tooltip=Activar/desactivar la cach\u00E9 para el dominio, cliente y datos de roles.
|
realm-cache-enabled.tooltip=Activar/desactivar la cach\u00E9 para el dominio, cliente y datos de roles.
|
||||||
user-cache-enabled=Cach\u00E9 de usuario habilitada
|
user-cache-enabled=Cach\u00E9 de usuario habilitada
|
||||||
|
@ -107,6 +119,7 @@ realm-tab-login=Inicio de sesi\u00F3n
|
||||||
realm-tab-keys=Claves
|
realm-tab-keys=Claves
|
||||||
realm-tab-email=Email
|
realm-tab-email=Email
|
||||||
realm-tab-themes=Temas
|
realm-tab-themes=Temas
|
||||||
|
#realm-tab-localization=Localization
|
||||||
realm-tab-cache=Cach\u00E9
|
realm-tab-cache=Cach\u00E9
|
||||||
realm-tab-tokens=Tokens
|
realm-tab-tokens=Tokens
|
||||||
realm-tab-security-defenses=Defensas de seguridad
|
realm-tab-security-defenses=Defensas de seguridad
|
||||||
|
|
|
@ -82,6 +82,18 @@ i18n-enabled=Internationalisation activ\u00e9e
|
||||||
supported-locales=Locales support\u00e9es
|
supported-locales=Locales support\u00e9es
|
||||||
supported-locales.placeholder=Entrez la locale et validez
|
supported-locales.placeholder=Entrez la locale et validez
|
||||||
default-locale=Locale par d\u00e9faut
|
default-locale=Locale par d\u00e9faut
|
||||||
|
#localization-upload-file=Upload localization JSON file
|
||||||
|
#missing-locale=Missing locale.
|
||||||
|
#missing-file=Missing file. Please select a file to upload.
|
||||||
|
#localization-file.upload.success=The localization data has been loaded from file.
|
||||||
|
#localization-file.upload.error=The file can not be uploaded. Please verify the file.
|
||||||
|
#localization-show=Show realm specific localizations
|
||||||
|
#no-localizations-configured=No realm specific localizations configured
|
||||||
|
#add-localization-text=Add localization text
|
||||||
|
#locale.create.success=The Locale has been created.
|
||||||
|
#localization-text.create.success=The localization text has been created.
|
||||||
|
#localization-text.update.success=The localization text has been updated.
|
||||||
|
#localization-text.remove.success=The localization text has been deleted.
|
||||||
realm-cache-enabled=Cache du domaine activ\u00e9
|
realm-cache-enabled=Cache du domaine activ\u00e9
|
||||||
realm-cache-enabled.tooltip=Activer/D\u00e9sactiver le cache pour le domaine, client et donn\u00e9es.
|
realm-cache-enabled.tooltip=Activer/D\u00e9sactiver le cache pour le domaine, client et donn\u00e9es.
|
||||||
user-cache-enabled=Cache utilisateur activ\u00e9
|
user-cache-enabled=Cache utilisateur activ\u00e9
|
||||||
|
@ -123,6 +135,7 @@ realm-tab-login=Connexion
|
||||||
realm-tab-keys=Clefs
|
realm-tab-keys=Clefs
|
||||||
realm-tab-email=Courriels
|
realm-tab-email=Courriels
|
||||||
realm-tab-themes=Th\u00e8mes
|
realm-tab-themes=Th\u00e8mes
|
||||||
|
#realm-tab-localization=Localization
|
||||||
realm-tab-cache=Cache
|
realm-tab-cache=Cache
|
||||||
realm-tab-tokens=Jetons
|
realm-tab-tokens=Jetons
|
||||||
realm-tab-security-defenses=Mesures de s\u00e9curit\u00e9
|
realm-tab-security-defenses=Mesures de s\u00e9curit\u00e9
|
||||||
|
|
|
@ -93,6 +93,18 @@ i18n-enabled=国際化の有効
|
||||||
supported-locales=サポートされるロケール
|
supported-locales=サポートされるロケール
|
||||||
supported-locales.placeholder=ロケールを入力し、Enterキーを押してください
|
supported-locales.placeholder=ロケールを入力し、Enterキーを押してください
|
||||||
default-locale=デフォルト・ロケール
|
default-locale=デフォルト・ロケール
|
||||||
|
#localization-upload-file=Upload localization JSON file
|
||||||
|
#missing-locale=Missing locale.
|
||||||
|
#missing-file=Missing file. Please select a file to upload.
|
||||||
|
#localization-file.upload.success=The localization data has been loaded from file.
|
||||||
|
#localization-file.upload.error=The file can not be uploaded. Please verify the file.
|
||||||
|
#localization-show=Show realm specific localizations
|
||||||
|
#no-localizations-configured=No realm specific localizations configured
|
||||||
|
#add-localization-text=Add localization text
|
||||||
|
#locale.create.success=The Locale has been created.
|
||||||
|
#localization-text.create.success=The localization text has been created.
|
||||||
|
#localization-text.update.success=The localization text has been updated.
|
||||||
|
#localization-text.remove.success=The localization text has been deleted.
|
||||||
realm-cache-clear=レルムキャッシュ
|
realm-cache-clear=レルムキャッシュ
|
||||||
realm-cache-clear.tooltip=レルムキャッシュからすべてのエントリーをクリアする(これにより、すべてのレルムのエントリーがクリアされます)。
|
realm-cache-clear.tooltip=レルムキャッシュからすべてのエントリーをクリアする(これにより、すべてのレルムのエントリーがクリアされます)。
|
||||||
user-cache-clear=ユーザー・キャッシュ
|
user-cache-clear=ユーザー・キャッシュ
|
||||||
|
@ -192,6 +204,7 @@ realm-tab-login=ログイン
|
||||||
realm-tab-keys=鍵
|
realm-tab-keys=鍵
|
||||||
realm-tab-email=Eメール
|
realm-tab-email=Eメール
|
||||||
realm-tab-themes=テーマ
|
realm-tab-themes=テーマ
|
||||||
|
#realm-tab-localization=Localization
|
||||||
realm-tab-cache=キャッシュ
|
realm-tab-cache=キャッシュ
|
||||||
realm-tab-tokens=トークン
|
realm-tab-tokens=トークン
|
||||||
realm-tab-client-registration=クライアント登録
|
realm-tab-client-registration=クライアント登録
|
||||||
|
|
|
@ -69,6 +69,18 @@ i18n-enabled=Daugiakalbystės palaikymas
|
||||||
supported-locales=Palaikomos kalbos
|
supported-locales=Palaikomos kalbos
|
||||||
supported-locales.placeholder=Pasirinkite arba įrašykite kalbos pavadinimą
|
supported-locales.placeholder=Pasirinkite arba įrašykite kalbos pavadinimą
|
||||||
default-locale=Numatyta kalba
|
default-locale=Numatyta kalba
|
||||||
|
#localization-upload-file=Upload localization JSON file
|
||||||
|
#missing-locale=Missing locale.
|
||||||
|
#missing-file=Missing file. Please select a file to upload.
|
||||||
|
#localization-file.upload.success=The localization data has been loaded from file.
|
||||||
|
#localization-file.upload.error=The file can not be uploaded. Please verify the file.
|
||||||
|
#localization-show=Show realm specific localizations
|
||||||
|
#no-localizations-configured=No realm specific localizations configured
|
||||||
|
#add-localization-text=Add localization text
|
||||||
|
#locale.create.success=The Locale has been created.
|
||||||
|
#localization-text.create.success=The localization text has been created.
|
||||||
|
#localization-text.update.success=The localization text has been updated.
|
||||||
|
#localization-text.remove.success=The localization text has been deleted.
|
||||||
realm-cache-clear=Srities podėlis
|
realm-cache-clear=Srities podėlis
|
||||||
realm-cache-clear.tooltip=Iš visų sričių pašalinama visa podėlyje (cache) esanti informacija
|
realm-cache-clear.tooltip=Iš visų sričių pašalinama visa podėlyje (cache) esanti informacija
|
||||||
user-cache-clear=Naudotojų podėlis
|
user-cache-clear=Naudotojų podėlis
|
||||||
|
@ -119,6 +131,7 @@ realm-tab-login=Prisijungimas
|
||||||
realm-tab-keys=Raktai
|
realm-tab-keys=Raktai
|
||||||
realm-tab-email=El. paštas
|
realm-tab-email=El. paštas
|
||||||
realm-tab-themes=Temos
|
realm-tab-themes=Temos
|
||||||
|
#realm-tab-localization=Localization
|
||||||
realm-tab-cache=Podėlis
|
realm-tab-cache=Podėlis
|
||||||
realm-tab-tokens=Raktai
|
realm-tab-tokens=Raktai
|
||||||
realm-tab-client-registration=Klientų registracija
|
realm-tab-client-registration=Klientų registracija
|
||||||
|
|
|
@ -68,6 +68,18 @@ i18n-enabled=Internasjonalisering aktivert
|
||||||
supported-locales=St\u00F8ttede lokaliteter
|
supported-locales=St\u00F8ttede lokaliteter
|
||||||
supported-locales.placeholder=Skriv inn en lokalitet og klikk enter
|
supported-locales.placeholder=Skriv inn en lokalitet og klikk enter
|
||||||
default-locale=Standard lokalitet
|
default-locale=Standard lokalitet
|
||||||
|
#localization-upload-file=Upload localization JSON file
|
||||||
|
#missing-locale=Missing locale.
|
||||||
|
#missing-file=Missing file. Please select a file to upload.
|
||||||
|
#localization-file.upload.success=The localization data has been loaded from file.
|
||||||
|
#localization-file.upload.error=The file can not be uploaded. Please verify the file.
|
||||||
|
#localization-show=Show realm specific localizations
|
||||||
|
#no-localizations-configured=No realm specific localizations configured
|
||||||
|
#add-localization-text=Add localization text
|
||||||
|
#locale.create.success=The Locale has been created.
|
||||||
|
#localization-text.create.success=The localization text has been created.
|
||||||
|
#localization-text.update.success=The localization text has been updated.
|
||||||
|
#localization-text.remove.success=The localization text has been deleted.
|
||||||
realm-cache-clear=Cache for sikkerhetsdomenet
|
realm-cache-clear=Cache for sikkerhetsdomenet
|
||||||
realm-cache-clear.tooltip=T\u00F8m sikkerhetsdomenecache (Dette vil fjerne oppf\u00F8ringer for alle sikkerhetsdomener)
|
realm-cache-clear.tooltip=T\u00F8m sikkerhetsdomenecache (Dette vil fjerne oppf\u00F8ringer for alle sikkerhetsdomener)
|
||||||
user-cache-clear=Brukercache
|
user-cache-clear=Brukercache
|
||||||
|
@ -120,6 +132,7 @@ realm-tab-login=Innlogging
|
||||||
realm-tab-keys=N\u00F8kler
|
realm-tab-keys=N\u00F8kler
|
||||||
realm-tab-email=E-post
|
realm-tab-email=E-post
|
||||||
realm-tab-themes=Tema
|
realm-tab-themes=Tema
|
||||||
|
#realm-tab-localization=Localization
|
||||||
realm-tab-cache=Cache
|
realm-tab-cache=Cache
|
||||||
realm-tab-tokens=Tokens
|
realm-tab-tokens=Tokens
|
||||||
realm-tab-client-initial-access=F\u00F8rste access token
|
realm-tab-client-initial-access=F\u00F8rste access token
|
||||||
|
|
|
@ -69,6 +69,18 @@ i18n-enabled=Habilitar internacionalização
|
||||||
supported-locales=Locais disponíveis
|
supported-locales=Locais disponíveis
|
||||||
supported-locales.placeholder=Digite um local e pressione Enter
|
supported-locales.placeholder=Digite um local e pressione Enter
|
||||||
default-locale=Local padrão
|
default-locale=Local padrão
|
||||||
|
#localization-upload-file=Upload localization JSON file
|
||||||
|
#missing-locale=Missing locale.
|
||||||
|
#missing-file=Missing file. Please select a file to upload.
|
||||||
|
#localization-file.upload.success=The localization data has been loaded from file.
|
||||||
|
#localization-file.upload.error=The file can not be uploaded. Please verify the file.
|
||||||
|
#localization-show=Show realm specific localizations
|
||||||
|
#no-localizations-configured=No realm specific localizations configured
|
||||||
|
#add-localization-text=Add localization text
|
||||||
|
#locale.create.success=The Locale has been created.
|
||||||
|
#localization-text.create.success=The localization text has been created.
|
||||||
|
#localization-text.update.success=The localization text has been updated.
|
||||||
|
#localization-text.remove.success=The localization text has been deleted.
|
||||||
realm-cache-clear=Realm Cache
|
realm-cache-clear=Realm Cache
|
||||||
realm-cache-clear.tooltip=Remove todas as entradas do cache de realm (isto irá remover as entradas para todos os realms)
|
realm-cache-clear.tooltip=Remove todas as entradas do cache de realm (isto irá remover as entradas para todos os realms)
|
||||||
user-cache-clear=Cache de usuário
|
user-cache-clear=Cache de usuário
|
||||||
|
@ -119,6 +131,7 @@ realm-tab-login=Login
|
||||||
realm-tab-keys=Chaves
|
realm-tab-keys=Chaves
|
||||||
realm-tab-email=E-mail
|
realm-tab-email=E-mail
|
||||||
realm-tab-themes=Temas
|
realm-tab-themes=Temas
|
||||||
|
#realm-tab-localization=Localization
|
||||||
realm-tab-cache=Cache
|
realm-tab-cache=Cache
|
||||||
realm-tab-tokens=Tokens
|
realm-tab-tokens=Tokens
|
||||||
realm-tab-client-initial-access=Tokens de Acesso inicial
|
realm-tab-client-initial-access=Tokens de Acesso inicial
|
||||||
|
|
|
@ -75,6 +75,18 @@ i18n-enabled=Интернационализация
|
||||||
supported-locales=Поддерживаемые языки
|
supported-locales=Поддерживаемые языки
|
||||||
supported-locales.placeholder=Выберите язык и нажмите Enter
|
supported-locales.placeholder=Выберите язык и нажмите Enter
|
||||||
default-locale=Язык по умолчанию
|
default-locale=Язык по умолчанию
|
||||||
|
#localization-upload-file=Upload localization JSON file
|
||||||
|
#missing-locale=Missing locale.
|
||||||
|
#missing-file=Missing file. Please select a file to upload.
|
||||||
|
#localization-file.upload.success=The localization data has been loaded from file.
|
||||||
|
#localization-file.upload.error=The file can not be uploaded. Please verify the file.
|
||||||
|
#localization-show=Show realm specific localizations
|
||||||
|
#no-localizations-configured=No realm specific localizations configured
|
||||||
|
#add-localization-text=Add localization text
|
||||||
|
#locale.create.success=The Locale has been created.
|
||||||
|
#localization-text.create.success=The localization text has been created.
|
||||||
|
#localization-text.update.success=The localization text has been updated.
|
||||||
|
#localization-text.remove.success=The localization text has been deleted.
|
||||||
realm-cache-clear=Кэш Realm
|
realm-cache-clear=Кэш Realm
|
||||||
realm-cache-clear.tooltip=Удалить все записи в кэше realm (удалит все записи для всех realm)
|
realm-cache-clear.tooltip=Удалить все записи в кэше realm (удалит все записи для всех realm)
|
||||||
user-cache-clear=Кэш пользователей
|
user-cache-clear=Кэш пользователей
|
||||||
|
@ -127,6 +139,7 @@ realm-tab-login=Вход
|
||||||
realm-tab-keys=Ключи
|
realm-tab-keys=Ключи
|
||||||
realm-tab-email=E-mail
|
realm-tab-email=E-mail
|
||||||
realm-tab-themes=Темы
|
realm-tab-themes=Темы
|
||||||
|
#realm-tab-localization=Localization
|
||||||
realm-tab-cache=Кэш
|
realm-tab-cache=Кэш
|
||||||
realm-tab-tokens=Токены
|
realm-tab-tokens=Токены
|
||||||
realm-tab-client-initial-access=Первоначальные токены доступа
|
realm-tab-client-initial-access=Первоначальные токены доступа
|
||||||
|
|
|
@ -69,6 +69,18 @@ i18n-enabled=启用国际化
|
||||||
supported-locales=支持的语言
|
supported-locales=支持的语言
|
||||||
supported-locales.placeholder=输入一个locale并按回车
|
supported-locales.placeholder=输入一个locale并按回车
|
||||||
default-locale=默认语言
|
default-locale=默认语言
|
||||||
|
#localization-upload-file=Upload localization JSON file
|
||||||
|
#missing-locale=Missing locale.
|
||||||
|
#missing-file=Missing file. Please select a file to upload.
|
||||||
|
#localization-file.upload.success=The localization data has been loaded from file.
|
||||||
|
#localization-file.upload.error=The file can not be uploaded. Please verify the file.
|
||||||
|
#localization-show=Show realm specific localizations
|
||||||
|
#no-localizations-configured=No realm specific localizations configured
|
||||||
|
#add-localization-text=Add localization text
|
||||||
|
#locale.create.success=The Locale has been created.
|
||||||
|
#localization-text.create.success=The localization text has been created.
|
||||||
|
#localization-text.update.success=The localization text has been updated.
|
||||||
|
#localization-text.remove.success=The localization text has been deleted.
|
||||||
realm-cache-clear=域缓存
|
realm-cache-clear=域缓存
|
||||||
realm-cache-clear.tooltip=从域缓存中清理所有条目(这会清理所有域的条目)
|
realm-cache-clear.tooltip=从域缓存中清理所有条目(这会清理所有域的条目)
|
||||||
user-cache-clear=用户缓存
|
user-cache-clear=用户缓存
|
||||||
|
@ -119,6 +131,7 @@ realm-tab-login=登录
|
||||||
realm-tab-keys=秘钥
|
realm-tab-keys=秘钥
|
||||||
realm-tab-email=Email
|
realm-tab-email=Email
|
||||||
realm-tab-themes=主题
|
realm-tab-themes=主题
|
||||||
|
#realm-tab-localization=Localization
|
||||||
realm-tab-cache=缓存
|
realm-tab-cache=缓存
|
||||||
realm-tab-tokens=Tokens
|
realm-tab-tokens=Tokens
|
||||||
realm-tab-client-registration=客户端注册
|
realm-tab-client-registration=客户端注册
|
||||||
|
|
|
@ -96,6 +96,17 @@ i18n-enabled=Internationalization Enabled
|
||||||
supported-locales=Supported Locales
|
supported-locales=Supported Locales
|
||||||
supported-locales.placeholder=Type a locale and enter
|
supported-locales.placeholder=Type a locale and enter
|
||||||
default-locale=Default Locale
|
default-locale=Default Locale
|
||||||
|
localization-upload-file=Upload localization JSON file
|
||||||
|
missing-locale=Missing locale.
|
||||||
|
missing-file=Missing file. Please select a file to upload.
|
||||||
|
localization-file.upload.success=The localization data has been loaded from file.
|
||||||
|
localization-file.upload.error=The file can not be uploaded. Please verify the file.
|
||||||
|
localization-show=Show realm specific localizations
|
||||||
|
no-localizations-configured=No realm specific localizations configured
|
||||||
|
add-localization-text=Add localization text
|
||||||
|
localization-text.create.success=The localization text has been created.
|
||||||
|
localization-text.update.success=The localization text has been updated.
|
||||||
|
localization-text.remove.success=The localization text has been deleted.
|
||||||
realm-cache-clear=Realm Cache
|
realm-cache-clear=Realm Cache
|
||||||
realm-cache-clear.tooltip=Clears all entries from the realm cache (this will clear entries for all realms)
|
realm-cache-clear.tooltip=Clears all entries from the realm cache (this will clear entries for all realms)
|
||||||
user-cache-clear=User Cache
|
user-cache-clear=User Cache
|
||||||
|
@ -199,6 +210,7 @@ realm-tab-login=Login
|
||||||
realm-tab-keys=Keys
|
realm-tab-keys=Keys
|
||||||
realm-tab-email=Email
|
realm-tab-email=Email
|
||||||
realm-tab-themes=Themes
|
realm-tab-themes=Themes
|
||||||
|
realm-tab-localization=Localization
|
||||||
realm-tab-cache=Cache
|
realm-tab-cache=Cache
|
||||||
realm-tab-tokens=Tokens
|
realm-tab-tokens=Tokens
|
||||||
realm-tab-client-registration=Client Registration
|
realm-tab-client-registration=Client Registration
|
||||||
|
|
|
@ -7,6 +7,8 @@ var locale = 'en';
|
||||||
var module = angular.module('keycloak', [ 'keycloak.services', 'keycloak.loaders', 'ui.bootstrap', 'ui.select2', 'angularFileUpload', 'angularTreeview', 'pascalprecht.translate', 'ngCookies', 'ngSanitize', 'ui.ace']);
|
var module = angular.module('keycloak', [ 'keycloak.services', 'keycloak.loaders', 'ui.bootstrap', 'ui.select2', 'angularFileUpload', 'angularTreeview', 'pascalprecht.translate', 'ngCookies', 'ngSanitize', 'ui.ace']);
|
||||||
var resourceRequests = 0;
|
var resourceRequests = 0;
|
||||||
var loadingTimer = -1;
|
var loadingTimer = -1;
|
||||||
|
var translateProvider = null;
|
||||||
|
var currentRealm = null;
|
||||||
|
|
||||||
angular.element(document).ready(function () {
|
angular.element(document).ready(function () {
|
||||||
var keycloakAuth = new Keycloak(consoleBaseUrl + 'config');
|
var keycloakAuth = new Keycloak(consoleBaseUrl + 'config');
|
||||||
|
@ -146,6 +148,7 @@ module.factory('authInterceptor', function($q, Auth) {
|
||||||
});
|
});
|
||||||
|
|
||||||
module.config(['$translateProvider', function($translateProvider) {
|
module.config(['$translateProvider', function($translateProvider) {
|
||||||
|
translateProvider = $translateProvider;
|
||||||
$translateProvider.useSanitizeValueStrategy('sanitizeParameters');
|
$translateProvider.useSanitizeValueStrategy('sanitizeParameters');
|
||||||
$translateProvider.preferredLanguage(locale);
|
$translateProvider.preferredLanguage(locale);
|
||||||
$translateProvider.translations(locale, resourceBundle);
|
$translateProvider.translations(locale, resourceBundle);
|
||||||
|
@ -178,6 +181,33 @@ module.config([ '$routeProvider', function($routeProvider) {
|
||||||
},
|
},
|
||||||
controller : 'RealmDetailCtrl'
|
controller : 'RealmDetailCtrl'
|
||||||
})
|
})
|
||||||
|
.when('/realms/:realm/localization', {
|
||||||
|
templateUrl : resourceUrl + '/partials/realm-localization.html',
|
||||||
|
resolve : {
|
||||||
|
realm : function(RealmLoader) {
|
||||||
|
return RealmLoader();
|
||||||
|
},
|
||||||
|
serverInfo : function(ServerInfoLoader) {
|
||||||
|
return ServerInfoLoader();
|
||||||
|
},
|
||||||
|
realmSpecificLocales : function(RealmSpecificLocalesLoader) {
|
||||||
|
return RealmSpecificLocalesLoader();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
controller : 'RealmLocalizationCtrl'
|
||||||
|
})
|
||||||
|
.when('/realms/:realm/localization/upload', {
|
||||||
|
templateUrl : resourceUrl + '/partials/realm-localization-upload.html',
|
||||||
|
resolve : {
|
||||||
|
realm : function(RealmLoader) {
|
||||||
|
return RealmLoader();
|
||||||
|
},
|
||||||
|
serverInfo : function(ServerInfoLoader) {
|
||||||
|
return ServerInfoLoader();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
controller : 'RealmLocalizationUploadCtrl'
|
||||||
|
})
|
||||||
.when('/realms/:realm/login-settings', {
|
.when('/realms/:realm/login-settings', {
|
||||||
templateUrl : resourceUrl + '/partials/realm-login-settings.html',
|
templateUrl : resourceUrl + '/partials/realm-login-settings.html',
|
||||||
resolve : {
|
resolve : {
|
||||||
|
@ -2085,6 +2115,42 @@ module.config([ '$routeProvider', function($routeProvider) {
|
||||||
},
|
},
|
||||||
controller : 'AuthenticationConfigCreateCtrl'
|
controller : 'AuthenticationConfigCreateCtrl'
|
||||||
})
|
})
|
||||||
|
.when('/create/localization/:realm/:locale', {
|
||||||
|
templateUrl : resourceUrl + '/partials/realm-localization-detail.html',
|
||||||
|
resolve : {
|
||||||
|
realm : function(RealmLoader) {
|
||||||
|
return RealmLoader();
|
||||||
|
},
|
||||||
|
locale: function($route) {
|
||||||
|
return $route.current.params.locale;
|
||||||
|
},
|
||||||
|
key: function() {
|
||||||
|
return null
|
||||||
|
},
|
||||||
|
localizationText : function() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
controller : 'RealmLocalizationDetailCtrl'
|
||||||
|
})
|
||||||
|
.when('/realms/:realm/localization/:locale/:key', {
|
||||||
|
templateUrl : resourceUrl + '/partials/realm-localization-detail.html',
|
||||||
|
resolve : {
|
||||||
|
realm : function(RealmLoader) {
|
||||||
|
return RealmLoader();
|
||||||
|
},
|
||||||
|
locale: function($route) {
|
||||||
|
return $route.current.params.locale;
|
||||||
|
},
|
||||||
|
key: function($route) {
|
||||||
|
return $route.current.params.key;
|
||||||
|
},
|
||||||
|
localizationText : function(RealmSpecificlocalizationTextLoader) {
|
||||||
|
return RealmSpecificlocalizationTextLoader();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
controller : 'RealmLocalizationDetailCtrl'
|
||||||
|
})
|
||||||
.when('/server-info', {
|
.when('/server-info', {
|
||||||
templateUrl : resourceUrl + '/partials/server-info.html',
|
templateUrl : resourceUrl + '/partials/server-info.html',
|
||||||
resolve : {
|
resolve : {
|
||||||
|
|
|
@ -83,7 +83,7 @@ function getAccessObject(Auth, Current) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
module.controller('GlobalCtrl', function($scope, $http, Auth, Current, $location, Notifications, ServerInfo) {
|
module.controller('GlobalCtrl', function($scope, $http, Auth, Current, $location, Notifications, ServerInfo, RealmSpecificLocalizationTexts) {
|
||||||
$scope.authUrl = authUrl;
|
$scope.authUrl = authUrl;
|
||||||
$scope.resourceUrl = resourceUrl;
|
$scope.resourceUrl = resourceUrl;
|
||||||
$scope.auth = Auth;
|
$scope.auth = Auth;
|
||||||
|
@ -97,6 +97,18 @@ module.controller('GlobalCtrl', function($scope, $http, Auth, Current, $location
|
||||||
$scope.fragment = $location.path();
|
$scope.fragment = $location.path();
|
||||||
$scope.path = $location.path().substring(1).split("/");
|
$scope.path = $location.path().substring(1).split("/");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$scope.$watch(function() {
|
||||||
|
return Current.realm;
|
||||||
|
}, function() {
|
||||||
|
if(Current.realm !== null && currentRealm !== Current.realm.id) {
|
||||||
|
currentRealm = Current.realm.id;
|
||||||
|
translateProvider.translations(locale, resourceBundle);
|
||||||
|
RealmSpecificLocalizationTexts.get({id: currentRealm, locale: locale}, function (localizationTexts) {
|
||||||
|
translateProvider.translations(locale, localizationTexts.toJSON());
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
module.controller('HomeCtrl', function(Realm, Auth, Current, $location) {
|
module.controller('HomeCtrl', function(Realm, Auth, Current, $location) {
|
||||||
|
@ -499,6 +511,131 @@ module.controller('RealmThemeCtrl', function($scope, Current, Realm, realm, serv
|
||||||
$scope.$watch('realm.internationalizationEnabled', updateSupported);
|
$scope.$watch('realm.internationalizationEnabled', updateSupported);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
module.controller('RealmLocalizationCtrl', function($scope, Current, $location, Realm, realm, serverInfo, Notifications, RealmSpecificLocales, realmSpecificLocales, RealmSpecificLocalizationTexts, RealmSpecificLocalizationText, Dialog, $translate){
|
||||||
|
$scope.realm = realm;
|
||||||
|
$scope.realmSpecificLocales = realmSpecificLocales;
|
||||||
|
$scope.newLocale = null;
|
||||||
|
$scope.selectedRealmSpecificLocales = null;
|
||||||
|
$scope.localizationTexts = null;
|
||||||
|
|
||||||
|
$scope.createLocale = function() {
|
||||||
|
if(!$scope.newLocale) {
|
||||||
|
Notifications.error($translate.instant('missing-locale'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$scope.realmSpecificLocales.push($scope.newLocale)
|
||||||
|
$scope.selectedRealmSpecificLocales = $scope.newLocale;
|
||||||
|
$scope.newLocale = null;
|
||||||
|
$location.url('/create/localization/' + realm.realm + '/' + $scope.selectedRealmSpecificLocales);
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.$watch(function() {
|
||||||
|
return $scope.selectedRealmSpecificLocales;
|
||||||
|
}, function() {
|
||||||
|
if($scope.selectedRealmSpecificLocales != null) {
|
||||||
|
$scope.updateRealmSpecificLocalizationTexts();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
$scope.updateRealmSpecificLocales = function() {
|
||||||
|
RealmSpecificLocales.get({id: realm.realm}, function (updated) {
|
||||||
|
$scope.realmSpecificLocales = updated;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.updateRealmSpecificLocalizationTexts = function() {
|
||||||
|
RealmSpecificLocalizationTexts.get({id: realm.realm, locale: $scope.selectedRealmSpecificLocales }, function (updated) {
|
||||||
|
$scope.localizationTexts = updated;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.removeLocalizationText = function(key) {
|
||||||
|
Dialog.confirmDelete(key, 'localization text', function() {
|
||||||
|
RealmSpecificLocalizationText.remove({
|
||||||
|
realm: realm.realm,
|
||||||
|
locale: $scope.selectedRealmSpecificLocales,
|
||||||
|
key: key
|
||||||
|
}, function () {
|
||||||
|
$scope.updateRealmSpecificLocalizationTexts();
|
||||||
|
Notifications.success($translate.instant('localization-text.remove.success'));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.controller('RealmLocalizationUploadCtrl', function($scope, Current, Realm, realm, serverInfo, $http, $route, Dialog, Notifications, $upload, $translate){
|
||||||
|
$scope.realm = realm;
|
||||||
|
$scope.locale = null;
|
||||||
|
$scope.files = [];
|
||||||
|
|
||||||
|
$scope.onFileSelect = function($files) {
|
||||||
|
$scope.files = $files;
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.reset = function() {
|
||||||
|
$scope.locale = null;
|
||||||
|
$scope.files = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.save = function() {
|
||||||
|
|
||||||
|
if(!$scope.files || $scope.files.length === 0) {
|
||||||
|
Notifications.error($translate.instant('missing-file'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//$files: an array of files selected, each file has name, size, and type.
|
||||||
|
for (var i = 0; i < $scope.files.length; i++) {
|
||||||
|
var $file = $scope.files[i];
|
||||||
|
$scope.upload = $upload.upload({
|
||||||
|
url: authUrl + '/admin/realms/' + realm.realm + '/localization/' + $scope.locale,
|
||||||
|
file: $file
|
||||||
|
}).then(function(response) {
|
||||||
|
$scope.reset();
|
||||||
|
Notifications.success($translate.instant('localization-file.upload.success'));
|
||||||
|
}).catch(function() {
|
||||||
|
Notifications.error($translate.instant('localization-file.upload.error'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
module.controller('RealmLocalizationDetailCtrl', function($scope, Current, $location, Realm, realm, Notifications, locale, key, RealmSpecificLocalizationText, localizationText, $translate){
|
||||||
|
$scope.realm = realm;
|
||||||
|
$scope.locale = locale;
|
||||||
|
$scope.key = key;
|
||||||
|
$scope.value = ((localizationText)? localizationText.content : null);
|
||||||
|
|
||||||
|
$scope.create = !key;
|
||||||
|
|
||||||
|
$scope.save = function() {
|
||||||
|
if ($scope.create) {
|
||||||
|
RealmSpecificLocalizationText.save({
|
||||||
|
realm: realm.realm,
|
||||||
|
locale: $scope.locale,
|
||||||
|
key: $scope.key
|
||||||
|
}, $scope.value, function (data, headers) {
|
||||||
|
$location.url("/realms/" + realm.realm + "/localization");
|
||||||
|
Notifications.success($translate.instant('localization-text.create.success'));
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
RealmSpecificLocalizationText.save({
|
||||||
|
realm: realm.realm,
|
||||||
|
locale: $scope.locale,
|
||||||
|
key: $scope.key
|
||||||
|
}, $scope.value, function (data, headers) {
|
||||||
|
$location.url("/realms/" + realm.realm + "/localization");
|
||||||
|
Notifications.success($translate.instant('localization-text.update.success'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.cancel = function () {
|
||||||
|
$location.url("/realms/" + realm.realm + "/localization");
|
||||||
|
};
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
module.controller('RealmCacheCtrl', function($scope, realm, RealmClearUserCache, RealmClearRealmCache, RealmClearKeysCache, Notifications) {
|
module.controller('RealmCacheCtrl', function($scope, realm, RealmClearUserCache, RealmClearRealmCache, RealmClearKeysCache, Notifications) {
|
||||||
$scope.realm = angular.copy(realm);
|
$scope.realm = angular.copy(realm);
|
||||||
|
|
||||||
|
|
|
@ -57,6 +57,24 @@ module.factory('RealmKeysLoader', function(Loader, RealmKeys, $route, $q) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
module.factory('RealmSpecificLocalesLoader', function(Loader, RealmSpecificLocales, $route, $q) {
|
||||||
|
return Loader.get(RealmSpecificLocales, function() {
|
||||||
|
return {
|
||||||
|
id : $route.current.params.realm
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
module.factory('RealmSpecificlocalizationTextLoader', function(Loader, RealmSpecificLocalizationText, $route, $q) {
|
||||||
|
return Loader.get(RealmSpecificLocalizationText, function() {
|
||||||
|
return {
|
||||||
|
realm : $route.current.params.realm,
|
||||||
|
locale : $route.current.params.locale,
|
||||||
|
key: $route.current.params.key
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
module.factory('RealmEventsConfigLoader', function(Loader, RealmEventsConfig, $route, $q) {
|
module.factory('RealmEventsConfigLoader', function(Loader, RealmEventsConfig, $route, $q) {
|
||||||
return Loader.get(RealmEventsConfig, function() {
|
return Loader.get(RealmEventsConfig, function() {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -377,6 +377,41 @@ module.factory('RealmKeys', function($resource) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
module.factory('RealmSpecificLocales', function($resource) {
|
||||||
|
return $resource(authUrl + '/admin/realms/:id/localization', {
|
||||||
|
id : '@realm'
|
||||||
|
},{'get': {method:'GET', isArray:true}});
|
||||||
|
});
|
||||||
|
|
||||||
|
module.factory('RealmSpecificLocalizationTexts', function($resource) {
|
||||||
|
return $resource(authUrl + '/admin/realms/:id/localization/:locale', {
|
||||||
|
id : '@realm',
|
||||||
|
locale : '@locale'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
module.factory('RealmSpecificLocalizationText', function ($resource) {
|
||||||
|
return $resource(authUrl + '/admin/realms/:realm/localization/:locale/:key', {
|
||||||
|
realm: '@realm',
|
||||||
|
locale: '@locale',
|
||||||
|
key: '@key'
|
||||||
|
}, {
|
||||||
|
// wrap plain text response as AngularJS $resource will convert it into a char array otherwise.
|
||||||
|
get: {
|
||||||
|
method: 'GET',
|
||||||
|
transformResponse: function (data) {
|
||||||
|
return {content: data};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
save: {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'text/plain;charset=utf-8'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
module.factory('RealmEventsConfig', function($resource) {
|
module.factory('RealmEventsConfig', function($resource) {
|
||||||
return $resource(authUrl + '/admin/realms/:id/events/config', {
|
return $resource(authUrl + '/admin/realms/:id/events/config', {
|
||||||
id : '@realm'
|
id : '@realm'
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
|
||||||
|
|
||||||
|
<ol class="breadcrumb">
|
||||||
|
<li><a href="#/realms/{{realm.realm}}/localization">{{:: 'localization' | translate}}</a></li>
|
||||||
|
<li data-ng-hide="create">{{key}}</li>
|
||||||
|
<li data-ng-show="create">{{:: 'add-localization-text' | translate}}</li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
<form class="form-horizontal clearfix" name="localizationForm" novalidate>
|
||||||
|
<fieldset>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-md-2 control-label" for="locale">{{:: 'locale' | translate}}</label>
|
||||||
|
|
||||||
|
<div class="col-md-6">
|
||||||
|
<input class="form-control" type="text" id="locale" name="locale" data-ng-model="locale"
|
||||||
|
readonly>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-md-2 control-label" for="key">
|
||||||
|
<span class="required" data-ng-show="create">*</span> {{:: 'key' | translate}}
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div class="col-md-6">
|
||||||
|
<input class="form-control" type="text" id="key" name="key" data-ng-model="key" autofocus
|
||||||
|
required data-ng-readonly="!create">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-md-2 control-label" for="value">
|
||||||
|
<span class="required" data-ng-show="create">*</span> {{:: 'value' | translate}}
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div class="col-md-6">
|
||||||
|
<input class="form-control" type="text" id="value" name="value" required data-ng-model="value">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-md-10 col-md-offset-2">
|
||||||
|
<button kc-save>{{:: 'save' | translate}}</button>
|
||||||
|
<button kc-cancel data-ng-click="cancel()">{{:: 'cancel' | translate}}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<kc-menu></kc-menu>
|
|
@ -0,0 +1,37 @@
|
||||||
|
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
|
||||||
|
<kc-tabs-realm></kc-tabs-realm>
|
||||||
|
|
||||||
|
<ul class="nav nav-tabs nav-tabs-pf">
|
||||||
|
<li><a href="#/realms/{{realm.realm}}/localization">{{:: 'lookup' | translate}}</a></li>
|
||||||
|
<li class="active"><a href="#/realms/{{realm.realm}}/localization/upload">{{:: 'localization-upload-file' | translate}}</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-md-2 control-label" for="locale"><span class="required">*</span> {{:: 'locale' | translate}}</label>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<input class="form-control" id="locale" type="text" ng-model="locale" placeholder="{{:: 'locale' | translate}}" required>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-md-2 control-label"><span class="required">*</span> {{:: 'file' | translate}}</label>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div data-ng-show="!files || files.length == 0">
|
||||||
|
<label for="import-file" class="btn btn-default">{{:: 'select-file' | translate}} <i class="pficon pficon-import"></i></label>
|
||||||
|
<input id="import-file" type="file" class="hidden" ng-file-select="onFileSelect($files)" required>
|
||||||
|
</div>
|
||||||
|
<span class="kc-uploaded-file" data-ng-show="files.length > 0">
|
||||||
|
{{files[0].name}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-md-10 col-md-offset-2">
|
||||||
|
<button data-kc-save>{{:: 'import' | translate}}</button>
|
||||||
|
<button data-kc-reset>{{:: 'cancel' | translate}}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<kc-menu></kc-menu>
|
|
@ -0,0 +1,61 @@
|
||||||
|
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
|
||||||
|
<kc-tabs-realm></kc-tabs-realm>
|
||||||
|
|
||||||
|
<ul class="nav nav-tabs nav-tabs-pf">
|
||||||
|
<li class="active"><a href="#/realms/{{realm.realm}}/localization">{{:: 'lookup' | translate}}</a></li>
|
||||||
|
<li><a href="#/realms/{{realm.realm}}/localization/upload">{{:: 'localization-upload-file' | translate}}</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<form class="form-horizontal" name="realmForm" novalidate>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-md-2 control-label" for="selectedRealmSpecificLocales">{{:: 'locale' | translate}}</label>
|
||||||
|
<div class="col-md-6" ng-show="realmSpecificLocales.length > 0">
|
||||||
|
<select class="form-control" id="selectedRealmSpecificLocales"
|
||||||
|
ng-model="selectedRealmSpecificLocales"
|
||||||
|
ng-options="o as o for o in realmSpecificLocales">
|
||||||
|
<option value="" disabled selected>{{:: 'select-one.placeholder' | translate}}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6" ng-show="realmSpecificLocales.length < 1">
|
||||||
|
{{:: 'no-localizations-configured' | translate}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group clearfix">
|
||||||
|
<label class="col-md-2 control-label"></label>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<input class="form-control ng-pristine ng-untouched ng-empty ng-invalid" id="newLocale" type="text" ng-model="newLocale" placeholder="locale">
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-4">
|
||||||
|
<button class="btn btn-primary ng-binding" type="submit" data-ng-click="createLocale()">Create</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<table class="table table-striped table-bordered" data-ng-show="localizationTexts">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="kc-table-actions" colspan="4">
|
||||||
|
<div class="form-inline">
|
||||||
|
<div class="pull-right" data-ng-show="access.manageRealm">
|
||||||
|
<a id="createLocalizationText" class="btn btn-default" href="#/create/localization/{{realm.realm}}/{{selectedRealmSpecificLocales}}">{{:: 'add-localization-text' | translate}}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
<tr >
|
||||||
|
<th>{{:: 'key' | translate}}</th>
|
||||||
|
<th>{{:: 'value' | translate}}</th>
|
||||||
|
<th colspan="2">{{:: 'actions' | translate}}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr ng-repeat="(key, value) in localizationTexts">
|
||||||
|
<td>{{key}}</td>
|
||||||
|
<td>{{value}}</td>
|
||||||
|
<td class="kc-action-cell" kc-open="/realms/{{realm.realm}}/localization/{{selectedRealmSpecificLocales}}/{{key}}">{{:: 'edit' | translate}}</td>
|
||||||
|
<td class="kc-action-cell" data-ng-click="removeLocalizationText(key)">{{:: 'delete' | translate}}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<kc-menu></kc-menu>
|
|
@ -24,6 +24,7 @@
|
||||||
|| path[2] == 'login-settings'
|
|| path[2] == 'login-settings'
|
||||||
|| path[2] == 'keys'
|
|| path[2] == 'keys'
|
||||||
|| path[2] == 'theme-settings'
|
|| path[2] == 'theme-settings'
|
||||||
|
|| path[2] == 'localization'
|
||||||
|| path[2] == 'token-settings'
|
|| path[2] == 'token-settings'
|
||||||
|| path[2] == 'client-registration'
|
|| path[2] == 'client-registration'
|
||||||
|| path[2] == 'cache-settings'
|
|| path[2] == 'cache-settings'
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
<li ng-class="{active: path[2] == 'keys'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/keys">{{:: 'realm-tab-keys' | translate}}</a></li>
|
<li ng-class="{active: path[2] == 'keys'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/keys">{{:: 'realm-tab-keys' | translate}}</a></li>
|
||||||
<li ng-class="{active: path[2] == 'smtp-settings'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/smtp-settings">{{:: 'realm-tab-email' | translate}}</a></li>
|
<li ng-class="{active: path[2] == 'smtp-settings'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/smtp-settings">{{:: 'realm-tab-email' | translate}}</a></li>
|
||||||
<li ng-class="{active: path[2] == 'theme-settings'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/theme-settings">{{:: 'realm-tab-themes' | translate}}</a></li>
|
<li ng-class="{active: path[2] == 'theme-settings'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/theme-settings">{{:: 'realm-tab-themes' | translate}}</a></li>
|
||||||
|
<li ng-class="{active: path[2] == 'localization'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/localization">{{:: 'realm-tab-localization' | translate}}</a></li>
|
||||||
<li ng-class="{active: path[2] == 'cache-settings'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/cache-settings">{{:: 'realm-tab-cache' | translate}}</a></li>
|
<li ng-class="{active: path[2] == 'cache-settings'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/cache-settings">{{:: 'realm-tab-cache' | translate}}</a></li>
|
||||||
<li ng-class="{active: path[2] == 'token-settings'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/token-settings">{{:: 'realm-tab-tokens' | translate}}</a></li>
|
<li ng-class="{active: path[2] == 'token-settings'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/token-settings">{{:: 'realm-tab-tokens' | translate}}</a></li>
|
||||||
<li ng-class="{active: path[2] == 'client-registration'}" data-ng-show="access.viewClients"><a href="#/realms/{{realm.realm}}/client-registration/client-initial-access">{{:: 'realm-tab-client-registration' | translate}}</a></li>
|
<li ng-class="{active: path[2] == 'client-registration'}" data-ng-show="access.viewClients"><a href="#/realms/{{realm.realm}}/client-registration/client-initial-access">{{:: 'realm-tab-client-registration' | translate}}</a></li>
|
||||||
|
|
Loading…
Reference in a new issue