AbstractDriver Module - StringField for password input

In an AbstractDriver Module, I would like to add some property for an password input field.
I don’t find the property or flags to add in order the StringField is displayed in password style password : *****

public static final StringField PASS = new StringField(META, "MyPass", SFieldFlags.SMANDATORY);

In a static block somewhere in your settings record, do this:

static {
    PASS.getFormMeta().setEditorSource(PasswordEditorSource.getSharedInstance());
}

for bonus points use EncodedStringField instread of StringField, which obscures the password in the internal DB.

PasswordEditorSource is, annoyingly, one of those two-field password deals. Most of the time this is stupid and unnecessary because you’re using it to enter the password to an external system, not set a password and double-check you didn’t typo it. Just have to live with it for now though :slight_smile:

edit: also, this has nothing to do with drivers. any module or system using PersistentRecord can use this.

2 Likes

Hi @Kevin.Herron,
could you please provide an example for the EnumEditorSource.
I try to create a field with a list of choice.

You don’t use EnumEditorSource directly, just use an EnumField for your field.

1 Like

@Kevin.Herron,
and the last one…type of field for my use case, is there another class instead of FileUploadEditorSource to configure a load file property :thinking:

Looks like you should be using a StringField with the editor source set to FileUploadEditorSource.getSharedInstance().

Cool ! the file is uploaded and stored in DB ? How did you retrieve the file content ?

You know… it looks like this might only be usable on your own custom record page.

I don’t know if this helps in your situation, but this is an example of how we added a FileUploadField to a page that extends RecordEditForm. In this case, MyRecord.MyStringField has its editor source set to FileUploadEditorSource.getSharedInstance(). This page links our own FileUploadField to one that was created for us somewhere in the form. When the submit occurs, we can access our FileUploadField and extract the bytes from the upload. From there, we can convert the bytes to a String, save the String to MyRecord.MyStringField and manually commit the record. As Kevin mentioned, this approach only works if you can implement your own custom edit record page :frowning:

public class MyEditForm extends RecordEditForm {
private FileUploadField uploadField;

@Override
protected Component newEditorComponent(String id, FormMeta formMeta, RecordEditMode mode, SRecordInstance record) {
	Component comp = super.newEditorComponent(id, formMeta, mode, record);
		if (formMeta.getField().equals(MyRecord.MyStringField)) {
			uploadField = (FileUploadField) ((AbstractFormComponentEditor<?>) comp).getFormComponent();
		}
}

@Override
protected void onSubmit(RecordEditMode mode, List<SRecordInstance> records) {
	List<FileUpload> fuList = uploadField.getModelObject();
    FileUpload fu = null;
    if (fuList != null && fuList.size() > 0) {
        fu = fuList.get(0);
    }
	
	if (fu != null) {
		String fileName = fu.getClientFileName();
		String fileContents = new String(fu.getBytes(), "UTF8");
		
		// The fileContents String can now be stored in any record as a standard String,
		// and retrieved later with a query
	}
	
	super.onSubmit(mode, records);
}

}

Thanks @mgross ! I will try it as soon as possible.

@mgross, when I add this class, I can’t solve the error : there is no default constructor for RecordEditForm…
My class from the Abstract Tag Driver example extends PersistentRecord…

I did some more digging into this today. As I feared, there is not a simple way to do this from the driver settings page you are using. The settings page is automatically generated as part of the extension point system, and there is no place to override the methods I showed in my example. The only way you are going to be able to use a FileUpload field is if you build out a new Gateway web page from scratch, add the page to the Gateway menu somewhere, and add another page that can extend RecordEditForm and work with your driver record directly. It’s a fair amount of work. Are you really really sure you want to add a FileUploadField?

Ok I see. I will keep as a workaround a text field for the file path selection.
Thanks @mgross for these clarifications.

I have a file upload field in my Ethernet/IP driver, to accept XML configuration files. It does have to be in a separate page, but that page can be offered to the user within the driver framework by implementing the getLinks() method of your DriverType subclass. Return one or more ConfigurationUILink items that can instantiate your config page.
In my case, the custom configuration page directly extends ConfigPanel with a constructor of the form required by ConfigurationUILink. You should use the driver instance configuration record from the constructor to set the configuration panel’s default model. Here are some snippets from my working code to help you get started:

public class HostDevConfig extends ConfigPanel {
	protected static final LoggerEx _log = LogUtil.getModuleLogger(Meta.SHORT_MODULE_ID, "HostDevConfig");
	private static final long serialVersionUID = 1L;

	protected final IConfigPage configPage;
	protected final ConfigPanel returnPanel;
	protected final Callback callback;

	public HostDevConfig(IConfigPage configPage, ConfigPanel returnPanel, Callback callback, PersistentRecord record) {
		super("enip1.Config.Title");
		this.configPage = configPage;
		this.returnPanel = returnPanel;
		this.callback = callback;

		setDefaultModel(new RecordModel<PersistentRecord>(record));

		addComponents();
	}

	@Override
	public String[] getMenuPath() {
		return returnPanel.getMenuPath();
	}

	private void addComponents() {
		add(new HostDevSummary("summary", getDefaultModel()));

		try {
			add(new ResourceLink<IResource>("export-link", new ExportXmlResource()));
		} catch (Exception e) {
			add(new BrokenLink("export-link"));
		}

		try {
			add(new ResourceLink<IResource>("live-export-link", new LiveXmlResource()));
		} catch (Exception e) {
			add(new BrokenLink("live-export-link"));
		}

		Form<?> uploadForm = new ImportXmlForm("upload-form");
		add(uploadForm);
/* ... */
	}

	private class ImportXmlForm extends Form<Object> {
		private static final long serialVersionUID = 1L;
		protected final FileUploadField uploadField;

		public ImportXmlForm(String id) {
			super(id);
			uploadField = new FileUploadField("upload-field");
			this.add(uploadField);
			SubmitLink importLink = new SubmitLink("import");
			this.add(importLink);
/* ... */
		}

		@Override
		protected void onSubmit() {
			HostDeviceSettings settings = (HostDeviceSettings) HostDevConfig.this.getDefaultModelObject();
/* ... */
			SAXParser sp;
			LgxXmlParser lxp = null;
			try {
				SAXParserFactory spf = SAXParserFactory.newInstance();
				sp = spf.newSAXParser();
				XMLReader xr = sp.getXMLReader();
				lxp = new LgxXmlParser();
				xr.setContentHandler(lxp);
				InputSource src = new InputSource(uploadField.getFileUpload().getInputStream());
				xr.parse(src);
/* ... */
				ByteArrayOutputStream baos = new ByteArrayOutputStream();
				GZIPOutputStream gzos = new GZIPOutputStream(baos);
				PrintStream stream = new PrintStream(gzos);
				dev.saveXML(stream, "");
				IOUtils.closeQuietly(stream);
/* ... */
				settings.setXML(baos.toByteArray());
				callback.save(settings);
				Session.get().info("Import succeeded.");
				configPage.setConfigPanel(returnPanel);
			} catch (Exception e) {
				_log.warn("Error importing configuration from XML file.", e);
				Session.get().warn("Failed to import the XML file.  See the log for details.");
				configPage.setConfigPanel(HostDevConfig.this);
			}
		}
	}
}

You’ll note that the XML storage field is a BlobField, not a StringField, as I want to store the gzipped version.
The code above is appropriate for storing the import with rest of the settings, and will auto-restart your driver instance on save. The latest version of the above code (not shown) saves to another persistent record so I have control over the application of the updated configuration.