Dropdown Security

I have a customer asking for 2 users to be able to select any option from a dropdown list, but only 1 user that is able to select one option for the dropdown list. Is that possible or am I limited to just the security options effecting the whole dropdown component?

He did mark as Vision, not Perspective

1 Like

Thanks, should've payed attention...

Do you want to show all users all options and then do your security check, or do you only want to show the elevated privileges users the special option so only someone with the right roles can even attempt to select it?

For what the customer is asking for I think he wants all users to see all options but operators can only select the "empty" option while admins can select any of the six options listed.

Yes, you can do this in Vision. See the example at the top. Prefix the items you want disabled with a !. Prefix any you want with confirmation with *

''' This will modify the list item heights to the height of the component multiplied by a factor
	Usage:
	Add to a dropdown component onPropertyChange event handler:
if event.propertyName in ('componentRunning', 'data'):
	target = event.source
	shared.components.ddm.setNewRenderer(target, 1.5)
	
### Library Revision: 1.2		Increment with any change to the functions within
### Revision History
### No.		Author			Date		Comment
### 1.0		Nick Minchin	??			Original
###	1.1		Nick Minchin	??			??
### 1.2		Nick Minchin	2019-06-19	Fixed for cases when no value is selected and combobox value is blank.
### 1.3		Nick Minchin	2019-10-25	Fixed bug in NewPMIComboBoxRenderer class that caused the current selection to be cleared when the script ran. Note: this would not do a tag write, but it would display <Select One> in the component.  
### 1.4	 Nick Minchin	2020-03-02  Added Item selection with Confirmation using "*" prefix
### 1.4.1	Nick Minchin	2020-03-11	Added initialLoad variable to item confirmation case so that confirmation popups are not shown if confirmation items are selected when component first loads. 
### 1.4.2	Nick Minchin	2020-05-20  Moved confirmation code around to be more consistent with the enabled code. No changes made to functionality.
###
### Dependencies
###	 
	
'''

### added to library: shared.components.ddm
from com.inductiveautomation.factorypmi.application.components.PMIComboBox import DataSetListCellRenderer
from com.inductiveautomation.factorypmi.application.components.PMIComboBox import DataSetComboBoxModel
from java.awt import Dimension
from java.awt import Font

# this class edit stops the user from selecting the item
class NewPMIComboBoxModel(DataSetComboBoxModel):
	def __init__(self, target):
		DataSetComboBoxModel.__init__(self, target, target.data)
		self.target = target
		# ensure that the currently selected item remains selected (without this, the item would deslect for some reason)
		self.initialLoad = 1
		try:
			self.selectedItem = target.model.selectedItem
		except:
			pass
		self.initialLoad = 0
	
	# disable items that start with ! from being clicked
	def setSelectedItem(self, item):
		if str(item)[0] == "!":
			def make_setPopupVisible():
				self.target.setPopupVisible(1)
			# keep the popup open (or, reopen it rather)
			system.util.invokeLater(make_setPopupVisible)
		elif str(item)[0] == "*" and not self.initialLoad:
			if system.gui.confirm("Are you you want to change the value to '%s'" % str(item).replace("*",""), "Confirm Action"):
				DataSetComboBoxModel.setSelectedItem(self, item)
		else:
			DataSetComboBoxModel.setSelectedItem(self, item)

# this class edit changes the item's look and feel
class NewPMIComboBoxRenderer(DataSetListCellRenderer):
	# Note: type(target) = PMIComboBox
	def __init__(self, target, scale=1):
		DataSetListCellRenderer.__init__(self, target)
		self.target = target
		self.scale = scale
	
	def getListCellRendererComponent(self, list, value, index, isSelected, cellHasFocus):
		# check for a starting '!'. if exists, disable the item
		itemEnabled = 1
		itemConfirm = 0

		if len(str(value)) > 0:
			itemEnabled = str(value)[0] != "!"
			itemConfirm = str(value)[0] == "*"
				  
		
		# get the item component so we can manipulate it
		component = DataSetListCellRenderer.getListCellRendererComponent(self, list, value, index, isSelected and itemEnabled, cellHasFocus)
																													   
		
		# set the height of the item based on the scale value provided
		component.setPreferredSize(Dimension(self.target.width, int(self.target.height * self.scale)))
		
		component.setEnabled(itemEnabled)
		component.setFont(component.getFont() if itemEnabled else component.getFont().deriveFont(Font.ITALIC))
		if not itemEnabled:
			# remove the "!" from any disabled item names
			component.text = str(value)[1:] + ' (Disabled)'
		if itemConfirm:
			component.text = str(value)[1:] + ' (Confirm)'
		
		return component

def setNewRenderer(PMIComboBox, scale=1):
	'''
	Description:
	This class is used to:
		- modify the list item heights to the height of the component multiplied by a factor. This is useful to increase the size for clients running touchscreens or for mobile projects.
		- prefix characters:
			allow menu items to be disabled by prefixing the item with a '!'
		- allow menu items to require confirmation by prefixing 
	
	Revision:
	No.		Date		Author			Comment
	1.0		2018-03		Paul Griffith	Original from https://forum.inductiveautomation.com/t/anyway-to-make-drop-down-table-bigger-for-touchscreen-projects/15045/5?u=nminchin
	1.1		2018-03		Nick Minchin	Modified for library use (https://forum.inductiveautomation.com/t/anyway-to-make-drop-down-table-bigger-for-touchscreen-projects/15045/6?u=nminchin)
	1.2		2019-04		Nick Minchin	Modified and added ability to disable items from being selected (https://forum.inductiveautomation.com/t/dropdown-list-pmicombobox-component-disable-item-from-selection/23697)
	'''
	PMIComboBox.setRenderer(NewPMIComboBoxRenderer(PMIComboBox, scale))
	PMIComboBox.setModel(NewPMIComboBoxModel(PMIComboBox))
2 Likes

Thank you for the reply. Although, I must admit I do not know how to go about adding this fix to my project. Do I copy this code and put it into a script? or does this go into the scripting of the dropdown box itself?

copy the script into a script library, e.g. shared.components.ddm
then you need to call it, as in the example, in the onPropertyChange event handler of your ddm component:

if event.propertyName in ('componentRunning', 'data'):
	target = event.source
	shared.components.ddm.setNewRenderer(target, 1.5)

This above will set the ddm to use the new renderer and will also increase the height of the items to 1.5x the size. I used this to make it easier for fat-fingered operators for touchscreens :slight_smile:

So I did that but I entered this into the onPropertyChange event handler and I get an error:
"Parse error for event handler "propertyChange" SyntaxError: ("no viable alternative at input 'NewPMIComboBoxModel'", ('', 3, 11, '\tddm.class NewPMIComboBoxModel(DataSetComboBoxModel):\n'))"

if event.propertyName in ('componentRunning', 'data'):
	target = event.source
	ddm.class NewPMIComboBoxModel(DataSetComboBoxModel):
		def __init__(self, target):
			DataSetComboBoxModel.__init__(self, target, target.data)
			self.target = target
			# ensure that the currently selected item remains selected (without this, the item would deslect for some reason)
			self.initialLoad = 1
			try:
				self.selectedItem = target.model.selectedItem
			except:
				pass
			self.initialLoad = 0
		
		# disable items that start with ! from being clicked
		def setSelectedItem(self, item):
			if str(item)[0] == "!":
				def make_setPopupVisible():
					self.target.setPopupVisible(1)
				# keep the popup open (or, reopen it rather)
				system.util.invokeLater(make_setPopupVisible)
			elif str(item)[0] == "*" and not self.initialLoad:
				if system.gui.confirm("Are you you want to change the value to '%s'" % str(item).replace("*",""), "Confirm Action"):
					DataSetComboBoxModel.setSelectedItem(self, item)
			else:
				DataSetComboBoxModel.setSelectedItem(self, item)

I apologize but I am quite a noob with scripting.

I would strongly recommend that you put my script into a script library, e.g. under (this is actually different from my suggestion above):
image

shared.fe.vision.components.ddm

Copy the whole script, not just a section of it.

Then in your Window, on the Dropdown menu component's onPropertyChange event handler script, add this:

if event.propertyName in ('componentRunning', 'data'):
	target = event.source
	shared.fe.vision.components.ddm.setNewRenderer(target, 1.0)
1 Like