# Default Project Properties

**URL:** <https://forum.inductiveautomation.com/t/default-project-properties/45292>\
**Category:** Module Development\
**Created:** [April 6, 2021, 2:07am UTC](https://forum.inductiveautomation.com/t/default-project-properties/45292 "2021-04-06T02:07:23Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![kgamble](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kgamble/32/8019_2.png) [@kgamble](https://forum.inductiveautomation.com/u/kgamble)\
**Post date:** [April 6, 2021, 2:07am UTC](https://forum.inductiveautomation.com/t/default-project-properties/45292/1 "2021-04-06T02:07:23Z")

</div>

Is it possible to add project settings for a custom module?

i.e.

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/a/f/af938cb420292e0d5a32e2b1914e0fb94e438eb5.png)

---

<div class="post-metadata">

**Author:** ![mlunapinel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/mlunapinel/32/19719_2.png) [@mlunapinel](https://forum.inductiveautomation.com/u/mlunapinel)\
**Post date:** [April 6, 2021, 8:21am UTC](https://forum.inductiveautomation.com/t/default-project-properties/45292/2 "2021-04-06T08:21:45Z")

</div>

Hello @kgamble ,

Yes, it is possible.

If you want to configure this settings in the gateway I would recommend checking out the [Home Connect Example](https://github.com/inductiveautomation/ignition-sdk-examples/tree/ignition-8.0/gateway-webpage) .

If you want it to be in the designer I would recommend @paul-griffith 's [project](https://github.com/paul-griffith/markdown-resources). Also I would check out [this thread](http://forum.inductiveautomation.com/t/configure-a-module-with-project-resources/40727).

Hope this helps!

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [April 6, 2021, 3:43pm UTC](https://forum.inductiveautomation.com/t/default-project-properties/45292/3 "2021-04-06T15:43:27Z")

</div>

Note that if you want your settings to appear in the same project properties UI as the other settings, you’ll (probably) want to create a singleton resource [1], and need to register your own property editor panel on the `DesignerContext` you get access to in your designer module hook (`com.inductiveautomation.ignition.designer.DesignerContext#addPropertyEditor`).

[1]: A singleton resource is just defined as a resource with no ‘folder path’; unlike standard resources that can be inside arbitrary folders, singletons are, as the name implies, always a single resource per project. Think gateway event scripts, client event scripts, custom session properties, etc.

---

<div class="post-metadata">

**Author:** ![kgamble](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kgamble/32/8019_2.png) [@kgamble](https://forum.inductiveautomation.com/u/kgamble)\
**Post date:** [April 6, 2021, 4:05pm UTC](https://forum.inductiveautomation.com/t/default-project-properties/45292/4 "2021-04-06T16:05:43Z")

</div>

> [@paul-griffith](#):
>
> Note that if you want your settings to appear in the same project properties UI as the other settings, you’ll (probably) want to create a singleton resource [1]

This is exactly what I am looking for! Is there an example of this? I took a look at the Markdown example above, but that's more of a resource than a property.

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [April 6, 2021, 4:16pm UTC](https://forum.inductiveautomation.com/t/default-project-properties/45292/5 "2021-04-06T16:16:50Z")

</div>

No public examples (yet?), but here’s the highlights…

1. Create a mutable, Java serializable class. Have a public static final ResourceType for it with your module ID.
2. Create your editor class, extending `AbstractPropertyEditorPanel`. In the constructor, set up your text fields/dropdowns/checkboxes/etc (make them fields). Call the protected `listenTo` methods on `AbstractPropertyEditorPanel` with your controls; that helps set up the editor panel’s apply/ok staging.
3. Implement `initProps` - the `Object` you receive will be null, or an instance of your custom resource type. If it’s not null, use it to set your various edit field’s initial value.
4. Implement `commit` - take the values of the various edit fields and write them back to your mutable prop object.

That should be enough to get you started; the rest should be pretty self explanatory. The `AbstractPropertyEditorPanel` will automatically serialize your resource into a `data.bin` file using Java serialization.

---

<div class="post-metadata">

**Author:** ![kgamble](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kgamble/32/8019_2.png) [@kgamble](https://forum.inductiveautomation.com/u/kgamble)\
**Post date:** [April 6, 2021, 10:52pm UTC](https://forum.inductiveautomation.com/t/default-project-properties/45292/6 "2021-04-06T22:52:10Z")

</div>

Considering my initial reaction was this gif

[https://media2.giphy.com/media/5wWf7H89PisM6An8UAU/giphy.gif(image larger than 4096KB)](https://media2.giphy.com/media/5wWf7H89PisM6An8UAU/giphy.gif)

I have gotten further than I expected.

As of right now my only two issues are:

1. I can’t seem to get my editor to show up under the Project category no matter what I try. It just always shows up underneath Perspective.  
 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/0/c/0c454505328066658107f2d3d1ae3723f8863df5.png)
2. The commit event does not fire when I hit OK, or Apply and I am not sure if I am missing something there as well, so I my changes never save and the Object in initProps always come back as null.

Here is a copy of the gateway scope of the module, it only really includes this so I hope its easy to see what I am missing.

[GatewayScope.zip](https://forum.inductiveautomation.com/uploads/short-url/yy8UIuuoVcZrX60j0nFZegMQAEQ.zip) (5.5 KB)

I appreciate any help!

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [April 6, 2021, 11:02pm UTC](https://forum.inductiveautomation.com/t/default-project-properties/45292/7 "2021-04-06T23:02:20Z")

</div>

> [@kgamble](#):
>
> I can’t seem to get my editor to show up under the Project category no matter what I try. It just always shows up underneath Perspective.

Oh, right. For better or for worse, the way things currently work this is a strictly ordered list, there's no actual hierarchy. You can provide your own 'category' by creating a subclass of `com.inductiveautomation.ignition.designer.propertyeditor.CategoryPanel`, but I don't see any way (without gross reflection hacks) to put your own panel below `Project`.

> [@kgamble](#):
>
> The commit event does not fire when I hit OK, or Apply and I am not sure if I am missing something there as well, so I my changes never save and the Object in initProps always come back as null.

You missed the last half of step 2 🙂 Call `listenTo` in your constructor to tell the panel what components it needs to 'care' about having changed; like this:

```java
dataKeyTextField = new JTextField(16);
listenTo(dataKeyTextField);

```

Also, not that important, but your method in DesignerHook doesn't make much sense:

```java
    public Class<? extends AbstractPropertyEditorPanel> PropertyEditor() {
        return PropertyEditor.class;
    }
    public void init() {
        context.addPropertyEditor(PropertyEditor());
    }

```

That could just be:

```java
public void init() {
        context.addPropertyEditor(PropertyEditor.class);
    }

```

---

<div class="post-metadata">

**Author:** ![kgamble](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kgamble/32/8019_2.png) [@kgamble](https://forum.inductiveautomation.com/u/kgamble)\
**Post date:** [April 6, 2021, 11:09pm UTC](https://forum.inductiveautomation.com/t/default-project-properties/45292/8 "2021-04-06T23:09:59Z")

</div>

> [@paul-griffith](#):
>
> You missed the last half of step 2 🙂 Call `listenTo` in your constructor to tell the panel what components it needs to ‘care’ about having changed; like this:

Doh! Added that and it works beautifully.

> [@paul-griffith](#):
>
> Also, not that important, but your method in DesignerHook doesn’t make much sense:

This is what happens when you start learning java and within one week you end up attempting to build something... lol

Last question, This is a default property per project right, so how exactly do I get it back out based on what project it was called in? I would presume it would be somewhere in the client scope in my scripting module where I implement the rpc handlers?

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [April 6, 2021, 11:23pm UTC](https://forum.inductiveautomation.com/t/default-project-properties/45292/9 "2021-04-06T23:23:48Z")

</div>

> [@kgamble](#):
>
> Last question, This is a default property per project right, so how exactly do I get it back out based on what project it was called in? I would presume it would be somewhere in the client scope in my scripting module where I implement the rpc handlers?

So, since you're defining a resource (that may or may not exist, but there will only ever be one of per project) - you would just interact with the project to retrieve your resource and deserialize it from the `data.bin` wherever you need to read this value. There's a `ResourceUtil` helper class with some methods you can use.  
Something like:

```java
var properties = gatewayContext.getProjectManager()
	.getProject("name")
	.map(RuntimeProject::validateOrThrow)
	.flatMap(project -> project.getSingletonResource(Neo4JProperties.RESOURCE_TYPE))
	.map(resource -> ResourceUtil.decodeOrNull(resource, gatewayContext.createDeserializer(), Neo4JProperties.class))
	.orElseGet(Neo4JProperties::new);

```

---

<div class="post-metadata">

**Author:** ![kgamble](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kgamble/32/8019_2.png) [@kgamble](https://forum.inductiveautomation.com/u/kgamble)\
**Post date:** [April 7, 2021, 5:45am UTC](https://forum.inductiveautomation.com/t/default-project-properties/45292/10 "2021-04-07T05:45:43Z")

</div>

Out of curiosity, is the class used to define these label/combobox/refreshicon exposed in any way via the sdk? It looks like its simplemented as `com.inductiveautomation.ignition.client.util.gui.AbstractProfileOptionDropdown` I am just struggling to create it.

![Screen Shot 2021-04-06 at 10.43.21 PM](https://us1.discourse-cdn.com/inductiveautomation/original/3X/6/9/695d09ba1b833813ba764acabbb567f718c5e3b8.png)

I recreated my own from scratch, but I feel like if I can latch into the pre-defined one that’s already used it may be better for future proofing.

---

<div class="post-metadata">

**Author:** ![kgamble](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kgamble/32/8019_2.png) [@kgamble](https://forum.inductiveautomation.com/u/kgamble)\
**Post date:** [April 7, 2021, 2:58pm UTC](https://forum.inductiveautomation.com/t/default-project-properties/45292/11 "2021-04-07T14:58:20Z")

</div>

> [@kgamble](#):
>
> It looks like its simplemented as `com.inductiveautomation.ignition.client.util.gui.AbstractProfileOptionDropdown` I am just struggling to create it.

To anyone following along at home, this looks like it was the answer I was looking for.

Creating an implementation of that class, and providing its contained dropdown with my options gave me the dropdown and refresh icon. I am guessing that the labels in the above image are just labels and not a part of the rest of the component.

---

<div class="post-metadata">

**Author:** ![kgamble](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kgamble/32/8019_2.png) [@kgamble](https://forum.inductiveautomation.com/u/kgamble)\
**Post date:** [April 7, 2021, 4:40pm UTC](https://forum.inductiveautomation.com/t/default-project-properties/45292/12 "2021-04-07T16:40:29Z")

</div>

> [@paul-griffith](#):
>
> You can provide your own ‘category’ by creating a subclass of `com.inductiveautomation.ignition.designer.propertyeditor.CategoryPanel`,

I created the subclass but how do I add it into the designer context?

Unfortunately `context.addPropertyCategory(CustomCategory.class)` doesnt seem to exist... lol

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [April 7, 2021, 4:45pm UTC](https://forum.inductiveautomation.com/t/default-project-properties/45292/13 "2021-04-07T16:45:01Z")

</div>

Just add it as a `PropertyPanel` (it technically is); the list will automatically render it as a category and not allow you to select it.

---

<div class="post-metadata">

**Author:** ![kgamble](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kgamble/32/8019_2.png) [@kgamble](https://forum.inductiveautomation.com/u/kgamble)\
**Post date:** [April 7, 2021, 7:43pm UTC](https://forum.inductiveautomation.com/t/default-project-properties/45292/14 "2021-04-07T19:43:31Z")

</div>

> [@paul-griffith](#):
>
> Create a mutable, Java serializable class. Have a public static final ResourceType for it with your module ID.

Is there an obvious reason as to why these are reset anytime the module restarts? The binary files are still there, but the method I use to get the data out start returning null as soon as the module restarts and until I open the designer and specifically the property editor panel for it.

I tried moving my ResourceType outside of the designer scope into common, thinking potentially that was related but it doesn't seem to have fixed the problem.

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [April 7, 2021, 8:25pm UTC](https://forum.inductiveautomation.com/t/default-project-properties/45292/15 "2021-04-07T20:25:08Z")

</div>

It seems like you’re maybe accidentally mutating a singleton? Your `ResourceType` field should be the only static field; it’s just a marker to the rest of the project resource system to identify your resource. You should be retrieving/creating fresh copies of the actual resource object as needed; see the ResourceUtil snippet above.

---

<div class="post-metadata">

**Author:** ![kgamble](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kgamble/32/8019_2.png) [@kgamble](https://forum.inductiveautomation.com/u/kgamble)\
**Post date:** [April 7, 2021, 9:02pm UTC](https://forum.inductiveautomation.com/t/default-project-properties/45292/16 "2021-04-07T21:02:07Z")

</div>

I ran through it and at least I think I am creating and retrieving copies as needed?

This is my resource

```auto
public class Neo4JProperties {
   public static final ResourceType RESOURCE_TYPE = new ResourceType("com.kgamble.neo4j", "Neo4JProperties");
   
   private String defaultDatabase;

   public String getDefaultDatabase() {
       return defaultDatabase;
   }

   public void setDefaultDatabase(String database) {
       defaultDatabase = database;
   }
}

```

Where I provide it to the script in the rpc handler:

```auto
@Override
public Object getRPCHandler(ClientReqSession session, String projectName) {
    Neo4JProperties properties = getProjectSettings(projectName);
    scriptModule.setDefaultDatabase(properties.getDefaultDatabase());
    return scriptModule;
}

```

The implementation of the `ResourceUtil` example you provided (I originally just copy and pasted yours, but vsCode really did not like the map method)

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/c/a/ca5ca3054c6a07539d24ce3e4d8601742076231c.png)

```auto
private Neo4JProperties getProjectSettings(String projectName) {
        Neo4JProperties properties = context.getProjectManager()
            .getProject(projectName)
            .map(t -> {
                try {  
                    return t.validateOrThrow();
                } catch (ProjectInvalidException e) {
                    e.printStackTrace();
                }
                return t;
            })
            .flatMap(project -> project.getSingletonResource(Neo4JProperties.RESOURCE_TYPE))
            .map(resource -> {
                try {
                    return ResourceUtil.decodeOrNull(resource, context.createDeserializer(), Neo4JProperties.class);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return null;
            })
            .orElseGet(Neo4JProperties::new);
       
        return properties;
    }

```

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [April 7, 2021, 9:14pm UTC](https://forum.inductiveautomation.com/t/default-project-properties/45292/17 "2021-04-07T21:14:43Z")

</div>

Oh, right. You can write all that code imperatively; Java’s checked exceptions don’t play nicely with lambdas (sigh). Something like this:

```java
var project = gatewayContext.getProjectManager()
	.getProject("name")
	.orElseThrow() // throws NoSuchElementException if project not found
	.validateOrThrow(); // throws ProjectInvalidException if the project is invalid

Neo4JProperties properties = null;
var maybeResource = project.getSingletonResource(Neo4JProperties.RESOURCE_TYPE);
if (maybeResource.isPresent()) {
	var resource = maybeResource.get();
	try {
		properties = ResourceUtil.decodeOrNull(resource, gatewayContext.createDeserializer(), Neo4JProperties.class);
	} catch (Exception e) {
		e.printStackTrace();
	}
}

if (properties == null) {
	properties = new Neo4JProperties();
}

```

---

<div class="post-metadata">

**Author:** ![kgamble](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kgamble/32/8019_2.png) [@kgamble](https://forum.inductiveautomation.com/u/kgamble)\
**Post date:** [April 7, 2021, 9:30pm UTC](https://forum.inductiveautomation.com/t/default-project-properties/45292/18 "2021-04-07T21:30:07Z")

</div>

Still didnt seem to resolve the problem, when I publish the module and it posts to the gateway (or restart the module or gateway) the record is null until I open the designer property editor.

The only other thing I can think of is if I potentially implemented the property editor incorrectly?

```auto
public class GeneralPropertyEditor extends AbstractPropertyEditorPanel {

    private final ScriptingFunctions rpc;
    private Neo4JProperties neo4jProps = new Neo4JProperties();
    private final DatabaseDropdown dropdown;

    public GeneralPropertyEditor(DesignerContext context) {
        super(new MigLayout("fill", "[pref!][grow,fill]", "[]15[]"));

        rpc = ModuleRPCFactory.create(
            "com.kgamble.neo4j.neo4j-driver",
            ScriptingFunctions.class
        );
        dropdown = new DatabaseDropdown(false, rpc);

        add(HeaderLabel.forKey("GeneralPropertyEditor.Database.Header"), "wrap r");

        add(new JLabel(BundleUtil.get().getString("GeneralPropertyEditor.Database.Label")), "");
        add(dropdown, "wrap");
        listenTo(dropdown.getDropdown());

    }

    public List<String> getConnections() {
        return rpc.getConnections();
    }

    @Override
    public Object commit() {
        if ( dropdown.getSelectedIndex() != -1 ) {
            neo4jProps.setDefaultDatabase(dropdown.getSelectedItem().toString());
        } else {
            return null;
        }
        
        return neo4jProps;
    }

    @Override
    public String getCategory() {
        return "Neo4J";
    }

    @Override
    public ResourceType getResourceType() {
        return Neo4JProperties.RESOURCE_TYPE;
    }

    @Override
    public String getTitleKey() {
        return "GeneralPropertyEditor.General.Title";
    }

    @Override
    public void initProps(Object props) {
        if ( props == null ) {
            dropdown.setSelectedItem(null);
        } else {
            Neo4JProperties updatedProps = (Neo4JProperties) props;
            this.neo4jProps = updatedProps;
    
            dropdown.setSelectedItem(neo4jProps.getDefaultDatabase());
        }
    }
    
}

```

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [April 7, 2021, 10:04pm UTC](https://forum.inductiveautomation.com/t/default-project-properties/45292/19 "2021-04-07T22:04:02Z")

</div>

> [@kgamble](#):
>
> ```auto
> @Override
> public Object commit() {
> if ( dropdown.getSelectedIndex() != -1 ) {
> neo4jProps.setDefaultDatabase(dropdown.getSelectedItem().toString());
> } else {
> return null;
> }
>         
> return neo4jProps;
> }
> 
> ```

I don't think that's what you want. I think you should always return `neo4jProps` - but if the dropdown has nothing selected, set the default database on the instance to null and handle it when you try to make a connection.

---

<div class="post-metadata">

**Author:** ![kgamble](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kgamble/32/8019_2.png) [@kgamble](https://forum.inductiveautomation.com/u/kgamble)\
**Post date:** [April 7, 2021, 10:44pm UTC](https://forum.inductiveautomation.com/t/default-project-properties/45292/20 "2021-04-07T22:44:44Z")

</div>

Unfortunately no luck with that either, will keep digging, but unfortunately my logging doesn’t seem to want to work from inside that getRpcHandler method.

[Next page](https://forum.inductiveautomation.com/t/default-project-properties/45292.md?page=2)
