keycloak-aplcache

Bundles now read from base theme instead of from classpath.

8/27/2015 2:48:20 PM

Details

diff --git a/forms/common-themes/src/main/resources/theme/base/admin/angular-messages/common_de.properties b/forms/common-themes/src/main/resources/theme/base/admin/angular-messages/common_de.properties
new file mode 100644
index 0000000..3321f1e
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/base/admin/angular-messages/common_de.properties
@@ -0,0 +1,4 @@
+enabled=de Enabled
+name=de Name
+save=de Save
+cancel=de Cancel
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/angular-messages/realm-settings_de.properties b/forms/common-themes/src/main/resources/theme/base/admin/angular-messages/realm-settings_de.properties
new file mode 100644
index 0000000..4b79d17
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/base/admin/angular-messages/realm-settings_de.properties
@@ -0,0 +1,18 @@
+realm-detail.enabled.tooltip=de Users and clients can only access a realm if it's enabled
+realm-login-settings.registrationAllowed=de User registration
+realm-login-settings.registrationAllowed.tooltip=de Enable/disable the registration page. A link for registration will show on login page too.
+realm-login-settings.registrationEmailAsUsername=de Email as username
+realm-login-settings.registrationEmailAsUsername.tooltip=de If enabled then username field is hidden from registration form and email is used as username for new user.
+realm-login-settings.editUsernameAllowed=de Edit username
+realm-login-settings.editUsernameAllowed.tooltip=de If enabled, the username field is editable, readonly otherwise.
+realm-login-settings.resetPasswordAllowed=de Forget password
+realm-login-settings.resetPasswordAllowed.tooltip=de Show a link on login page for user to click on when they have forgotten their credentials.
+realm-login-settings.rememberMe=de Remember Me
+realm-login-settings.rememberMe.tooltip=de Show checkbox on login page to allow user to remain logged in between browser restarts until session expires.
+realm-login-settings.verifyEmail=de Verify email
+realm-login-settings.verifyEmail.tooltip=de Require the user to verify their email address the first time they login.
+realm-login-settings.sslRequired=de Require SSL
+realm-login-settings.sslRequired.option.all=de all requests
+realm-login-settings.sslRequired.option.external=de external requests
+realm-login-settings.sslRequired.option.none=de none
+realm-login-settings.sslRequired.tooltip=de Is HTTPS required?  'None' means HTTPS is not required for any client IP address. 'External requests' means localhost and private IP addresses can access without HTTPS.  'All requests' means HTTPS is required for all IP addresses.
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/angular-messages/realm-settings_en.properties b/forms/common-themes/src/main/resources/theme/base/admin/angular-messages/realm-settings_en.properties
new file mode 100644
index 0000000..227c2f1
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/base/admin/angular-messages/realm-settings_en.properties
@@ -0,0 +1,18 @@
+realm-detail.enabled.tooltip=Users and clients can only access a realm if it's enabled
+registrationAllowed=User registration
+registrationAllowed.tooltip=Enable/disable the registration page. A link for registration will show on login page too.
+registrationEmailAsUsername=Email as username
+registrationEmailAsUsername.tooltip=If enabled then username field is hidden from registration form and email is used as username for new user.
+editUsernameAllowed=Edit username
+editUsernameAllowed.tooltip=If enabled, the username field is editable, readonly otherwise.
+resetPasswordAllowed=Forget password
+resetPasswordAllowed.tooltip=Show a link on login page for user to click on when they have forgotten their credentials.
+rememberMe=Remember Me
+rememberMe.tooltip=Show checkbox on login page to allow user to remain logged in between browser restarts until session expires.
+verifyEmail=Verify email
+verifyEmail.tooltip=Require the user to verify their email address the first time they login.
+sslRequired=Require SSL
+sslRequired.option.all=all requests
+sslRequired.option.external=external requests
+sslRequired.option.none=none
+sslRequired.tooltip=Is HTTPS required?  'None' means HTTPS is not required for any client IP address. 'External requests' means localhost and private IP addresses can access without HTTPS.  'All requests' means HTTPS is required for all IP addresses.
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-detail.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-detail.html
index 469e8c6..83f1288 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-detail.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-detail.html
@@ -3,29 +3,29 @@
 
     <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
             <div class="form-group">
-                <label class="col-md-2 control-label" for="name"><span class="required">*</span> Name</label>
+                <label class="col-md-2 control-label" for="name"><span class="required">*</span> {{'name' | translate}}</label>
                 <div class="col-md-6">
                     <input class="form-control" type="text" id="name" name="name" data-ng-model="realm.realm" autofocus required>
                 </div>
             </div>
 
             <div class="form-group">
-                <label class="col-md-2 control-label" for="enabled">Enabled</label>
+                <label class="col-md-2 control-label" for="enabled">{{'enabled' | translate}}</label>
                 <div class="col-md-6">
                     <input ng-model="realm.enabled" name="enabled" id="enabled" onoffswitch />
                 </div>
-                <kc-tooltip>Users and clients can only access a realm if it's enabled</kc-tooltip>
+                <kc-tooltip>{{'realm-detail.enabled.tooltip' | translate}}</kc-tooltip>
             </div>
 
             <div class="form-group">
                 <div class="col-md-10 col-md-offset-2" data-ng-show="createRealm && access.manageRealm">
-                    <button kc-save data-ng-show="changed">Save</button>
-                    <button kc-cancel data-ng-click="cancel()">Cancel</button>
+                    <button kc-save data-ng-show="changed">{{'save' | translate}}</button>
+                    <button kc-cancel data-ng-click="cancel()">{{'cancel' | translate}}</button>
                 </div>
 
                 <div class="col-md-10 col-md-offset-2" data-ng-show="!createRealm && access.manageRealm">
-                    <button kc-save  data-ng-disabled="!changed">Save</button>
-                    <button kc-reset data-ng-disabled="!changed">Cancel</button>
+                    <button kc-save  data-ng-disabled="!changed">{{'save' | translate}}</button>
+                    <button kc-reset data-ng-disabled="!changed">{{'cancel' | translate}}</button>
                 </div>
             </div>
     </form>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-login-settings.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-login-settings.html
index adccf3b..451a315 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-login-settings.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-login-settings.html
@@ -4,66 +4,66 @@
     <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
         <fieldset class="border-top">
             <div class="form-group">
-                <label for="registrationAllowed" class="col-md-2 control-label">User registration</label>
+                <label for="registrationAllowed" class="col-md-2 control-label">{{'registrationAllowed' | translate}}</label>
                 <div class="col-md-6">
                     <input ng-model="realm.registrationAllowed" name="registrationAllowed" id="registrationAllowed" onoffswitch />
                 </div>
-                <kc-tooltip>Enable/disable the registration page. A link for registration will show on login page too.</kc-tooltip>
+                <kc-tooltip>{{'registrationAllowed.tooltip' | translate}}</kc-tooltip>
             </div>
             <div class="form-group" ng-show="realm.registrationAllowed">
-                <label for="registrationEmailAsUsername" class="col-md-2 control-label">Email as username</label>
+                <label for="registrationEmailAsUsername" class="col-md-2 control-label">{{'registrationEmailAsUsername' | translate}}</label>
                 <div class="col-md-6">
                     <input ng-model="realm.registrationEmailAsUsername" name="registrationEmailAsUsername" id="registrationEmailAsUsername" onoffswitch />
                 </div>
-                <kc-tooltip>If enabled then username field is hidden from registration form and email is used as username for new user.</kc-tooltip>
+                <kc-tooltip>{{'registrationEmailAsUsername.tooltip' | translate}}</kc-tooltip>
             </div>
             <div class="form-group">
-                <label for="editUsernameAllowed" class="col-md-2 control-label">Edit username</label>
+                <label for="editUsernameAllowed" class="col-md-2 control-label">{{'editUsernameAllowed' | translate}}</label>
                 <div class="col-md-6">
                     <input ng-model="realm.editUsernameAllowed" name="editUsernameAllowed" id="editUsernameAllowed" onoffswitch />
                 </div>
-                <kc-tooltip>If enabled, the username field is editable, readonly otherwise.</kc-tooltip>
+                <kc-tooltip>{{'editUsernameAllowed.tooltip' | translate}}</kc-tooltip>
             </div>
             <div class="form-group">
-                <label for="resetPasswordAllowed" class="col-md-2 control-label">Forget password</label>
+                <label for="resetPasswordAllowed" class="col-md-2 control-label">{{'resetPasswordAllowed' | translate}}</label>
                 <div class="col-md-6">
                     <input ng-model="realm.resetPasswordAllowed" name="resetPasswordAllowed" id="resetPasswordAllowed" onoffswitch />
                 </div>
-                <kc-tooltip>Show a link on login page for user to click on when they have forgotten their credentials.</kc-tooltip>
+                <kc-tooltip>{{'resetPasswordAllowed.tooltip' |translate}}</kc-tooltip>
             </div>
             <div class="form-group">
-                <label class="col-md-2 control-label" for="rememberMe">Remember Me</label>
+                <label class="col-md-2 control-label" for="rememberMe">{{'rememberMe' | translate}}</label>
                 <div class="col-md-6">
                     <input ng-model="realm.rememberMe" name="rememberMe" id="rememberMe" onoffswitch />
                 </div>
-                <kc-tooltip>Show checkbox on login page to allow user to remain logged in between browser restarts until session expires.</kc-tooltip>
+                <kc-tooltip>{{'rememberMe.tooltip' | translate}}</kc-tooltip>
             </div>
             <div class="form-group">
-                <label for="verifyEmail" class="col-md-2 control-label">Verify email</label>
+                <label for="verifyEmail" class="col-md-2 control-label">{{'verifyEmail' | translate}}</label>
                 <div class="col-md-6">
                     <input ng-model="realm.verifyEmail" name="verifyEmail" id="verifyEmail" onoffswitch />
                 </div>
-                <kc-tooltip>Require the user to verify their email address the first time they login.</kc-tooltip>
+                <kc-tooltip>{{'verifyEmail.tooltip' | translate}}</kc-tooltip>
             </div>
             <div class="form-group">
-                <label for="sslRequired" class="col-md-2 control-label">Require SSL</label>
+                <label for="sslRequired" class="col-md-2 control-label">{{'sslRequired' | translate}}</label>
                 <div class="col-md-2">
                     <div>
                         <select id="sslRequired" ng-model="realm.sslRequired" class="form-control">
-                            <option value="all">all requests</option>
-                            <option value="external">external requests</option>
-                            <option value="none">none</option>
+                            <option value="all">{{'sslRequired.option.all' | translate}}</option>
+                            <option value="external">{{'sslRequired.option.external' | translate}}</option>
+                            <option value="none">{{'sslRequired.option.none' | translate}}</option>
                         </select>
                     </div>
                 </div>
-                <kc-tooltip>Is HTTPS required?  'None' means HTTPS is not required for any client IP address. 'External requests' means localhost and private IP addresses can access without HTTPS.  'All requests' means HTTPS is required for all IP addresses.</kc-tooltip>
+                <kc-tooltip>{{'sslRequired.tooltip' | translate}}</kc-tooltip>
             </div>
         </fieldset>
 
         <div class="form-group" data-ng-show="access.manageRealm">
             <div class="col-md-10 col-md-offset-2">
-                <button kc-save  data-ng-disabled="!changed">Save</button>
-                <button kc-reset data-ng-disabled="!changed">Cancel</button>
+                <button kc-save  data-ng-disabled="!changed">{{'save' | translate}}</button>
+                <button kc-reset data-ng-disabled="!changed">{{'cancel' | translate}}</button>
             </div>
         </div>
     </form>
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/AdminMessagesLoader.java b/services/src/main/java/org/keycloak/services/resources/admin/AdminMessagesLoader.java
index 96d4f8d..709cb12 100644
--- a/services/src/main/java/org/keycloak/services/resources/admin/AdminMessagesLoader.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/AdminMessagesLoader.java
@@ -17,8 +17,10 @@
 
 package org.keycloak.services.resources.admin;
 
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FilenameFilter;
 import java.io.IOException;
-import java.io.InputStream;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Properties;
@@ -33,6 +35,8 @@ import java.util.Properties;
  * @author Stan Silvert ssilvert@redhat.com (C) 2015 Red Hat Inc.
  */
 public class AdminMessagesLoader {
+    private static final String CONFIG_DIR = System.getProperty("jboss.server.config.dir");
+    private static final String BUNDLE_DIR = CONFIG_DIR + "/themes/base/admin/angular-messages/";
 
     private static final Map<String, Properties> allMessages = new HashMap<String, Properties>();
 
@@ -44,19 +48,46 @@ public class AdminMessagesLoader {
     }
 
     private static Properties loadMessages(String locale) throws IOException {
-        Properties msgs = new Properties();
+        Properties masterMsgs = new Properties();
 
-        try (InputStream msgStream = getBundleStream(locale)){
-            if (msgStream == null) return msgs;
-            msgs.load(msgStream);
+        for (File file : getBundlesForLocale(locale)) {
+            try (FileInputStream msgStream = new FileInputStream(file)){
+                Properties propsFromFile = new Properties();
+                propsFromFile.load(msgStream);
+                checkForDups(masterMsgs, propsFromFile, file, locale);
+                masterMsgs.putAll(propsFromFile);
+            }
         }
 
-        allMessages.put(locale, msgs);
-        return msgs;
+        allMessages.put(locale, masterMsgs);
+        return masterMsgs;
     }
 
-    private static InputStream getBundleStream(String locale) {
-        String filename = "admin-messages_" + locale + ".properties";
-        return AdminMessagesLoader.class.getResourceAsStream(filename);
+    private static void checkForDups(Properties masterMsgs, Properties propsFromFile, File file, String locale) {
+        for (String prop : propsFromFile.stringPropertyNames()) {
+            if (masterMsgs.getProperty(prop) != null) {
+                String errorMsg = "Message bundle " + file.getName() + " contains key '" + prop;
+                errorMsg += "', which already exists in another bundle for " + locale + " locale.";
+                throw new RuntimeException(errorMsg);
+            }
+        }
+    }
+
+    private static File[] getBundlesForLocale(String locale) {
+        File bundleDir = new File(BUNDLE_DIR);
+        return bundleDir.listFiles(new LocaleFilter(locale));
+    }
+
+    private static class LocaleFilter implements FilenameFilter {
+        private final String locale;
+
+        public LocaleFilter(String locale) {
+            this.locale = locale;
+        }
+
+        @Override
+        public boolean accept(File dir, String name) {
+            return name.endsWith("_" + locale + ".properties");
+        }
     }
 }
diff --git a/services/src/main/java/org/keycloak/services/Urls.java b/services/src/main/java/org/keycloak/services/Urls.java
index 16a08d6..33bdd57 100755
--- a/services/src/main/java/org/keycloak/services/Urls.java
+++ b/services/src/main/java/org/keycloak/services/Urls.java
@@ -212,9 +212,9 @@ public class Urls {
         // I'm only using using localeCookiePath to get the /auth part of the path.
         // I can't assume the URL starts with "/auth".  Keycloak could be installed
         // as root context.  Typically, the angular-translate cookie path needs to be
-        //  /auth/admin/{realmName}/console
+        //  /auth/admin/{realmName}/console/
         String basePath = localeCookiePath(baseUri, realmName);
-        return basePath.substring(0, basePath.indexOf("realms")) + "admin/" + realmName + "/console";
+        return basePath.substring(0, basePath.indexOf("realms")) + "admin/" + realmName + "/console/";
     }
 
     public static URI themeRoot(URI baseUri) {