Label bug in module configuration page (Ignition 8.1.48)

I’m working on developing the configuration page for my module and when configuring the second page I came across this problem with my labels.

I am using a UserMfaRecord.properties file for them and I think I did everything the same way I did for the first page but somehow I can’t seem to get the labels to have the correct name. I got the category to work properly using a different .properties file (the same one I used for my first page), but when I tried using it for the labels it still didn’t change a thing. Here’s the code:

 public static class UserMfaRecord extends PersistentRecord {  
        
        public static final RecordMeta<UserMfaRecord> META = new RecordMeta<UserMfaRecord>(
                UserMfaRecord.class, "UserMfaRecord")
                .setNounKey("UserMfaRecord.Noun")
                .setNounPluralKey("UserMfaRecord.Noun.Plural");

         public interface IRecordListener<T> {

             void onRecordCreated(T record);
         }

         private static final List<IRecordListener<UserMfaRecord>> listeners = new ArrayList<>();

         public static void addRecordListener(IRecordListener<UserMfaRecord> listener) {
             listeners.add(listener);
         }

         public UserMfaRecord() {
             super();
             notifyCustomListeners(this);
         }

         public static void notifyCustomListeners(UserMfaRecord record) {
             for (IRecordListener<UserMfaRecord> listener : listeners) {
                 listener.onRecordCreated(record);
            }
         }
      

        public static final IdentityField Id = new IdentityField(META);

        public static final StringField User = new StringField(META, "User", SFieldFlags.SPRIMARY_KEY, 
        SFieldFlags.SMANDATORY, SFieldFlags.SDESCRIPTIVE);

        // public static final EncodedStringField Password
        //         = new EncodedStringField(META, "Password", SFieldFlags.SMANDATORY, SFieldFlags.SDESCRIPTIVE);
        public static final EnumField<MfaOptions> MfaOption
                = new EnumField<>(META, "MfaOption", MfaOptions.class, SFieldFlags.SMANDATORY, SFieldFlags.SDESCRIPTIVE);

        public static final Category USER_DATA
                = new Category("KeycloakSettingsRecord.Category.USER_DATA", 1000)
                        .include(User, MfaOption);

        static {
            User.setUnique(true);
            MfaOption.setDefault(MfaOptions.SMS);

        }

And here’s the properties file: UserMfaRecord.properties

User.Name=Username
MfaOption.Name=Mfa Option

If anyone has any tips I’d really appreciate the help!

1 Like

Have you fully restarted the gateway?

There's a longstanding bug (that we're, at this point, never going to fix) where repeated calls to initialize bundles end up in a weird state where things don't quite work correctly.

Yes! I realized that was happening and started restarting the gateway every time I changed any labels or tried any fixes for this issue, but nothing changed. Do you know what else it could be?

For the sake of it, does it resolve the keys if you repeat the prefix again on the actual key literals?
As in:

UserMfaRecord.User.Name=Username
UserMfaRecord.MfaOption.Name=Mfa Option
fieldNameKey = field.getRecordMeta().getUserClass().getSimpleName() + "." + field.getFieldName() + ".Name";
fieldDescriptionKey = field.getRecordMeta().getUserClass().getSimpleName() + "." + field.getFieldName() + ".Desc";`

I have no idea if this will help, but you could try running this (for development only!) in your gateway hook once:

org.apache.wicket.Application.get().getResourceSettings().getLocalizer().setEnableCache(false);

Yeah I tried that, it also didn’t work.

Sorry I didn’t really understand, where exactly should I run it?

In your module's GatewayHook, in the setup or startup methods, you could try running that snippet. It should, in theory, tell Wicket not to do any caching of localization data, which might help with the "having to restart the gateway every time" problem.

Won't help with the actual observed issue here, though, I don't think.

I've found that BundleUtil works best when called in the gateway hook's constructor, not in any of the lifecycle methods. FWIW.

(Crucial for v8.3, fwiw.)

1 Like

Thank you. Do you have any ideas as to how I could solve the other issue? It’s more pressing atm and I’ve honeslty run out of things to try.

Not really. You'll have to go through and make sure you're mounting all the correct bundles at the correct locations. There's plenty of existing modules that have multiple bundle keys and mount them correctly, so I don't think there's an underlying bug (besides the aforementioned caching/staleness issue).

If you really can't get it working, theoretically you could override RecordActionTable and hardcode or otherwise provide your own labels for the elements, but I wouldn't jump at that idea.

Have you drilled into your .modl and ensured that the .properties files are included? The PersistentRecord subclasses must have their props files in the same package in the jar.

2 Likes

Yes! Here’s an example of a field, could you please let me know if you spot anything wrong?

public static class EmailConfigRecord extends PersistentRecord {

        public static final RecordMeta<EmailConfigRecord> META = new RecordMeta<>(
                EmailConfigRecord.class, "EmailConfigRecord")
                .setNounKey("EmailConfigRecord.Noun")
                .setNounPluralKey("EmailConfigRecord.Noun.Plural");

        public static final IdentityField Id = new IdentityField(META);

        public static final StringField EmailFrom = new StringField(META, "EmailFrom");

and here’s the .properties file

EmailFrom.Name=From Address

here’s what’s like in the gatewathook:

BundleUtil.get().addBundle("EmailConfigRecord", getClass(), "Keycloak");

and here on the setup method:

context.getSchemaUpdater().updatePersistentRecords(KeycloakSettingsRecord.EmailConfigRecord.META);

No, that loads a properties file that ties to the hook class, and would be expected to be in the hook's package. You need a properties file for each subclass of PersistentRecord, with the same base file name as the class file, and in the same package as the class file.

The schema updater will find and add those bundles for you, and you cannot change that behavior.

Use BundleUtil for wicket and other translatables other than persistent record elements.

It also appears that your PR subclass is a nested class. I'm not sure that is supported.

3 Likes

Ooh, yeah, good catch. I can guarantee that 100% of the PersistentRecord implementations in the Ignition codebase were top level classes, so it would not surprise me at all if nested classes break our lookup and we just never knew about it.

2 Likes

Thank you so much! This completely solved the problem, I really appreciate the help!

3 Likes