Explorar o código

configeditor enhancements
- replaced filter icon dialog and replaced with toggles for level and modified only filter
- made save/cancel button more prominent
- improved correctness of menu navigation filtering

Jason Rivard %!s(int64=4) %!d(string=hai) anos
pai
achega
fffd7aee99

+ 142 - 92
server/src/main/java/password/pwm/http/servlet/configeditor/data/NavTreeDataMaker.java

@@ -20,6 +20,7 @@
 
 
 package password.pwm.http.servlet.configeditor.data;
 package password.pwm.http.servlet.configeditor.data;
 
 
+import lombok.Value;
 import password.pwm.PwmApplication;
 import password.pwm.PwmApplication;
 import password.pwm.PwmConstants;
 import password.pwm.PwmConstants;
 import password.pwm.PwmEnvironment;
 import password.pwm.PwmEnvironment;
@@ -39,6 +40,7 @@ import password.pwm.util.logging.PwmLogger;
 import java.time.Instant;
 import java.time.Instant;
 import java.util.ArrayList;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
 import java.util.List;
 import java.util.Locale;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Map;
@@ -50,6 +52,10 @@ import java.util.TreeSet;
 import java.util.function.Supplier;
 import java.util.function.Supplier;
 import java.util.stream.Collectors;
 import java.util.stream.Collectors;
 
 
+/**
+ * Create a list of {@link NavTreeItem} values that represent a navigable tree menu.  The tree menu is displayed
+ * for administers in the config editor.
+ */
 public class NavTreeDataMaker
 public class NavTreeDataMaker
 {
 {
     private static final PwmLogger LOGGER = PwmLogger.forClass( NavTreeDataMaker.class );
     private static final PwmLogger LOGGER = PwmLogger.forClass( NavTreeDataMaker.class );
@@ -174,76 +180,45 @@ public class NavTreeDataMaker
         return modifiedKeys;
         return modifiedKeys;
     }
     }
 
 
-    private static boolean categoryMatcher(
-            final PwmApplication pwmApplication,
-            final PwmSettingCategory category,
+
+    private static List<ProfiledCategory> makeProfiledCategories(
             final StoredConfiguration storedConfiguration,
             final StoredConfiguration storedConfiguration,
             final NavTreeSettings navTreeSettings
             final NavTreeSettings navTreeSettings
     )
     )
     {
     {
-        if ( category.isHidden() )
-        {
-            return false;
-        }
-
-        if ( category == PwmSettingCategory.HTTPS_SERVER )
-        {
-            if ( !pwmApplication.getPwmEnvironment().getFlags().contains( PwmEnvironment.ApplicationFlag.ManageHttps ) )
-            {
-                return false;
-            }
-        }
-
-        for ( final PwmSettingCategory childCategory : category.getChildCategories() )
-        {
-            if ( categoryMatcher( pwmApplication, childCategory, storedConfiguration, navTreeSettings ) )
-            {
-                return true;
-            }
-        }
-
-        if ( category.hasProfiles() )
+        final List<ProfiledCategory> returnList = new ArrayList<>();
+        for ( final PwmSettingCategory category : PwmSettingCategory.sortedValues( navTreeSettings.getLocale() ) )
         {
         {
-            final List<String> profileIDs = storedConfiguration.profilesForSetting(
-                    category.getProfileSetting().orElseThrow( IllegalStateException::new ) );
-
-            if ( profileIDs == null || profileIDs.isEmpty() )
-            {
-                return true;
-            }
-
-            for ( final String profileID : profileIDs )
+            if ( category.hasProfiles() )
             {
             {
-                for ( final PwmSetting setting : category.getSettings() )
+                if ( !category.getParent().hasProfiles() )
                 {
                 {
-                    if ( settingMatches( storedConfiguration, setting, profileID, navTreeSettings ) )
+                    returnList.add( new ProfiledCategory( category, null, ProfiledCategory.Type.Profile ) );
+                }
+                else
+                {
+                    final List<String> profileIDs = StoredConfigurationUtil.profilesForCategory( category, storedConfiguration );
+                    for ( final String profileID : profileIDs )
                     {
                     {
-                        return true;
+                        returnList.add( new ProfiledCategory( category, profileID, ProfiledCategory.Type.CategoryWithProfile ) );
                     }
                     }
                 }
                 }
             }
             }
-
-        }
-        else
-        {
-            for ( final PwmSetting setting : category.getSettings() )
+            else
             {
             {
-                if ( settingMatches( storedConfiguration, setting, null, navTreeSettings ) )
-                {
-                    return true;
-                }
+                returnList.add( new ProfiledCategory( category, null, ProfiledCategory.Type.StaticCategory ) );
             }
             }
         }
         }
-        return false;
+        return Collections.unmodifiableList( returnList );
     }
     }
 
 
-    private static List<PwmSettingCategory> filteredCategories(
+    private static List<ProfiledCategory> filteredCategories(
             final PwmApplication pwmApplication,
             final PwmApplication pwmApplication,
             final StoredConfiguration storedConfiguration,
             final StoredConfiguration storedConfiguration,
             final NavTreeSettings navTreeSettings
             final NavTreeSettings navTreeSettings
     )
     )
     {
     {
-        return Collections.unmodifiableList( PwmSettingCategory.sortedValues( navTreeSettings.getLocale() )
+        return Collections.unmodifiableList( makeProfiledCategories( storedConfiguration, navTreeSettings )
                 .stream()
                 .stream()
                 .filter( pwmSettingCategory -> NavTreeDataMaker.categoryMatcher(
                 .filter( pwmSettingCategory -> NavTreeDataMaker.categoryMatcher(
                         pwmApplication,
                         pwmApplication,
@@ -263,66 +238,78 @@ public class NavTreeDataMaker
             final StoredConfiguration storedConfiguration
             final StoredConfiguration storedConfiguration
     )
     )
     {
     {
-        final List<PwmSettingCategory> categories = NavTreeDataMaker.filteredCategories(
+        final List<ProfiledCategory> profiledCategories = NavTreeDataMaker.filteredCategories(
                 pwmApplication,
                 pwmApplication,
                 storedConfiguration,
                 storedConfiguration,
                 navTreeSettings
                 navTreeSettings
         );
         );
         final Locale locale = navTreeSettings.getLocale();
         final Locale locale = navTreeSettings.getLocale();
         final List<NavTreeItem> navigationData = new ArrayList<>();
         final List<NavTreeItem> navigationData = new ArrayList<>();
+        final Map<PwmSettingCategory, List<String>> seenParentsAndProfiles = new HashMap<>();
 
 
-        for ( final PwmSettingCategory loopCategory : categories )
+        for ( final ProfiledCategory profiledCategory : profiledCategories )
         {
         {
-            if ( !loopCategory.hasProfiles() )
+            final PwmSettingCategory loopCategory = profiledCategory.getPwmSettingCategory();
+            if ( profiledCategory.getType() == ProfiledCategory.Type.StaticCategory )
             {
             {
-                // regular category, so output a standard nav tree item
                 navigationData.add( navTreeItemForCategory( loopCategory, locale, null ) );
                 navigationData.add( navTreeItemForCategory( loopCategory, locale, null ) );
             }
             }
-            else
+            else if ( profiledCategory.getType() == ProfiledCategory.Type.Profile )
             {
             {
-                final List<String> profiles = StoredConfigurationUtil.profilesForCategory( loopCategory, storedConfiguration );
-                final boolean topLevelProfileParent = loopCategory.isTopLevelProfile();
-
-                if ( topLevelProfileParent )
-                {
-                    // edit profile option
-                    navigationData.add( navTreeItemForCategory( loopCategory, locale, null ) );
-
-                    {
-                        final PwmSetting profileSetting = loopCategory.getProfileSetting().orElseThrow( IllegalStateException::new );
-                        final NavTreeItem profileEditorInfo = NavTreeItem.builder()
-                                .id( loopCategory.getKey() + "-EDITOR" )
-                                .name( LocaleHelper.getLocalizedMessage( locale, Config.Label_ProfileListEditMenuItem, null ) )
-                                .type( NavTreeItem.NavItemType.profileDefinition )
-                                .profileSetting( profileSetting.getKey() )
-                                .parent( loopCategory.getKey() )
-                                .build();
-                        navigationData.add( profileEditorInfo );
-                    }
+                navigationData.addAll( makeNavTreeItemsForProfile( profiledCategory, locale ) );
+            }
+            else if ( profiledCategory.getType() == ProfiledCategory.Type.CategoryWithProfile )
+            {
+                final String profileId = profiledCategory.getProfile();
+                final PwmSettingCategory parentCategory = loopCategory.getParent();
 
 
-                    for ( final String profileId : profiles )
-                    {
-                        final NavTreeItem profileInfo = navTreeItemForCategory( loopCategory, locale, profileId )
-                                .toBuilder()
-                                .name( profileId.isEmpty() ? "Default" : profileId )
-                                .id( "profile-" + loopCategory.getKey() + "-" + profileId )
-                                .parent( loopCategory.getKey() )
-                                .type( loopCategory.getChildCategories().isEmpty() ? NavTreeItem.NavItemType.category :  NavTreeItem.NavItemType.navigation )
-                                .build();
-                        navigationData.add( profileInfo );
-                    }
-                }
-                else
+                seenParentsAndProfiles.computeIfAbsent( parentCategory, pwmSettingCategory -> new ArrayList<>() );
+                if ( !seenParentsAndProfiles.get( parentCategory ).contains( profileId ) )
                 {
                 {
-                    for ( final String profileId : profiles )
-                    {
-                        navigationData.add( navTreeItemForCategory( loopCategory, locale, profileId ) );
-                    }
+                    seenParentsAndProfiles.get( parentCategory ).add( profileId );
+
+                    final NavTreeItem profileNode = navTreeItemForCategory( parentCategory, locale, profileId )
+                            .toBuilder()
+                            .name( StringUtil.isEmpty( profileId ) ? "Default" : profileId )
+                            .id( "profile-" + parentCategory.getKey() + "-" + profileId )
+                            .parent( parentCategory.getKey() )
+                            .type( parentCategory.getChildCategories().isEmpty() ? NavTreeItem.NavItemType.category :  NavTreeItem.NavItemType.navigation )
+                            .build();
+                    navigationData.add( profileNode );
                 }
                 }
+
+                navigationData.add( navTreeItemForCategory( loopCategory, locale, profiledCategory.getProfile() ) );
             }
             }
         }
         }
 
 
-        return navigationData;
+        return Collections.unmodifiableList( navigationData );
+    }
+
+    private static List<NavTreeItem> makeNavTreeItemsForProfile(
+            final ProfiledCategory profiledCategory,
+            final Locale locale
+    )
+    {
+        final List<NavTreeItem> returnData = new ArrayList<>();
+        final PwmSettingCategory loopCategory = profiledCategory.getPwmSettingCategory();
+
+        // add the category itself
+        returnData.add( navTreeItemForCategory( loopCategory, locale, null ) );
+
+        // add the "edit profiles" category
+        {
+            final PwmSetting profileSetting = loopCategory.getProfileSetting().orElseThrow( IllegalStateException::new );
+            final NavTreeItem profileEditorInfo = NavTreeItem.builder()
+                    .id( loopCategory.getKey() + "-EDITOR" )
+                    .name( LocaleHelper.getLocalizedMessage( locale, Config.Label_ProfileListEditMenuItem, null ) )
+                    .type( NavTreeItem.NavItemType.profileDefinition )
+                    .profileSetting( profileSetting.getKey() )
+                    .parent( loopCategory.getKey() )
+                    .build();
+            returnData.add( profileEditorInfo );
+        }
+
+        return Collections.unmodifiableList( returnData );
     }
     }
 
 
     private static NavTreeItem navTreeItemForCategory(
     private static NavTreeItem navTreeItemForCategory(
@@ -366,6 +353,49 @@ public class NavTreeDataMaker
         return categoryItem.build();
         return categoryItem.build();
     }
     }
 
 
+    private static boolean categoryMatcher(
+            final PwmApplication pwmApplication,
+            final ProfiledCategory profiledCategory,
+            final StoredConfiguration storedConfiguration,
+            final NavTreeSettings navTreeSettings
+    )
+    {
+        final PwmSettingCategory category = profiledCategory.getPwmSettingCategory();
+
+        if ( category.isHidden() )
+        {
+            return false;
+        }
+
+        if ( category == PwmSettingCategory.HTTPS_SERVER )
+        {
+            if ( !pwmApplication.getPwmEnvironment().getFlags().contains( PwmEnvironment.ApplicationFlag.ManageHttps ) )
+            {
+                return false;
+            }
+        }
+
+        final String profileID = profiledCategory.getProfile();
+        for ( final PwmSettingCategory childCategory : category.getChildCategories() )
+        {
+            final ProfiledCategory childProfiledCategory = new ProfiledCategory( childCategory, profileID, ProfiledCategory.Type.CategoryWithProfile );
+            if ( categoryMatcher( pwmApplication, childProfiledCategory, storedConfiguration, navTreeSettings ) )
+            {
+                return true;
+            }
+        }
+
+        for ( final PwmSetting setting : category.getSettings() )
+        {
+            if ( settingMatches( storedConfiguration, setting, profileID, navTreeSettings ) )
+            {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
     private static boolean settingMatches(
     private static boolean settingMatches(
             final StoredConfiguration storedConfiguration,
             final StoredConfiguration storedConfiguration,
             final PwmSetting setting,
             final PwmSetting setting,
@@ -386,7 +416,7 @@ public class NavTreeDataMaker
             }
             }
         }
         }
 
 
-        if ( navTreeSettings.getLevel() > 0 && setting.getLevel() > navTreeSettings.getLevel() )
+        if ( setting.getLevel() > navTreeSettings.getLevel() )
         {
         {
             return false;
             return false;
         }
         }
@@ -425,4 +455,24 @@ public class NavTreeDataMaker
             navigationData.add( 0, navTreeItem );
             navigationData.add( 0, navTreeItem );
         } );
         } );
     }
     }
+
+    @Value
+    private static class ProfiledCategory
+    {
+        private final PwmSettingCategory pwmSettingCategory;
+        private final String profile;
+        private final Type type;
+
+        enum Type
+        {
+            /** Standard (non-profiled) category. */
+            StaticCategory,
+
+            /** Profile root (needs edit menu and list of profile values as NavTree nodes). */
+            Profile,
+
+            /** Category that is part of a profile.  Repeated for each profile value parent. */
+            CategoryWithProfile
+        }
+    }
 }
 }

+ 4 - 1
server/src/main/java/password/pwm/http/servlet/configeditor/data/NavTreeSettings.java

@@ -31,7 +31,10 @@ import java.util.Locale;
 public class NavTreeSettings
 public class NavTreeSettings
 {
 {
     private final boolean modifiedSettingsOnly;
     private final boolean modifiedSettingsOnly;
-    private final int level;
+
+    @Builder.Default
+    private final int level = 2;
+
     private final String filterText;
     private final String filterText;
 
 
     @Builder.Default
     @Builder.Default

+ 1 - 0
server/src/main/resources/password/pwm/config/PwmSetting.xml

@@ -1073,6 +1073,7 @@
         <default>
         <default>
             <value><![CDATA[Your username is @User:ID@]]></value>
             <value><![CDATA[Your username is @User:ID@]]></value>
         </default>
         </default>
+        <flag>MacroSupport</flag>
     </setting>
     </setting>
     <setting hidden="false" key="sms.updateProfile.token.message" level="1" required="true">
     <setting hidden="false" key="sms.updateProfile.token.message" level="1" required="true">
         <default>
         <default>

+ 3 - 3
server/src/main/resources/password/pwm/i18n/Config.properties

@@ -58,9 +58,9 @@ Display_ConfigOpenInfo=<p>@PwmAppName@ is currently in <b>configuration</b> mode
 Display_EditorLDAPSizeExceeded=Search results exceeded maximum search size.  Only this display is affected by these search limits.  Application behavior is not constrained by this search limit so additional users will match beyond those shown here.
 Display_EditorLDAPSizeExceeded=Search results exceeded maximum search size.  Only this display is affected by these search limits.  Application behavior is not constrained by this search limit so additional users will match beyond those shown here.
 Display_LdapPermissionRecommendations=<p>This report shows recommended LDAP permission requirements for the current configuration.  Depending on your LDAP directory type, these may be referred to as permissions, rights, or ACLs (Access Control List).</p><p?>These recommendations should be applied with caution and with an understanding of the security model of your specific LDAP directory environment.  <b>The suggested permissions may not neccessarily be appropriate for your environment.</b>  The access levels <i>read</i> and <i>write</i> are generalizations.  Your LDAP directory may use different permission types.</p><p>There may be additional permissions required that do not appear on this report. For example, permissions required to resolve macro expressions are not included.</p>
 Display_LdapPermissionRecommendations=<p>This report shows recommended LDAP permission requirements for the current configuration.  Depending on your LDAP directory type, these may be referred to as permissions, rights, or ACLs (Access Control List).</p><p?>These recommendations should be applied with caution and with an understanding of the security model of your specific LDAP directory environment.  <b>The suggested permissions may not neccessarily be appropriate for your environment.</b>  The access levels <i>read</i> and <i>write</i> are generalizations.  Your LDAP directory may use different permission types.</p><p>There may be additional permissions required that do not appear on this report. For example, permissions required to resolve macro expressions are not included.</p>
 Display_Wordlists_Description=Word lists and seed lists can be uploaded using this page or configured to import from a remote URL using the settings <code>@PwmSettingReference:pwm.wordlist.location@</code> and <code>@PwmSettingReference:pwm.seedlist.location@</code>.
 Display_Wordlists_Description=Word lists and seed lists can be uploaded using this page or configured to import from a remote URL using the settings <code>@PwmSettingReference:pwm.wordlist.location@</code> and <code>@PwmSettingReference:pwm.seedlist.location@</code>.
-Display_SettingFilter_Level_0=Required
-Display_SettingFilter_Level_1=Standard / Common
-Display_SettingFilter_Level_2=Advanced
+Display_SettingFilter_Level_0=Basic
+Display_SettingFilter_Level_1=Intermediate
+Display_SettingFilter_Level_2=All Settings
 Display_SettingNavigationSeparator=\u0020\u21e8\u0020
 Display_SettingNavigationSeparator=\u0020\u21e8\u0020
 Display_SettingNavigationNullProfile=[profile]
 Display_SettingNavigationNullProfile=[profile]
 Display_RememberLogin=Remember password for %1%.
 Display_RememberLogin=Remember password for %1%.

+ 1 - 1
server/src/main/resources/password/pwm/i18n/PwmSetting.properties

@@ -512,7 +512,7 @@ Setting_Description_newUser.createContext=Specify the LDAP context where you wou
 Setting_Description_newUser.ldapProfile=Specify the LDAP profile where you would like @PwmAppName@ to create new users. If blank, the default LDAP profile will be used when creating new user.
 Setting_Description_newUser.ldapProfile=Specify the LDAP profile where you would like @PwmAppName@ to create new users. If blank, the default LDAP profile will be used when creating new user.
 Setting_Description_newUser.deleteOnFail=Enable this option to have @PwmAppName@ delete the new user account if the creation fails for some reason.  It deletes the (potentially partially-created) "broken" account in LDAP.
 Setting_Description_newUser.deleteOnFail=Enable this option to have @PwmAppName@ delete the new user account if the creation fails for some reason.  It deletes the (potentially partially-created) "broken" account in LDAP.
 Setting_Description_newUser.email.verification=Enable this option to have @PwmAppName@ send an email to the new user's email address before it creates the account.  The new user must verify receipt of the email before @PwmAppName@ creates the account. All of your email settings must also be filled out before this will work. Testing the email settings should take place to verify that this email will be sent.
 Setting_Description_newUser.email.verification=Enable this option to have @PwmAppName@ send an email to the new user's email address before it creates the account.  The new user must verify receipt of the email before @PwmAppName@ creates the account. All of your email settings must also be filled out before this will work. Testing the email settings should take place to verify that this email will be sent.
-Setting_Description_newUser.enable=Enable this option to allow @PwmAppName@ to display the new user registration.
+Setting_Description_newUser.enable=Set this option to allow @PwmAppName@ to enable the new user registration module and show new user registration as an option on the public menu and login pages.
 Setting_Description_newUser.form=Specify the New User form creation attributes and fields. This is used to determine what information will need to be filled in before submitting the new user form to create the new user.
 Setting_Description_newUser.form=Specify the New User form creation attributes and fields. This is used to determine what information will need to be filled in before submitting the new user form to create the new user.
 Setting_Description_newUser.logoutAfterCreation=Enable this option to force the new user to log out (and send him to the logoutURL) after the account has been created.<br/><br/>Leave this option disabled (default) to make @PwmAppName@ automatically login the new user.
 Setting_Description_newUser.logoutAfterCreation=Enable this option to force the new user to log out (and send him to the logoutURL) after the account has been created.<br/><br/>Leave this option disabled (default) to make @PwmAppName@ automatically login the new user.
 Setting_Description_newUser.minimumWaitTime=Specify a delay time during a new user creation. @PwmAppName@ delays the creation of the user for at least this amount of time before forwarding the user to the next activity.  <br/><br/>Specify the value in seconds.
 Setting_Description_newUser.minimumWaitTime=Specify a delay time during a new user creation. @PwmAppName@ delays the creation of the user for at least this amount of time before forwarding the user to the next activity.  <br/><br/>Specify the value in seconds.

+ 82 - 44
webapp/src/main/webapp/WEB-INF/jsp/configeditor.jsp

@@ -47,21 +47,11 @@
             <div id="header-title">
             <div id="header-title">
                 <span id="currentPageDisplay"></span>
                 <span id="currentPageDisplay"></span>
                 <span style="visibility: hidden" id="working_icon" class="headerIcon pwm-icon pwm-icon-cog pwm-icon-spin"></span>
                 <span style="visibility: hidden" id="working_icon" class="headerIcon pwm-icon pwm-icon-cog pwm-icon-spin"></span>
-                <div class="headerIcon" id="cancelButton_icon" title="<pwm:display key="Tooltip_CancelEditorButton" bundle="Config"/>">
-                    <span class="pwm-icon pwm-icon-times"></span>
-                </div>
-                <div class="headerIcon" id="saveButton_icon" title="<pwm:display key="Tooltip_SaveEditorButton" bundle="Config"/>">
-                    <span class="pwm-icon pwm-icon-save"></span>
-                </div>
-                <div class="headerIcon" id="setPassword_icon" title="<pwm:display key="Tooltip_SetConfigPasswordButton" bundle="Config"/>">
-                    <span class="pwm-icon pwm-icon-key"></span>
-                </div>
+
                 <div class="headerIcon" id="referenceDoc_icon" title="<pwm:display key="Tooltip_OpenReferenceDocButton" bundle="Config"/>">
                 <div class="headerIcon" id="referenceDoc_icon" title="<pwm:display key="Tooltip_OpenReferenceDocButton" bundle="Config"/>">
                     <span class="pwm-icon pwm-icon-book"></span>
                     <span class="pwm-icon pwm-icon-book"></span>
                 </div>
                 </div>
-                <div class="headerIcon" id="macroDoc_icon" title="<pwm:display key="Tooltip_OpenMacroHelpButton" bundle="Config"/>">
-                    <span class="pwm-icon pwm-icon-magic"></span>
-                </div>
+
                 <span id="idle_status" class="editorIdleStatus">
                 <span id="idle_status" class="editorIdleStatus">
                     <%--idle timeout text --%>
                     <%--idle timeout text --%>
                 </span>
                 </span>
@@ -69,53 +59,101 @@
         </div>
         </div>
     </div>
     </div>
     <div id="centerbody-config" class="centerbody-config" ng-app="configeditor.module" ng-controller="ConfigEditorController as $ctrl">
     <div id="centerbody-config" class="centerbody-config" ng-app="configeditor.module" ng-controller="ConfigEditorController as $ctrl">
+
         <div id="settingSearchPanel">
         <div id="settingSearchPanel">
-            <table class="noborder settingSearchPanelTable">
-                <colgroup>
-                    <col class="settingSearchPanelTable_Col1">
-                    <col class="settingSearchPanelTable_Col2">
-                    <col class="settingSearchPanelTable_Col3">
-                </colgroup>
-                <tr>
-                    <td>
-                        <span id="settingSearchIcon" class="pwm-icon pwm-icon-search" title="<pwm:display key="Tooltip_IconSettingsSearch" bundle="Config"/>"></span>
-                    </td>
-                    <td>
-                        <input placeholder="<pwm:display key="Placeholder_Search"/>" type="search" id="homeSettingSearch" name="homeSettingSearch" class="inputfield" placeholder="Search Configuration" <pwm:autofocus/>/>
-                    </td>
-                    <td>
-                        <div style="margin-top:5px; width:20px; max-width: 20px;">
-                            <div id="indicator-searching" style="display: none">
-                                <span style="" class="pwm-icon pwm-icon-lg pwm-icon-spin pwm-icon-spinner"></span>
-                            </div>
-                            <div id="indicator-noResults" style="display: none;" title="<pwm:display key="Tooltip_IconSearchNoResults" bundle="Config"/>">
-                                <span style="color: #ffcd59;" class="pwm-icon pwm-icon-lg pwm-icon-ban"></span>
+            <div style="float:left; width: 49%">
+                <table class="noborder settingSearchPanelTable">
+                    <colgroup>
+                        <col class="settingSearchPanelTable_Col1">
+                        <col class="settingSearchPanelTable_Col2">
+                        <col class="settingSearchPanelTable_Col3">
+                    </colgroup>
+                    <tr>
+                        <td>
+                            <span id="settingSearchIcon" class="pwm-icon pwm-icon-search" title="<pwm:display key="Tooltip_IconSettingsSearch" bundle="Config"/>"></span>
+                        </td>
+                        <td>
+                            <input placeholder="<pwm:display key="Placeholder_Search"/>" type="search" id="homeSettingSearch" name="homeSettingSearch" class="inputfield" placeholder="Search Configuration" autocomplete="off" <pwm:autofocus/>/>
+                        </td>
+                        <td>
+                            <div style="margin-top:5px; width:20px; max-width: 20px;">
+                                <div id="indicator-searching" class="hidden">
+                                    <span style="" class="pwm-icon pwm-icon-lg pwm-icon-spin pwm-icon-spinner"></span>
+                                </div>
+                                <div id="indicator-noResults" class="hidden" title="<pwm:display key="Tooltip_IconSearchNoResults" bundle="Config"/>">
+                                    <span style="color: #ffcd59;" class="pwm-icon pwm-icon-lg pwm-icon-ban"></span>
+                                </div>
                             </div>
                             </div>
-                        </div>
-                    </td>
-                </tr>
-            </table>
-            <div id="searchResults" style="visibility: hidden">
+                        </td>
+                    </tr>
+                </table>
+            </div>
+            <div style="float: right;">
+                <table class="noborder nopadding nomargin" width="5%">
+                    <tr>
+                        <td>
+                            <a id="macroDoc_icon" class="configLightText">Macro Help</a><br/>
+                            <a id="setPassword_icon" class="configLightText">Config Password</a>
+                        </td>
+                        <td>
+                            <label title="<pwm:display key="Tooltip_SaveEditorButton" bundle="Config"/>">
+                                <button id="saveButton_icon">Save</button>
+                            </label>
+                        </td>
+                        <td>
+                            <label title="<pwm:display key="Tooltip_CancelEditorButton" bundle="Config"/>">
+                                <button  id="cancelButton_icon">Cancel</button>
+                            </label>
+                        </td>
+
+                    </tr>
+                </table>
+            </div>
+            <div id="searchResults" class="hidden">
                 <%-- search results inserted here --%>
                 <%-- search results inserted here --%>
             </div>
             </div>
         </div>
         </div>
-        <div id="navigationTreeWrapper" style="display:none">
+        <div id="navigationTreeWrapper">
             <div id="navigationTree">
             <div id="navigationTree">
                 <%-- navtree goes here --%>
                 <%-- navtree goes here --%>
             </div>
             </div>
+
+
             <div id="navTreeExpanderButtons">
             <div id="navTreeExpanderButtons">
-                <span id="button-navigationExpandAll" class="pwm-icon pwm-icon-plus-square" title="<pwm:display key="Tooltip_IconExpandAll" bundle="Config"/>"></span>
-                &nbsp;&nbsp;
-                <span id="button-navigationCollapseAll" class="pwm-icon pwm-icon-minus-square" title="<pwm:display key="Tooltip_IconCollapseAll" bundle="Config"/>"></span>
-                &nbsp;&nbsp;
-                <div class="headerIcon" id="settingFilter_icon" title="<pwm:display key="Tooltip_IconFilterSettings" bundle="Config"/>">
-                    <span class="pwm-icon pwm-icon-filter"></span>
+                <div>
+                    <span id="button-navigationExpandAll" class="pwm-icon pwm-icon-plus-square" title="<pwm:display key="Tooltip_IconExpandAll" bundle="Config"/>"></span>
+                    &nbsp;&nbsp;
+                    <span id="button-navigationCollapseAll" class="pwm-icon pwm-icon-minus-square" title="<pwm:display key="Tooltip_IconCollapseAll" bundle="Config"/>"></span>
+                </div>
+                <div>
+                    <div class="toggleWrapper">
+                        <div class="toggle" id="radio-setting-level">
+                            <input type="radio" name="radio-setting-level" value="2" id="radio-setting-level-2" />
+                            <label for="radio-setting-level-2"><pwm:display key="Display_SettingFilter_Level_2" bundle="Config"/></label>
+                            <input type="radio" name="radio-setting-level" value="1" id="radio-setting-level-1" />
+                            <label for="radio-setting-level-1"><pwm:display key="Display_SettingFilter_Level_1" bundle="Config"/></label>
+                            <input type="radio" name="radio-setting-level" value="0" id="radio-setting-level-0" />
+                            <label for="radio-setting-level-0"><pwm:display key="Display_SettingFilter_Level_0" bundle="Config"/></label>
+                        </div>
+
+                    </div>
+                    <div style="text-align: center; display: inline-block">
+                        <div class="toggle" id="radio-modified-only" >
+                            <input type="radio" name="radio-modified-only" value="all" id="radio-modified-only-all" />
+                            <label for="radio-modified-only-all">All Settings</label>
+                            <input type="radio" name="radio-modified-only" value="modified" id="radio-modified-only-modified" />
+                            <label for="radio-modified-only-modified">Modified Only</label>
+                        </div>
+                    </div>
                 </div>
                 </div>
             </div>
             </div>
         </div>
         </div>
         <div id="settingsPanel">
         <div id="settingsPanel">
             <%-- settings content goes here --%>
             <%-- settings content goes here --%>
         </div>
         </div>
+        <div id="infoPanel">
+            <%-- info panel goes here --%>
+        </div>
         <div id="config-infoPanel"></div>
         <div id="config-infoPanel"></div>
     </div>
     </div>
     <br/><br/>
     <br/><br/>

+ 195 - 211
webapp/src/main/webapp/public/resources/js/configeditor.js

@@ -29,6 +29,87 @@ var PWM_SETTINGS = PWM_SETTINGS || {};
 PWM_VAR['outstandingOperations'] = 0;
 PWM_VAR['outstandingOperations'] = 0;
 PWM_VAR['skippedSettingCount'] = 0;
 PWM_VAR['skippedSettingCount'] = 0;
 
 
+PWM_CFGEDIT.syntaxFunctionMap = {
+    FORM: function (settingKey) {
+        FormTableHandler.init(settingKey, {});
+    },
+    OPTIONLIST: function (settingKey) {
+        OptionListHandler.init(settingKey);
+    },
+    CUSTOMLINKS: function (settingKey) {
+        CustomLinkHandler.init(settingKey, {});
+    },
+    EMAIL: function (settingKey) {
+        EmailTableHandler.init(settingKey);
+    },
+    ACTION: function (settingKey) {
+        ActionHandler.init(settingKey);
+    },
+    PASSWORD: function (settingKey) {
+        ChangePasswordHandler.init(settingKey);
+    },
+    NAMED_SECRET: function (settingKey) {
+        NamedSecretHandler.init(settingKey);
+    },
+    NUMERIC: function (settingKey) {
+        NumericValueHandler.init(settingKey);
+    },
+    DURATION: function (settingKey) {
+        DurationValueHandler.init(settingKey);
+    },
+    DURATION_ARRAY: function (settingKey) {
+        DurationArrayValueHandler.init(settingKey);
+    },
+    STRING: function (settingKey) {
+        StringValueHandler.init(settingKey);
+    },
+    TEXT_AREA: function (settingKey) {
+        TextAreaValueHandler.init(settingKey)
+    },
+    SELECT: function (settingKey) {
+        SelectValueHandler.init(settingKey);
+    },
+    BOOLEAN: function (settingKey) {
+        BooleanHandler.init(settingKey);
+    },
+    LOCALIZED_STRING_ARRAY: function (settingKey) {
+        MultiLocaleTableHandler.initMultiLocaleTable(settingKey);
+    },
+    STRING_ARRAY: function (settingKey) {
+        StringArrayValueHandler.init(settingKey);
+    },
+    PROFILE: function (settingKey) {
+        StringArrayValueHandler.init(settingKey);
+    },
+    LOCALIZED_STRING: function (settingKey) {
+        LocalizedStringValueHandler.init(settingKey);
+    },
+    LOCALIZED_TEXT_AREA: function (settingKey) {
+        LocalizedStringValueHandler.init(settingKey);
+    },
+    CHALLENGE: function (settingKey) {
+        ChallengeSettingHandler.init(settingKey);
+    },
+    X509CERT: function (settingKey) {
+        X509CertificateHandler.init(settingKey);
+    },
+    PRIVATE_KEY: function (settingKey) {
+        PrivateKeyHandler.init(settingKey);
+    },
+    FILE: function (settingKey) {
+        FileValueHandler.init(settingKey);
+    },
+    VERIFICATION_METHOD: function (settingKey) {
+        VerificationMethodHandler.init(settingKey);
+    },
+    REMOTE_WEB_SERVICE: function (settingKey) {
+        RemoteWebServiceHandler.init(settingKey);
+    },
+    USER_PERMISSION: function (settingKey) {
+        UserPermissionHandler.init(settingKey);
+    }
+};
+
 
 
 PWM_CFGEDIT.readSetting = function(keyName, valueWriter) {
 PWM_CFGEDIT.readSetting = function(keyName, valueWriter) {
     var modifiedOnly = PWM_CFGEDIT.readNavigationFilters()['modifiedSettingsOnly'];
     var modifiedOnly = PWM_CFGEDIT.readNavigationFilters()['modifiedSettingsOnly'];
@@ -313,6 +394,8 @@ function handleResetClick(settingKey) {
 }
 }
 
 
 PWM_CFGEDIT.initConfigEditor = function(nextFunction) {
 PWM_CFGEDIT.initConfigEditor = function(nextFunction) {
+    PWM_CFGEDIT.applyStoredSettingFilterPrefs();
+
     PWM_MAIN.addEventHandler('homeSettingSearch',['input','focus'],function(){PWM_CFGEDIT.processSettingSearch(PWM_MAIN.getObject('searchResults'));});
     PWM_MAIN.addEventHandler('homeSettingSearch',['input','focus'],function(){PWM_CFGEDIT.processSettingSearch(PWM_MAIN.getObject('searchResults'));});
     PWM_MAIN.addEventHandler('button-navigationExpandAll','click',function(){PWM_VAR['navigationTree'].expandAll()});
     PWM_MAIN.addEventHandler('button-navigationExpandAll','click',function(){PWM_VAR['navigationTree'].expandAll()});
     PWM_MAIN.addEventHandler('button-navigationCollapseAll','click',function(){PWM_VAR['navigationTree'].collapseAll()});
     PWM_MAIN.addEventHandler('button-navigationCollapseAll','click',function(){PWM_VAR['navigationTree'].collapseAll()});
@@ -324,7 +407,6 @@ PWM_CFGEDIT.initConfigEditor = function(nextFunction) {
         PWM_MAIN.newWindowOpen(PWM_GLOBAL['url-context'] + '/public/reference/','referencedoc');
         PWM_MAIN.newWindowOpen(PWM_GLOBAL['url-context'] + '/public/reference/','referencedoc');
     });
     });
     PWM_MAIN.addEventHandler('macroDoc_icon','click',function(){ PWM_CFGEDIT.showMacroHelp(); });
     PWM_MAIN.addEventHandler('macroDoc_icon','click',function(){ PWM_CFGEDIT.showMacroHelp(); });
-    PWM_MAIN.addEventHandler('settingFilter_icon','click',function(){ PWM_CFGEDIT.showSettingFilter(); });
 
 
     PWM_MAIN.addEventHandler('button-closeMenu','click',function(){
     PWM_MAIN.addEventHandler('button-closeMenu','click',function(){
         PWM_CFGEDIT.closeMenuPanel();
         PWM_CFGEDIT.closeMenuPanel();
@@ -332,6 +414,12 @@ PWM_CFGEDIT.initConfigEditor = function(nextFunction) {
     PWM_MAIN.addEventHandler('button-openMenu','click',function(){
     PWM_MAIN.addEventHandler('button-openMenu','click',function(){
         PWM_CFGEDIT.openMenuPanel();
         PWM_CFGEDIT.openMenuPanel();
     });
     });
+    PWM_MAIN.addEventHandler('radio-setting-level','change',function(){
+        PWM_CFGEDIT.handleSettingsFilterLevelRadioClick();
+    });
+    PWM_MAIN.addEventHandler('radio-modified-only','change',function(){
+        PWM_CFGEDIT.handleModifiedSettingsRadioClick();
+    });
 
 
 
 
     PWM_CONFIG.heartbeatCheck();
     PWM_CONFIG.heartbeatCheck();
@@ -421,9 +509,9 @@ PWM_CFGEDIT.processSettingSearch = function(destinationDiv) {
     PWM_VAR['settingSearchIteration'] = iteration;
     PWM_VAR['settingSearchIteration'] = iteration;
 
 
     var resetDisplay = function() {
     var resetDisplay = function() {
-        PWM_MAIN.getObject('indicator-noResults').style.display = 'none';
-        PWM_MAIN.getObject('indicator-searching').style.display = 'none';
-        destinationDiv.style.visibility = 'hidden';
+        PWM_MAIN.addCssClass('indicator-noResults',"hidden");
+        PWM_MAIN.addCssClass('indicator-searching',"hidden");
+        PWM_MAIN.addCssClass(destinationDiv.id,"hidden");
         destinationDiv.innerHTML = '';
         destinationDiv.innerHTML = '';
     };
     };
 
 
@@ -458,14 +546,13 @@ PWM_CFGEDIT.processSettingSearch = function(destinationDiv) {
             var resultCount = 0;
             var resultCount = 0;
             var elapsedTime = (new Date().getTime()) - startTime;
             var elapsedTime = (new Date().getTime()) - startTime;
             if (PWM_MAIN.JSLibrary.isEmpty(data['data'])) {
             if (PWM_MAIN.JSLibrary.isEmpty(data['data'])) {
-                PWM_MAIN.getObject('indicator-noResults').style.display = 'inline';
+                PWM_MAIN.removeCssClass('indicator-noResults','hidden')
                 console.log('search #' + iteration + ', 0 results, ' + elapsedTime + 'ms');
                 console.log('search #' + iteration + ', 0 results, ' + elapsedTime + 'ms');
             } else {
             } else {
-                for (var categoryIter in data['data']) {
-                    var category = data['data'][categoryIter];
+                PWM_MAIN.addCssClass('indicator-noResults','hidden')
+                PWM_MAIN.JSLibrary.forEachInObject(data['data'], function (categoryIter, category) {
                     bodyText += '<div class="panel-searchResultCategory">' + categoryIter + '</div>';
                     bodyText += '<div class="panel-searchResultCategory">' + categoryIter + '</div>';
-                    for (var settingIter in category) {
-                        var setting = category[settingIter];
+                    PWM_MAIN.JSLibrary.forEachInObject(category, function (settingIter, setting) {
                         var profileID = setting['profile'];
                         var profileID = setting['profile'];
                         var linkID = 'link-' + setting['category'] + '-' + settingIter + (profileID ? profileID : '');
                         var linkID = 'link-' + setting['category'] + '-' + settingIter + (profileID ? profileID : '');
                         var settingID = "search_" + (profileID ? profileID + '_' : '') + settingIter;
                         var settingID = "search_" + (profileID ? profileID + '_' : '') + settingIter;
@@ -477,44 +564,40 @@ PWM_CFGEDIT.processSettingSearch = function(destinationDiv) {
                         }
                         }
                         bodyText += '</div>';
                         bodyText += '</div>';
                         resultCount++;
                         resultCount++;
-                    }
-                }
+                    });
+                });
                 console.log('search #' + iteration + ', ' + resultCount + ' results, ' + elapsedTime + 'ms');
                 console.log('search #' + iteration + ', ' + resultCount + ' results, ' + elapsedTime + 'ms');
-                destinationDiv.style.visibility = 'visible';
+                PWM_MAIN.removeCssClass(destinationDiv.id, "hidden");
                 destinationDiv.innerHTML = bodyText;
                 destinationDiv.innerHTML = bodyText;
-                for (var categoryIter in data['data']) {
-                    var category = data['data'][categoryIter];
-                    for (var iter in category) {
-                        (function (settingKey) {
-                            var setting = category[settingKey];
-                            var profileID = setting['profile'];
-                            var settingID = "search_" + (profileID ? profileID + '_' : '') + settingKey;
-                            var value = setting['value'];
-                            var toolBody = '<span style="font-weight: bold">Setting</span>';
-                            toolBody += '<br/>' + PWM_SETTINGS['settings'][settingKey]['label'] + '<br/><br/>';
-                            toolBody += '<span style="font-weight: bold">Description</span>';
-                            toolBody += '<br/>' + PWM_SETTINGS['settings'][settingKey]['description'] + '<br/><br/>';
-                            toolBody += '<span style="font-weight: bold">Value</span>';
-                            toolBody += '<br/>' + value.replace('\n', '<br/>') + '<br/>';
-                            PWM_MAIN.showDijitTooltip({
-                                id: settingID + '_popup',
-                                text: toolBody,
-                                width: 500
-                            });
-                            var linkID = 'link-' + setting['category'] + '-' + settingKey + (profileID ? profileID : '');
-                            PWM_MAIN.addEventHandler(linkID ,'click',function(){
-                                resetDisplay();
-                                PWM_MAIN.Preferences.writeSessionStorage('configEditor-lastSelected',{
-                                    type:'category',
-                                    category:setting['category'],
-                                    setting:settingKey,
-                                    profile:profileID
-                                });
-                                PWM_CFGEDIT.gotoSetting(setting['category'],settingKey,profileID);
+                PWM_MAIN.JSLibrary.forEachInObject(data['data'], function (categoryIter, category) {
+                    PWM_MAIN.JSLibrary.forEachInObject(category, function (settingKey, setting) {
+                        var profileID = setting['profile'];
+                        var settingID = "search_" + (profileID ? profileID + '_' : '') + settingKey;
+                        var value = setting['value'];
+                        var toolBody = '<span style="font-weight: bold">Setting</span>';
+                        toolBody += '<br/>' + PWM_SETTINGS['settings'][settingKey]['label'] + '<br/><br/>';
+                        toolBody += '<span style="font-weight: bold">Description</span>';
+                        toolBody += '<br/>' + PWM_SETTINGS['settings'][settingKey]['description'] + '<br/><br/>';
+                        toolBody += '<span style="font-weight: bold">Value</span>';
+                        toolBody += '<br/>' + value.replace('\n', '<br/>') + '<br/>';
+                        PWM_MAIN.showDijitTooltip({
+                            id: settingID + '_popup',
+                            text: toolBody,
+                            width: 500
+                        });
+                        var linkID = 'link-' + setting['category'] + '-' + settingKey + (profileID ? profileID : '');
+                        PWM_MAIN.addEventHandler(linkID, 'click', function () {
+                            resetDisplay();
+                            PWM_MAIN.Preferences.writeSessionStorage('configEditor-lastSelected', {
+                                type: 'category',
+                                category: setting['category'],
+                                setting: settingKey,
+                                profile: profileID
                             });
                             });
-                        }(iter));
-                    }
-                }
+                            PWM_CFGEDIT.gotoSetting(setting['category'], settingKey, profileID);
+                        });
+                    });
+                });
             }
             }
         }
         }
     };
     };
@@ -522,13 +605,13 @@ PWM_CFGEDIT.processSettingSearch = function(destinationDiv) {
     validationProps['serviceURL'] = url;
     validationProps['serviceURL'] = url;
     validationProps['readDataFunction'] = function(){
     validationProps['readDataFunction'] = function(){
         resetDisplay();
         resetDisplay();
-        PWM_MAIN.getObject('indicator-searching').style.display = 'inline';
+        PWM_MAIN.removeCssClass('indicator-searching','hidden');
 
 
         var value = readSearchTerm();
         var value = readSearchTerm();
         return {search:value,key:value};
         return {search:value,key:value};
     };
     };
     validationProps['completeFunction'] = function() {
     validationProps['completeFunction'] = function() {
-        PWM_MAIN.getObject('indicator-searching').style.display = 'none';
+        PWM_MAIN.addCssClass('indicator-searching','hidden');
     };
     };
     validationProps['processResultsFunction'] = loadFunction;
     validationProps['processResultsFunction'] = loadFunction;
     PWM_MAIN.pwmFormValidator(validationProps);
     PWM_MAIN.pwmFormValidator(validationProps);
@@ -811,24 +894,26 @@ PWM_CFGEDIT.selectTemplate = function(newTemplate) {
 
 
 PWM_CFGEDIT.loadMainPageBody = function() {
 PWM_CFGEDIT.loadMainPageBody = function() {
 
 
-    PWM_CFGEDIT.drawNavigationMenu();
+    var drawSettingsFunction = function () {
+        var lastSelected = PWM_MAIN.Preferences.readSessionStorage('configEditor-lastSelected', null);
+        if (lastSelected) {
+            PWM_CFGEDIT.dispatchNavigationItem(lastSelected);
+        } else {
+            PWM_CFGEDIT.dispatchNavigationItem({id: 'TEMPLATES', type: 'category', category: 'TEMPLATES'});
+        }
 
 
-    var lastSelected = PWM_MAIN.Preferences.readSessionStorage('configEditor-lastSelected',null);
-    if (lastSelected) {
-        PWM_CFGEDIT.dispatchNavigationItem(lastSelected);
-    } else {
-        PWM_CFGEDIT.dispatchNavigationItem({id:'TEMPLATES',type:'category',category:'TEMPLATES'});
+        require(["dojo/io-query"], function (ioQuery) {
+            var uri = window.location.href;
+            var queryString = uri.substring(uri.indexOf("?") + 1, uri.length);
+            var queryParams = ioQuery.queryToObject(queryString);
+            if (queryParams['processAction'] === 'gotoSetting') {
+                PWM_CFGEDIT.gotoSetting(queryParams['category'], queryParams['settingKey'], queryParams['profile']);
+                return;
+            }
+        });
     }
     }
 
 
-    require(["dojo/io-query"],function(ioQuery){
-        var uri = window.location.href;
-        var queryString = uri.substring(uri.indexOf("?") + 1, uri.length);
-        var queryParams = ioQuery.queryToObject(queryString);
-        if (queryParams['processAction'] === 'gotoSetting') {
-            PWM_CFGEDIT.gotoSetting(queryParams['category'],queryParams['settingKey'],queryParams['profile']);
-            return;
-        }
-    });
+    PWM_CFGEDIT.drawNavigationMenu( drawSettingsFunction );
 };
 };
 
 
 PWM_CFGEDIT.displaySettingsCategory = function(category) {
 PWM_CFGEDIT.displaySettingsCategory = function(category) {
@@ -954,115 +1039,14 @@ PWM_CFGEDIT.initSettingDisplay = function(setting, options) {
         handleResetClick(settingKey);
         handleResetClick(settingKey);
     });
     });
 
 
-    switch (setting['syntax']) {
-        case 'FORM':
-            FormTableHandler.init(settingKey,{});
-            break;
-
-        case 'OPTIONLIST':
-            OptionListHandler.init(settingKey);
-            break;
-
-        case 'CUSTOMLINKS':
-            CustomLinkHandler.init(settingKey, {});
-            break;
-
-        case 'EMAIL':
-            EmailTableHandler.init(settingKey);
-            break;
-
-        case 'ACTION':
-            ActionHandler.init(settingKey);
-            break;
-
-        case 'PASSWORD':
-            ChangePasswordHandler.init(settingKey);
-            break;
-
-        case 'NAMED_SECRET':
-            NamedSecretHandler.init(settingKey);
-            break;
-
-        case 'NUMERIC':
-            NumericValueHandler.init(settingKey);
-            break;
-
-        case 'DURATION':
-            DurationValueHandler.init(settingKey);
-            break;
-
-        case 'DURATION_ARRAY':
-            DurationArrayValueHandler.init(settingKey);
-            break;
-
-        case 'STRING':
-            StringValueHandler.init(settingKey);
-            break;
+    var syntax = setting['syntax'];
+    var syntaxFunction = PWM_CFGEDIT.syntaxFunctionMap[syntax];
+    syntaxFunction(settingKey);
 
 
-        case 'TEXT_AREA':
-            TextAreaValueHandler.init(settingKey);
-            break;
-
-        case 'SELECT':
-            SelectValueHandler.init(settingKey);
-            break;
-
-        case 'BOOLEAN':
-            BooleanHandler.init(settingKey);
-            break;
-
-        case 'LOCALIZED_STRING_ARRAY':
-            MultiLocaleTableHandler.initMultiLocaleTable(settingKey);
-            break;
-
-        case 'STRING_ARRAY':
-        case 'PROFILE':
-            StringArrayValueHandler.init(settingKey);
-            break;
-
-        case 'LOCALIZED_STRING':
-        case 'LOCALIZED_TEXT_AREA':
-            LocalizedStringValueHandler.init(settingKey);
-            break;
-
-        case 'USER_PERMISSION':
-            UserPermissionHandler.init(settingKey);
-            break;
-
-        case 'CHALLENGE':
-            ChallengeSettingHandler.init(settingKey);
-            break;
-
-        case 'X509CERT':
-            X509CertificateHandler.init(settingKey);
-            break;
-
-        case 'PRIVATE_KEY':
-            PrivateKeyHandler.init(settingKey);
-            break;
-
-        case 'FILE':
-            FileValueHandler.init(settingKey);
-            break;
-
-        case 'VERIFICATION_METHOD':
-            VerificationMethodHandler.init(settingKey);
-            break;
-
-        case 'REMOTE_WEB_SERVICE':
-            RemoteWebServiceHandler.init(settingKey);
-            break;
-
-        case 'NONE':
-            break;
-
-        default:
-            console.log ('unknown setting syntax type: ' + setting['syntax']);
-
-    }
 };
 };
 
 
-PWM_CFGEDIT.drawNavigationMenu = function() {
+PWM_CFGEDIT.drawNavigationMenu = function(nextFunction) {
+    console.log('drawNavigationMenu')
     PWM_MAIN.getObject('navigationTree').innerHTML = '';
     PWM_MAIN.getObject('navigationTree').innerHTML = '';
     PWM_MAIN.setStyle('navigationTreeWrapper','display','none');
     PWM_MAIN.setStyle('navigationTreeWrapper','display','none');
 
 
@@ -1128,7 +1112,10 @@ PWM_CFGEDIT.drawNavigationMenu = function() {
     PWM_MAIN.ajaxRequest(url,function(data){
     PWM_MAIN.ajaxRequest(url,function(data){
         var menuTreeData = data['data'];
         var menuTreeData = data['data'];
         makeTreeFunction(menuTreeData);
         makeTreeFunction(menuTreeData);
-    },{content:filterParams});
+        if (nextFunction) {
+            nextFunction();
+        }
+    },{content:filterParams,preventCache:true});
 };
 };
 
 
 PWM_CFGEDIT.readNavigationFilters = function() {
 PWM_CFGEDIT.readNavigationFilters = function() {
@@ -1259,52 +1246,30 @@ PWM_CFGEDIT.displaySettingHelp = function(settingKey) {
     }
     }
 };
 };
 
 
-PWM_CFGEDIT.showSettingFilter = function() {
-    var currentValues = PWM_CFGEDIT.readNavigationFilters();
-
-    var dialogBody = '<div><form id="form-settingFilter"><table class="" style="table-layout: fixed">';
-    dialogBody += '<tr><td>Setting Level</td><td><label>';
-    dialogBody += '<input type="range" min="0" max="2" name="input-settingLevel" id="input-settingLevel" value="' + currentValues['level'] + '" style="width:100px"/>';
-    dialogBody += '<span id="panel-settingLevelDescription"></span></label></td></tr>';
-    dialogBody += '<tr><td>Modified</td><td>';
-    dialogBody += '<input type="radio" name="input-modifiedSettingsOnly" id="input-modifiedSettingsOnly-all" ' + (!currentValues['modifiedSettingsOnly'] ? 'checked' : '') + '>All';
-    dialogBody += '<input type="radio" name="input-modifiedSettingsOnly" id="input-modifiedSettingsOnly-modified" ' + (currentValues['modifiedSettingsOnly'] ? 'checked' : '') + '>Modified';
-    //dialogBody += '</td></tr><tr><td>Text';
-    //dialogBody += '</td><td><input type="text" id="input-settingFilterText" class="inputfield" id="input-settingFilterText"/>';
-    dialogBody += '</td></tr>';
-    dialogBody += '</table></div></div>';
-    var updateSettingLevelDescription = function() {
-        var value = parseInt(PWM_MAIN.getObject('input-settingLevel').value);
-        var descriptionText = PWM_CONFIG.showString('Display_SettingFilter_Level_' + value);
-        PWM_MAIN.getObject('panel-settingLevelDescription').innerHTML = descriptionText;
-    };
-    var updateIcon = function() {
-        var isDefault = PWM_VAR['settingFilter_modifiedSettingsOnly'] === false && PWM_VAR['settingFilter_level'] === 2;
-        if (isDefault) {
-            PWM_MAIN.removeCssClass('settingFilter_icon', "modified");
-        } else {
-            PWM_MAIN.addCssClass('settingFilter_icon', "modified");
-        }
-    };
-    var updateVars = function() {
-        PWM_VAR['settingFilter_modifiedSettingsOnly'] = PWM_MAIN.getObject('input-modifiedSettingsOnly-modified').checked;
-        PWM_VAR['settingFilter_level'] = parseInt(PWM_MAIN.getObject('input-settingLevel').value);
-        //PWM_VAR['settingFilter_text'] = PWM_MAIN.getObject('input-settingFilterText').value;
-        updateSettingLevelDescription();
-    };
+PWM_CFGEDIT.applyStoredSettingFilterPrefs = function() {
+    var level = PWM_MAIN.Preferences.readSessionStorage('settingFilter_level',2);
+    PWM_MAIN.getObject('radio-setting-level-' + level).checked = true;
+    PWM_VAR['settingFilter_level'] = level;
 
 
-    PWM_MAIN.showDialog({title:'Setting Filters',text:dialogBody,loadFunction:function(){
-            //PWM_MAIN.getObject('input-settingFilterText').value = currentValues['text'];
-            PWM_MAIN.addEventHandler('form-settingFilter','change',function(){
-                updateVars();
-            });
-            updateSettingLevelDescription();
-        },okAction:function(){
-            updateIcon();
-            PWM_CFGEDIT.loadMainPageBody();
-        }});
+    var modified = PWM_MAIN.Preferences.readSessionStorage('settingFilter_modifiedSettingsOnly',false);
+    var idSuffix = modified ? 'modified' : 'all';
+    PWM_MAIN.getObject('radio-modified-only-' + idSuffix).checked = true;
+    PWM_VAR['settingFilter_modifiedSettingsOnly'] = modified;
+};
+
+PWM_CFGEDIT.handleSettingsFilterLevelRadioClick = function (){
+    var value = parseInt(PWM_MAIN.JSLibrary.readValueOfRadioFormInput('radio-setting-level'));
+    PWM_VAR['settingFilter_level'] = value;
+    PWM_MAIN.Preferences.writeSessionStorage('settingFilter_level',value);
+    PWM_CFGEDIT.loadMainPageBody();
 };
 };
 
 
+PWM_CFGEDIT.handleModifiedSettingsRadioClick = function (){
+    var value = PWM_MAIN.JSLibrary.readValueOfRadioFormInput('radio-modified-only') === 'modified';
+    PWM_VAR['settingFilter_modifiedSettingsOnly'] = value ;
+    PWM_MAIN.Preferences.writeSessionStorage('settingFilter_modifiedSettingsOnly',value);
+    PWM_CFGEDIT.loadMainPageBody();
+};
 
 
 PWM_CFGEDIT.readCurrentProfile = function() {
 PWM_CFGEDIT.readCurrentProfile = function() {
     return PWM_VAR['currentProfile'];
     return PWM_VAR['currentProfile'];
@@ -1340,5 +1305,24 @@ PWM_CFGEDIT.openMenuPanel = function() {
 };
 };
 
 
 
 
+PWM_CFGEDIT.drawInfoPage = function(settingInfo) {
+    var categoryInfo = PWM_SETTINGS['categories'][settingInfo['category']];
+    var macroSupport = PWM_MAIN.JSLibrary.arrayContains(settingInfo['flags'],'MacroSupport');
+    var infoPanelElement = PWM_MAIN.getObject('infoPanel');
+
+    var text = '<div class="setting_outline">';
+    text += '<div class="setting-title">' + categoryInfo['label'] + '</div>';
+    text += '<div class="pane-help">' + categoryInfo['description'] + '</div>';
+    text += '</div><br/>';
+
+    text += '<div class="setting_outline">';
+    text += '<div class="setting-title">' + settingInfo['label'] + '</div>';
+    text += '<div class="pane-help">' + settingInfo['description'] + '</div>';
+    if (macroSupport) {
+        text += '<div>This setting has support for using <a>Macros</a><div>';
+    }
+    text += '</div>';
+    infoPanelElement.innerHTML = text;
+};
 
 
 
 

+ 91 - 4
webapp/src/main/webapp/public/resources/themes/pwm/configStyle.css

@@ -247,10 +247,11 @@ table {
     width: 580px;
     width: 580px;
     overflow-y: auto;
     overflow-y: auto;
     position: fixed;
     position: fixed;
-    margin-left: 120px;
+    margin-left: 30px;
     margin-right: 110px;
     margin-right: 110px;
     z-index: 10;
     z-index: 10;
     background: white;
     background: white;
+    top: 65px;
 }
 }
 
 
 #configurationNotesTextarea {
 #configurationNotesTextarea {
@@ -264,7 +265,7 @@ table {
     background-color: white;
     background-color: white;
     border: 1px solid #dae1e1;
     border: 1px solid #dae1e1;
     border-radius: 3px;
     border-radius: 3px;
-    bottom: 20px;
+    bottom: 75px;
     left: 8px;
     left: 8px;
     position: absolute;
     position: absolute;
     top: 55px;
     top: 55px;
@@ -280,7 +281,7 @@ table {
 }
 }
 
 
 #navTreeExpanderButtons {
 #navTreeExpanderButtons {
-    bottom: -20px;
+    bottom: -75px;
     cursor: pointer;
     cursor: pointer;
     position: absolute;
     position: absolute;
     text-align: center;
     text-align: center;
@@ -298,13 +299,25 @@ table {
     width: 606px;
     width: 606px;
 }
 }
 
 
+#infoPanel {
+    border: 0 none;
+    bottom: 0;
+    left: 890px;
+    overflow: auto;
+    padding-right: 6px;
+    position: absolute;
+    top: 55px;
+    width: 10;
+    max-width: 400px;
+}
+
 #settingSearchPanel {
 #settingSearchPanel {
     animation: fadein 0.4s;
     animation: fadein 0.4s;
     background: none repeat scroll 0 0;
     background: none repeat scroll 0 0;
     left: 8px;
     left: 8px;
     position: absolute;
     position: absolute;
     top: 10px;
     top: 10px;
-    width: 850px;
+    width: 100%;
     z-index: 2;
     z-index: 2;
 }
 }
 
 
@@ -440,6 +453,11 @@ input:invalid{
     text-decoration: underline;
     text-decoration: underline;
 }
 }
 
 
+.configLightText {
+    font-size: smaller;
+    color: grey;
+}
+
 .panel-searchResultCategory {
 .panel-searchResultCategory {
     font-weight: bold;
     font-weight: bold;
 }
 }
@@ -615,3 +633,72 @@ select {
 .overflow-y {
 .overflow-y {
     overflow-y: auto;
     overflow-y: auto;
 }
 }
+
+
+/* TOGGLE STYLING */
+.toggle {
+    margin: 0 0 0.2rem;
+    box-sizing: border-box;
+    font-size: 0;
+    display: flex;
+    flex-flow: row nowrap;
+    justify-content: flex-start;
+    align-items: stretch;
+}
+.toggle input {
+    width: 0;
+    height: 0;
+    position: absolute;
+    left: -9999px;
+}
+.toggle input + label {
+    color: #656565;
+    margin: 0;
+    padding: 0.3em;
+    box-sizing: border-box;
+    position: relative;
+    display: inline-block;
+    border: solid 1px #ddd;
+    background-color: #fff;
+    font-size: 1rem;
+    line-height: 140%;
+    text-align: center;
+    box-shadow: 0 0 0 rgba(255, 255, 255, 0);
+    transition: border-color 0.15s ease-out, color 0.25s ease-out, background-color 0.15s ease-out, box-shadow 0.15s ease-out;
+    /* ADD THESE PROPERTIES TO SWITCH FROM AUTO WIDTH TO FULL WIDTH */
+    /*
+    flex: 0 0 50%;
+     display: flex;
+     justify-content: center;
+     align-items: center;
+*/
+    /* ----- */
+}
+.toggle input + label:first-of-type {
+    border-radius: 6px 0 0 6px;
+    border-right: none;
+}
+.toggle input + label:last-of-type {
+    border-radius: 0 6px 6px 0;
+    border-left: none;
+}
+.toggle input:checked + label {
+    background-color: #d3d3d3;
+    border-color: #d3d3d3;
+    z-index: 1;
+}
+.toggleWrapper {
+    text-align: center;
+    display: inline-block
+}
+
+
+@media (max-width: 800px) {
+    .toggle input + label {
+        padding: 0.75rem 0.25rem;
+        flex: 0 0 50%;
+        display: flex;
+        justify-content: center;
+        align-items: center;
+    }
+}