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")
|
||||
KeyResource keys();
|
||||
|
||||
@Path("localization")
|
||||
RealmLocalizationResource localization();
|
||||
|
||||
}
|
||||
|
|
|
@ -1645,6 +1645,35 @@ public class RealmAdapter implements CachedRealmModel {
|
|||
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
|
||||
public String toString() {
|
||||
return String.format("%s@%08x", getId(), hashCode());
|
||||
|
|
|
@ -171,7 +171,6 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
return groupDelegate;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void registerRealmInvalidation(String id, String name) {
|
||||
cache.realmUpdated(id, name, invalidations);
|
||||
|
@ -1247,4 +1246,51 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
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;
|
||||
|
||||
protected Map<String, Map<String,String>> realmLocalizationTexts;
|
||||
|
||||
public CachedRealm(Long revision, RealmModel model) {
|
||||
super(revision, model.getId());
|
||||
name = model.getName();
|
||||
|
@ -301,6 +303,7 @@ public class CachedRealm extends AbstractExtendableRevisioned {
|
|||
} catch (UnsupportedOperationException ex) {
|
||||
}
|
||||
|
||||
realmLocalizationTexts = model.getRealmLocalizationTexts();
|
||||
}
|
||||
|
||||
protected void cacheClientScopes(RealmModel model) {
|
||||
|
@ -718,4 +721,8 @@ public class CachedRealm extends AbstractExtendableRevisioned {
|
|||
public boolean isAllowUserManagedAccess() {
|
||||
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.GroupEntity;
|
||||
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.utils.KeycloakModelUtils;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.LockModeType;
|
||||
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.stream.Collectors;
|
||||
|
@ -858,6 +862,84 @@ public class JpaRealmProvider implements RealmProvider, ClientProvider, GroupPro
|
|||
.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) {
|
||||
ClientInitialAccessModel model = new ClientInitialAccessModel();
|
||||
model.setId(entity.getId());
|
||||
|
|
|
@ -2225,6 +2225,53 @@ public class RealmAdapter implements RealmModel, JpaModel<RealmEntity> {
|
|||
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
|
||||
public String toString() {
|
||||
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.JoinColumn;
|
||||
import javax.persistence.JoinTable;
|
||||
import javax.persistence.MapKey;
|
||||
import javax.persistence.MapKeyColumn;
|
||||
import javax.persistence.NamedQueries;
|
||||
import javax.persistence.NamedQuery;
|
||||
|
@ -242,6 +243,9 @@ public class RealmEntity {
|
|||
@Column(name="ALLOW_USER_MANAGED_ACCESS")
|
||||
private boolean allowUserManagedAccess;
|
||||
|
||||
@OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "realmId")
|
||||
@MapKey(name="locale")
|
||||
Map<String, RealmLocalizationTextsEntity> realmLocalizationTexts;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
|
@ -834,6 +838,17 @@ public class RealmEntity {
|
|||
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
|
||||
public boolean equals(Object o) {
|
||||
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"/>
|
||||
</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>
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
<class>org.keycloak.models.jpa.entities.FederatedIdentityEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.MigrationModelEntity</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.UserAttributeEntity</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 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.
|
||||
*/
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
package org.keycloak.models;
|
||||
|
||||
import java.util.Map;
|
||||
import org.keycloak.migration.MigrationModel;
|
||||
import org.keycloak.provider.Provider;
|
||||
|
||||
|
@ -80,6 +81,18 @@ public interface RealmProvider extends Provider /* TODO: Remove in future versio
|
|||
void removeExpiredClientInitialAccess();
|
||||
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
|
||||
// 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
|
||||
|
|
|
@ -28,7 +28,6 @@ import java.util.Locale;
|
|||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.broker.provider.BrokeredIdentityContext;
|
||||
import org.keycloak.common.util.ObjectUtil;
|
||||
import org.keycloak.email.EmailException;
|
||||
|
@ -209,6 +208,8 @@ public class FreeMarkerEmailTemplateProvider implements EmailTemplateProvider {
|
|||
Locale locale = session.getContext().resolveLocale(user);
|
||||
attributes.put("locale", 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("properties", theme.getProperties());
|
||||
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);
|
||||
Properties messagesBundle = handleThemeResources(theme, locale, attributes);
|
||||
Map<String, String> localizationTexts = realm.getRealmLocalizationTextsByLocale(locale.toLanguageTag());
|
||||
messagesBundle.putAll(localizationTexts);
|
||||
|
||||
URI baseUri = uriInfo.getBaseUri();
|
||||
UriBuilder baseUriBuilder = uriInfo.getBaseUriBuilder();
|
||||
|
|
|
@ -182,6 +182,8 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
|||
|
||||
Locale locale = session.getContext().resolveLocale(user);
|
||||
Properties messagesBundle = handleThemeResources(theme, locale);
|
||||
Map<String, String> localizationTexts = realm.getRealmLocalizationTextsByLocale(locale.toLanguageTag());
|
||||
messagesBundle.putAll(localizationTexts);
|
||||
|
||||
handleMessages(locale, messagesBundle);
|
||||
|
||||
|
@ -248,6 +250,8 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
|||
|
||||
Locale locale = session.getContext().resolveLocale(user);
|
||||
Properties messagesBundle = handleThemeResources(theme, locale);
|
||||
Map<String, String> localizationTexts = realm.getRealmLocalizationTextsByLocale(locale.getCountry());
|
||||
messagesBundle.putAll(localizationTexts);
|
||||
|
||||
handleMessages(locale, messagesBundle);
|
||||
|
||||
|
@ -353,6 +357,8 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
|||
|
||||
Locale locale = session.getContext().resolveLocale(user);
|
||||
Properties messagesBundle = handleThemeResources(theme, locale);
|
||||
Map<String, String> localizationTexts = realm.getRealmLocalizationTextsByLocale(locale.getCountry());
|
||||
messagesBundle.putAll(localizationTexts);
|
||||
FormMessage msg = new FormMessage(null, message);
|
||||
return formatMessage(msg, messagesBundle, locale);
|
||||
}
|
||||
|
@ -369,6 +375,8 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
|||
|
||||
Locale locale = session.getContext().resolveLocale(user);
|
||||
Properties messagesBundle = handleThemeResources(theme, locale);
|
||||
Map<String, String> localizationTexts = realm.getRealmLocalizationTextsByLocale(locale.getCountry());
|
||||
messagesBundle.putAll(localizationTexts);
|
||||
FormMessage msg = new FormMessage(message, (Object[]) parameters);
|
||||
return formatMessage(msg, messagesBundle, locale);
|
||||
}
|
||||
|
|
|
@ -220,6 +220,15 @@ public class RealmAdminResource {
|
|||
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.
|
||||
|
|
|
@ -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.testsuite.arquillian.TestContext;
|
||||
import com.google.common.collect.Streams;
|
||||
import java.util.Collection;
|
||||
|
||||
import java.util.concurrent.ConcurrentLinkedDeque;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
/**
|
||||
* 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 AUTH_FLOW_IDS = "AUTH_FLOW_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 String realmName;
|
||||
|
@ -115,6 +115,9 @@ public class TestCleanup {
|
|||
entities.add(AUTH_FLOW_IDS, flowId);
|
||||
}
|
||||
|
||||
public void addLocalization(String language) {
|
||||
entities.add(LOCALIZATION_LANGUAGES, language);
|
||||
}
|
||||
|
||||
public void addAuthenticationConfigId(String 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() {
|
||||
|
|
|
@ -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.placeholder=Indica l''idioma i prem Intro
|
||||
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.tooltip=Activar/desactivar la cach\u00E9 per al domini, client i dades de rols.
|
||||
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-email=Email
|
||||
realm-tab-themes=Temes
|
||||
#realm-tab-localization=Localization
|
||||
realm-tab-cache=Cach\u00E9
|
||||
realm-tab-tokens=Tokens
|
||||
realm-tab-security-defenses=Defenses de seguretat
|
||||
|
|
|
@ -91,6 +91,18 @@ i18n-enabled=Internationalisierung aktiv
|
|||
supported-locales=Unterst\u00FCtzte Sprachen
|
||||
#supported-locales.placeholder=Type a locale and enter
|
||||
#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.tooltip=Clears all entries from the realm cache (this will clear entries for all realms)
|
||||
#user-cache-clear=User Cache
|
||||
|
@ -175,6 +187,7 @@ days=Tage
|
|||
#realm-tab-keys=Keys
|
||||
#realm-tab-email=Email
|
||||
#realm-tab-themes=Themes
|
||||
realm-tab-localization=Internationalisierung
|
||||
#realm-tab-cache=Cache
|
||||
#realm-tab-tokens=Tokens
|
||||
#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.tooltip=Relay state you want to send with SAML request when you want to do IDP Initiated SSO.
|
||||
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.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
|
||||
|
@ -505,13 +518,13 @@ last-refresh=Letzte Aktualisierung
|
|||
#first-broker-login-flow=First Login Flow
|
||||
#post-broker-login-flow=Post Login Flow
|
||||
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.legacy=Legacy
|
||||
sync-mode.import=Importieren
|
||||
sync-mode.force=Erzwingen
|
||||
sync-mode-override=Ãœberschriebene 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=\u00DCberschriebene Synchronisation
|
||||
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.tooltip=The redirect uri to use when configuring the identity provider.
|
||||
#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.move.success=Gruppe verschoben.
|
||||
group.remove.confirm.title=Gruppe löschen
|
||||
group.remove.confirm.message=Sind Sie sicher, dass Sie die Gruppe \u201E{{name}}\u201C löschen möchten?
|
||||
group.remove.success=Die Gruppe wurde gelöscht.
|
||||
group.remove.confirm.title=Gruppe l\u00F6schen
|
||||
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\u00F6scht.
|
||||
group.fetch.fail=Fehler beim Laden: {{params}}
|
||||
group.create.success=Gruppe erstellt.
|
||||
group.edit.success=Die Änderungen wurde gespeichert.
|
||||
group.roles.add.success=Rollenzuweisung hinzugefügt.
|
||||
group.edit.success=Die \u00C4nderungen wurde gespeichert.
|
||||
group.roles.add.success=Rollenzuweisung hinzugef\u00FCgt.
|
||||
group.roles.remove.success=Rollenzuweisung entfernt.
|
||||
group.default.add.error=Bitte eine Gruppe auswählen.
|
||||
group.default.add.success=Standardgruppe hinzugefügt.
|
||||
group.default.add.error=Bitte eine Gruppe ausw\u00E4hlen.
|
||||
group.default.add.success=Standardgruppe hinzugef\u00FCgt.
|
||||
group.default.remove.success=Standardgruppe entfernt.
|
||||
|
||||
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.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.logout.all.success=Benutzer von allen Sitzungen 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.message=Sind Sie sicher, dass Sie die Verknüpfung mit dem Identity Provider \u201E{{name}}\u201C entfernen möchten?
|
||||
user.fedid.link.remove.success=Verknüpfung mit Identity Provider entfernt.
|
||||
user.fedid.link.add.success=Verknüpfung mit Identity Provider angelegt.
|
||||
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\u00FCpfung mit dem Identity Provider \u201E{{name}}\u201C entfernen m\u00F6chten?
|
||||
user.fedid.link.remove.success=Verkn\u00FCpfung mit Identity Provider entfernt.
|
||||
user.fedid.link.add.success=Verkn\u00FCpfung mit Identity Provider angelegt.
|
||||
user.consent.revoke.success=Einwilligung widerrufen.
|
||||
user.consent.revoke.error=Einwilligung konnte nicht widerrufen werden.
|
||||
user.unlock.success=Alle vorübergehend gesperrten Benutzer wurden entsperrt.
|
||||
user.remove.confirm.title=Benutzer löschen
|
||||
user.remove.confirm.message=Sind Sie sicher, dass Sie den Benutzer \u201E{{name}}\u201C löschen möchten?
|
||||
user.remove.success=Der Benutzer wurde gelöscht.
|
||||
user.remove.error=Der Benutzer konnte nicht gelöscht werden.
|
||||
user.unlock.success=Alle vor\u00FCbergehend gesperrten Benutzer wurden entsperrt.
|
||||
user.remove.confirm.title=Benutzer l\u00F6schen
|
||||
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\u00F6scht.
|
||||
user.remove.error=Der Benutzer konnte nicht gel\u00F6scht werden.
|
||||
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.error=Beim Speichern der Zugangsdaten ist ein Fehler aufgetreten.
|
||||
user.credential.remove.confirm.title=Zugangsdaten löschen
|
||||
user.credential.remove.confirm.message=Sind Sie sicher, dass Sie die Zugangsdaten löschen löschen möchten?
|
||||
user.credential.remove.success=Die Zugangsdaten wurden gelöscht.
|
||||
user.credential.remove.error=Beim Löschen der Zugangsdaten ist ein Fehler aufgetreten.
|
||||
user.credential.remove.confirm.title=Zugangsdaten l\u00F6schen
|
||||
user.credential.remove.confirm.message=Sind Sie sicher, dass Sie die Zugangsdaten l\u00F6schen m\u00F6chten?
|
||||
user.credential.remove.success=Die Zugangsdaten wurden gel\u00F6scht.
|
||||
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-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.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.password.error.not-matching=Die Passwörter stimmen nicht überein.
|
||||
user.password.reset.confirm.title=Passwort zurücksetzen
|
||||
user.password.reset.confirm.message=Sind Sie sicher, dass Sie das Passwort für diesen Benutzer zurücksetzen möchten?
|
||||
user.password.reset.success=Das Passwort wurde zurückgesetzt.
|
||||
user.password.error.not-matching=Die Passw\u00F6rter stimmen nicht \u00FCberein.
|
||||
user.password.reset.confirm.title=Passwort zur\u00FCcksetzen
|
||||
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\u00FCckgesetzt.
|
||||
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.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.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.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.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.error=Fehler beim Senden der E-Mail
|
||||
#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.groups.fetch.all.error=Fehler beim Laden alle Gruppen: {{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.already-added=Benutzer gehört der Gruppe bereits an.
|
||||
user.groups.join.success=Zur Gruppe hinzugefügt.
|
||||
user.groups.leave.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\u00F6rt der Gruppe bereits an.
|
||||
user.groups.join.success=Zur Gruppe hinzugef\u00FCgt.
|
||||
user.groups.leave.error.no-group-selected=Bitte w\u00E4hlen Sie eine Gruppe aus!
|
||||
user.groups.leave.success=Aus Gruppe entfernt.
|
||||
|
||||
#default.available-roles.tooltip=Realm level roles that can be assigned.
|
||||
|
@ -1608,8 +1621,8 @@ notifications.success.header=Erfolg!
|
|||
notifications.error.header=Fehler!
|
||||
notifications.warn.header=Warnung!
|
||||
|
||||
dialogs.delete.title={{type}} löschen
|
||||
dialogs.delete.message=Sind Sie sicher, dass Sie {{type}} {{name}} löschen möchten?
|
||||
dialogs.delete.confirm=Löschen
|
||||
dialogs.delete.title={{type}} l\u00F6schen
|
||||
dialogs.delete.message=Sind Sie sicher, dass Sie {{type}} {{name}} l\u00F6schen m\u00F6chten?
|
||||
dialogs.delete.confirm=L\u00F6schen
|
||||
dialogs.cancel=Abbrechen
|
||||
dialogs.ok=OK
|
||||
|
|
|
@ -62,6 +62,18 @@ i18n-enabled=Internacionalizaci\u00F3n activa
|
|||
supported-locales=Idiomas soportados
|
||||
supported-locales.placeholder=Indica el idioma y pulsa Intro
|
||||
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.tooltip=Activar/desactivar la cach\u00E9 para el dominio, cliente y datos de roles.
|
||||
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-email=Email
|
||||
realm-tab-themes=Temas
|
||||
#realm-tab-localization=Localization
|
||||
realm-tab-cache=Cach\u00E9
|
||||
realm-tab-tokens=Tokens
|
||||
realm-tab-security-defenses=Defensas de seguridad
|
||||
|
|
|
@ -82,6 +82,18 @@ i18n-enabled=Internationalisation activ\u00e9e
|
|||
supported-locales=Locales support\u00e9es
|
||||
supported-locales.placeholder=Entrez la locale et validez
|
||||
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.tooltip=Activer/D\u00e9sactiver le cache pour le domaine, client et donn\u00e9es.
|
||||
user-cache-enabled=Cache utilisateur activ\u00e9
|
||||
|
@ -123,6 +135,7 @@ realm-tab-login=Connexion
|
|||
realm-tab-keys=Clefs
|
||||
realm-tab-email=Courriels
|
||||
realm-tab-themes=Th\u00e8mes
|
||||
#realm-tab-localization=Localization
|
||||
realm-tab-cache=Cache
|
||||
realm-tab-tokens=Jetons
|
||||
realm-tab-security-defenses=Mesures de s\u00e9curit\u00e9
|
||||
|
|
|
@ -93,6 +93,18 @@ i18n-enabled=国際化の有効
|
|||
supported-locales=サポートされるロケール
|
||||
supported-locales.placeholder=ロケールを入力し、Enterキーを押してください
|
||||
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.tooltip=レルムキャッシュからすべてのエントリーをクリアする(これにより、すべてのレルムのエントリーがクリアされます)。
|
||||
user-cache-clear=ユーザー・キャッシュ
|
||||
|
@ -192,6 +204,7 @@ realm-tab-login=ログイン
|
|||
realm-tab-keys=鍵
|
||||
realm-tab-email=Eメール
|
||||
realm-tab-themes=テーマ
|
||||
#realm-tab-localization=Localization
|
||||
realm-tab-cache=キャッシュ
|
||||
realm-tab-tokens=トークン
|
||||
realm-tab-client-registration=クライアント登録
|
||||
|
|
|
@ -69,6 +69,18 @@ i18n-enabled=Daugiakalbystės palaikymas
|
|||
supported-locales=Palaikomos kalbos
|
||||
supported-locales.placeholder=Pasirinkite arba įrašykite kalbos pavadinimą
|
||||
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.tooltip=Iš visų sričių pašalinama visa podėlyje (cache) esanti informacija
|
||||
user-cache-clear=Naudotojų podėlis
|
||||
|
@ -119,6 +131,7 @@ realm-tab-login=Prisijungimas
|
|||
realm-tab-keys=Raktai
|
||||
realm-tab-email=El. paštas
|
||||
realm-tab-themes=Temos
|
||||
#realm-tab-localization=Localization
|
||||
realm-tab-cache=Podėlis
|
||||
realm-tab-tokens=Raktai
|
||||
realm-tab-client-registration=Klientų registracija
|
||||
|
|
|
@ -68,6 +68,18 @@ i18n-enabled=Internasjonalisering aktivert
|
|||
supported-locales=St\u00F8ttede lokaliteter
|
||||
supported-locales.placeholder=Skriv inn en lokalitet og klikk enter
|
||||
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.tooltip=T\u00F8m sikkerhetsdomenecache (Dette vil fjerne oppf\u00F8ringer for alle sikkerhetsdomener)
|
||||
user-cache-clear=Brukercache
|
||||
|
@ -120,6 +132,7 @@ realm-tab-login=Innlogging
|
|||
realm-tab-keys=N\u00F8kler
|
||||
realm-tab-email=E-post
|
||||
realm-tab-themes=Tema
|
||||
#realm-tab-localization=Localization
|
||||
realm-tab-cache=Cache
|
||||
realm-tab-tokens=Tokens
|
||||
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.placeholder=Digite um local e pressione Enter
|
||||
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.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
|
||||
|
@ -119,6 +131,7 @@ realm-tab-login=Login
|
|||
realm-tab-keys=Chaves
|
||||
realm-tab-email=E-mail
|
||||
realm-tab-themes=Temas
|
||||
#realm-tab-localization=Localization
|
||||
realm-tab-cache=Cache
|
||||
realm-tab-tokens=Tokens
|
||||
realm-tab-client-initial-access=Tokens de Acesso inicial
|
||||
|
|
|
@ -75,6 +75,18 @@ i18n-enabled=Интернационализация
|
|||
supported-locales=Поддерживаемые языки
|
||||
supported-locales.placeholder=Выберите язык и нажмите Enter
|
||||
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.tooltip=Удалить все записи в кэше realm (удалит все записи для всех realm)
|
||||
user-cache-clear=Кэш пользователей
|
||||
|
@ -127,6 +139,7 @@ realm-tab-login=Вход
|
|||
realm-tab-keys=Ключи
|
||||
realm-tab-email=E-mail
|
||||
realm-tab-themes=Темы
|
||||
#realm-tab-localization=Localization
|
||||
realm-tab-cache=Кэш
|
||||
realm-tab-tokens=Токены
|
||||
realm-tab-client-initial-access=Первоначальные токены доступа
|
||||
|
|
|
@ -69,6 +69,18 @@ i18n-enabled=启用国际化
|
|||
supported-locales=支持的语言
|
||||
supported-locales.placeholder=输入一个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.tooltip=从域缓存中清理所有条目(这会清理所有域的条目)
|
||||
user-cache-clear=用户缓存
|
||||
|
@ -119,6 +131,7 @@ realm-tab-login=登录
|
|||
realm-tab-keys=秘钥
|
||||
realm-tab-email=Email
|
||||
realm-tab-themes=主题
|
||||
#realm-tab-localization=Localization
|
||||
realm-tab-cache=缓存
|
||||
realm-tab-tokens=Tokens
|
||||
realm-tab-client-registration=客户端注册
|
||||
|
|
|
@ -96,6 +96,17 @@ i18n-enabled=Internationalization Enabled
|
|||
supported-locales=Supported Locales
|
||||
supported-locales.placeholder=Type a locale and 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
|
||||
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.tooltip=Clears all entries from the realm cache (this will clear entries for all realms)
|
||||
user-cache-clear=User Cache
|
||||
|
@ -199,6 +210,7 @@ realm-tab-login=Login
|
|||
realm-tab-keys=Keys
|
||||
realm-tab-email=Email
|
||||
realm-tab-themes=Themes
|
||||
realm-tab-localization=Localization
|
||||
realm-tab-cache=Cache
|
||||
realm-tab-tokens=Tokens
|
||||
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 resourceRequests = 0;
|
||||
var loadingTimer = -1;
|
||||
var translateProvider = null;
|
||||
var currentRealm = null;
|
||||
|
||||
angular.element(document).ready(function () {
|
||||
var keycloakAuth = new Keycloak(consoleBaseUrl + 'config');
|
||||
|
@ -146,6 +148,7 @@ module.factory('authInterceptor', function($q, Auth) {
|
|||
});
|
||||
|
||||
module.config(['$translateProvider', function($translateProvider) {
|
||||
translateProvider = $translateProvider;
|
||||
$translateProvider.useSanitizeValueStrategy('sanitizeParameters');
|
||||
$translateProvider.preferredLanguage(locale);
|
||||
$translateProvider.translations(locale, resourceBundle);
|
||||
|
@ -178,6 +181,33 @@ module.config([ '$routeProvider', function($routeProvider) {
|
|||
},
|
||||
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', {
|
||||
templateUrl : resourceUrl + '/partials/realm-login-settings.html',
|
||||
resolve : {
|
||||
|
@ -2085,6 +2115,42 @@ module.config([ '$routeProvider', function($routeProvider) {
|
|||
},
|
||||
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', {
|
||||
templateUrl : resourceUrl + '/partials/server-info.html',
|
||||
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.resourceUrl = resourceUrl;
|
||||
$scope.auth = Auth;
|
||||
|
@ -97,6 +97,18 @@ module.controller('GlobalCtrl', function($scope, $http, Auth, Current, $location
|
|||
$scope.fragment = $location.path();
|
||||
$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) {
|
||||
|
@ -499,6 +511,131 @@ module.controller('RealmThemeCtrl', function($scope, Current, Realm, realm, serv
|
|||
$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) {
|
||||
$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) {
|
||||
return Loader.get(RealmEventsConfig, function() {
|
||||
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) {
|
||||
return $resource(authUrl + '/admin/realms/:id/events/config', {
|
||||
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] == 'keys'
|
||||
|| path[2] == 'theme-settings'
|
||||
|| path[2] == 'localization'
|
||||
|| path[2] == 'token-settings'
|
||||
|| path[2] == 'client-registration'
|
||||
|| 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] == '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] == '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] == '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>
|
||||
|
|
Loading…
Reference in a new issue