Comments Panel
Component Palette Icon:
Additional information on the Comments Panel can be found on the Comments Panel Example page.
Looking for documentation on the legacy Comments Panel component? Please see the Legacy Comments Panel page.
Not sure which version you are looking at? The Legacy version of this component has several properties that the new one does not: "Insert Query 1", " Insert Query 2", "Delete Query", "Unstick Query", and "Download Attachment Query".
The comments panel is used to power a blog-style comments system within your project. This can be useful for ad-hoc collaboration and communication between shifts, remote users, etc. This component is driven by a dataset that should be bound to a SQL query. Unlike most components, this component has built-in functionality to alter an external database. This is how the default Add Note functionality works. You have the opportunity to alter the queries that the components uses by enabling Extension Functions.
The schema that typically drives this component involves up to three tables:
-
The first table (by default: Notes) stores all of the notes across the board.
-
The second table (by default, ItemNotes) is used to associate notes with other things. This allows you to have different sets of notes for different screens/objects. Typically you'd bind the data to a query that joined these tables together restricting the second identifier in the ItemNotes table to the value appropriate for the window you're on.
-
The third table (by default: users) is a user mapping table that assigns an ID to each user on the table. This is easiest to do if a database authentication profile is used as the _users table automatically creates the required columns, but non-database authentication profiles can be used as long as the table is manually created and maintained.
You can opt out of this three-table system by simply making use of the Extension Functions on the component. See below for more details.
By default, users can remove their own comments, and comments can have files attached. To allow attachments, make sure you have a BLOB field in your notes table.
The following is a list of the default behaviors when all Extension Functions are disabled
Behavior |
Description |
Adding a note |
Runs a SELECT query against the users table to retrieve the ID value for the user that added the note. Then stores this ID into the Notes table along with the rest of the note data |
Deleting a note |
Deletes a row from the Notes table. Uses the ID value from the Notes table to determine which row should be deleted. Does not delete a row from the ItemNotes table |
Unsticking a note |
Updates the sticky column for the note on the Notes table. Sets the value to 0 based on the ID of the note |
Download an attachment from a note |
Returns the binary data from the Attachment column on the Notes table. |
Ability for users to delete notes |
Users may not delete notes from other users, but are allowed to delete their own notes. |
Enabling the Extension Functions on the component allow for custom functionality on the component. Some examples are:
-
Store all note data on a single database table - simply modify each Extension Function to run queries against a single database table
-
Save the attachment to a shared drive instead of a database column - modify insertNote to save the attachment to a hard drive.
-
Allow users to delete all notes by role - check the role of the user in canDelete and return True if the user has a specific role
Name |
Description |
Property Type |
Scripting |
Category |
Add Note Text |
The word(s) used for the "Add Note" button |
String |
.addNoteText |
Appearance |
Antialias |
Enabled antialiasing when drawing the component |
boolean |
.antialias |
Appearance |
Attach File Text |
The word(s) used for the "Attach File" link. |
String |
.attachText |
Appearance |
Attachments Enabled |
Controls whether or not files can be attached to notes. |
boolean |
.attachmentsEnabled |
Behavior |
Border |
The border surrounding this component. NOTE that the border is unaffected by rotation. |
border |
.border |
Common |
Cancel Text |
The word(s) used for the "Cancel" button |
String |
.cancelText |
Appearance |
Data |
Fill this DataSet in with the notes for the desired entity. Columns are: ID, Username, Timestamp, Note, Filename, IsSticky |
Dataset |
.data |
Data |
Data Quality |
The data quality code for any tag bindings on this component. |
int |
.dataQuality |
Data |
Database Connection |
Name of the database connection to run the queries against. Leave blank to use project's default connection |
String |
.datasource |
Behavior |
Date Format |
The format string to use for the date of the note. |
String |
.dateFormat |
Appearance |
Display Mode |
Horizontal display mode will layout so that the comment header will be positioned to the left of the comment. Vertical display mode will have the comment header above the comment. |
int |
.displayMode |
Behavior |
Enabled |
If disabled, a component cannot be used. |
boolean |
.componentEnabled |
Common |
Font |
Font of text on this component. |
Font |
.font |
Appearance |
Foreground Color |
The foreground color of the component. |
Color |
.foreground |
Appearance |
Header Color |
The background color of the header notes. |
Color |
.headersColor |
Appearance |
Maximum Attachment Size |
The maximum attachment size in bytes that will be accepted. A value of 0 means no limit. |
long |
.maxAttachmentSize |
Behavior |
Mouseover Text |
The text that is displayed in the tooltip which pops up on mouseover of this component. |
String |
.toolTipText |
Common |
Name |
The name of this component. |
String |
.name |
Common |
Note Color |
The background color for notes. |
Color |
.noteColor |
Appearance |
Padding |
The amount of padding between the notes. |
int |
.padding |
Appearance |
Skip Audit |
If true, update queries originating from this component will skip the audit system. Can be important when attachments are turned on. |
boolean |
.skipAudit |
Behavior |
Sticky Header Color |
The background color of the header for sticky notes. |
Color |
.stickyHeaderColor |
Appearance |
Sticky Note Color |
The background color for sticky notes. |
Color |
.stickyNoteColor |
Appearance |
Sticky Text |
The word(s) used for the "Sticky" checkbox. |
String |
.stickyText |
Appearance |
Touchscreen Mode |
Controls when this input component responds if touchscreen mode is enabled. |
int |
.touchscreenMode |
Behavior |
Visible |
If disabled, the component will be hidden. |
boolean |
.visible |
Common |
This component does not have scripting functions associated with it.
insertNote
-
Description
Called when a note is added.
-
Parameters
component self - A reference to the component that is invoking this function
string note - The text contents of the note
string filename - The full filepath to the the attachment
string sticky - A boolean indicating whether this note should be flagged as stickied
-
Return
Nothing
-
Scope
Client
deleteNote
-
Description
Called when a user clicks the 'delete' link on a note.
-
Parameters
component self - A reference to the component that is invoking this function
integer id - The id of the note
-
Return
Nothing
-
Scope
Client
unstickNote
-
Description
Called when a user clicks the 'unstick' link on a note.
-
Parameters
component self - A reference to the component that is invoking this function
integer id - The id of the note
-
Return
Nothing
-
Scope
Client
downloadAttachment
-
Description
Called when a user attempts to download an attachment from a note.
-
Parameters
component self - A reference to the component that is invoking this function
integer id - The id of the note
-
Return
Nothing
-
Scope
Client
canDelete
-
Description
Returns whether or not a note with the given id can be deleted. Notes that return True will show a 'delete' link.
-
Parameters
component self - A reference to the component that is invoking this function
integer id - The id of the note
-
Return
boolean - Notes with a True return can be deleted by the user, False return can not be deleted.
-
Scope
Client
mouse
mouseClicked
This event signifies a mouse click on the source component. A mouse click the combination of a mouse press and a mouse release, both of which must have occurred over the source component. Note that this event fires after the pressed and released events have fired.
.source |
The component that fired this event |
.button |
The code for the button that caused this event to fire. |
.clickCount |
The number of mouse clicks associated with this event. |
.x |
The x-coordinate (with respect to the source component) of this mouse event. |
.y |
The y-coordinate (with respect to the source component) of this mouse event. |
.popupTrigger |
Returns True (1) if this mouse event is a popup trigger. What constitutes a popup trigger is operating system dependent, which is why this abstraction exists. |
.altDown |
True (1) if the Alt key was held down during this event, false (0) otherwise. |
.controlDown |
True (1) if the Control key was held down during this event, false (0) otherwise. |
.shiftDown |
True (1) if the Shift key was held down during this event, false (0) otherwise. |
mouseEntered
This event fires when the mouse enters the space over the source component.
.source |
The component that fired this event |
.button |
The code for the button that caused this event to fire. |
.clickCount |
The number of mouse clicks associated with this event. |
.x |
The x-coordinate (with respect to the source component) of this mouse event. |
.y |
The y-coordinate (with respect to the source component) of this mouse event. |
.popupTrigger |
Returns True (1) if this mouse event is a popup trigger. What constitutes a popup trigger is operating system dependent, which is why this abstraction exists. |
.altDown |
True (1) if the Alt key was held down during this event, false (0) otherwise. |
.controlDown |
True (1) if the Control key was held down during this event, false (0) otherwise. |
.shiftDown |
True (1) if the Shift key was held down during this event, false (0) otherwise. |
mouseExited
This event fires when the mouse leaves the space over the source component.
.source |
The component that fired this event |
.button |
The code for the button that caused this event to fire. |
.clickCount |
The number of mouse clicks associated with this event. |
.x |
The x-coordinate (with respect to the source component) of this mouse event. |
.y |
The y-coordinate (with respect to the source component) of this mouse event. |
.popupTrigger |
Returns True (1) if this mouse event is a popup trigger. What constitutes a popup trigger is operating system dependent, which is why this abstraction exists. |
.altDown |
True (1) if the Alt key was held down during this event, false (0) otherwise. |
.controlDown |
True (1) if the Control key was held down during this event, false (0) otherwise. |
.shiftDown |
True (1) if the Shift key was held down during this event, false (0) otherwise. |
mousePressed
This event fires when a mouse button is pressed down on the source component.
.source |
The component that fired this event |
.button |
The code for the button that caused this event to fire. |
.clickCount |
The number of mouse clicks associated with this event. |
.x |
The x-coordinate (with respect to the source component) of this mouse event. |
.y |
The y-coordinate (with respect to the source component) of this mouse event. |
.popupTrigger |
Returns True (1) if this mouse event is a popup trigger. What constitutes a popup trigger is operating system dependent, which is why this abstraction exists. |
.altDown |
True (1) if the Alt key was held down during this event, false (0) otherwise. |
.controlDown |
True (1) if the Control key was held down during this event, false (0) otherwise. |
.shiftDown |
True (1) if the Shift key was held down during this event, false (0) otherwise. |
mouseReleased
This event fires when a mouse button is released, if that mouse button's press happened over this component.
.source |
The component that fired this event |
.button |
The code for the button that caused this event to fire. |
.clickCount |
The number of mouse clicks associated with this event. |
.x |
The x-coordinate (with respect to the source component) of this mouse event. |
.y |
The y-coordinate (with respect to the source component) of this mouse event. |
.popupTrigger |
Returns True (1) if this mouse event is a popup trigger. What constitutes a popup trigger is operating system dependent, which is why this abstraction exists. |
.altDown |
True (1) if the Alt key was held down during this event, false (0) otherwise. |
.controlDown |
True (1) if the Control key was held down during this event, false (0) otherwise. |
.shiftDown |
True (1) if the Shift key was held down during this event, false (0) otherwise. |
mouseMotion
mouseDragged
Fires when the mouse moves over a component after a button has been pushed.
.source |
The component that fired this event |
.button |
The code for the button that caused this event to fire. |
.clickCount |
The number of mouse clicks associated with this event. |
.x |
The x-coordinate (with respect to the source component) of this mouse event. |
.y |
The y-coordinate (with respect to the source component) of this mouse event. |
.popupTrigger |
Returns True (1) if this mouse event is a popup trigger. What constitutes a popup trigger is operating system dependent, which is why this abstraction exists. |
.altDown |
True (1) if the Alt key was held down during this event, false (0) otherwise. |
.controlDown |
True (1) if the Control key was held down during this event, false (0) otherwise. |
.shiftDown |
True (1) if the Shift key was held down during this event, false (0) otherwise. |
mouseMoved
Fires when the mouse moves over a component, but no buttons are pushed.
.source |
The component that fired this event |
.button |
The code for the button that caused this event to fire. |
.clickCount |
The number of mouse clicks associated with this event. |
.x |
The x-coordinate (with respect to the source component) of this mouse event. |
.y |
The y-coordinate (with respect to the source component) of this mouse event. |
.popupTrigger |
Returns True (1) if this mouse event is a popup trigger. What constitutes a popup trigger is operating system dependent, which is why this abstraction exists. |
.altDown |
True (1) if the Alt key was held down during this event, false (0) otherwise. |
.controlDown |
True (1) if the Control key was held down during this event, false (0) otherwise. |
.shiftDown |
True (1) if the Shift key was held down during this event, false (0) otherwise. |
propertyChange
propertyChange
Fires whenever a bindable property of the source component changes. This works for standard and custom (dynamic) properties.
.source |
The component that fired this event |
.newValue |
The new value that this property changed to. |
.oldValue |
The value that this property was before it changed. Note that not all components include an accurate oldValue in their events. |
.propertyName |
The name of the property that changed. NOTE: remember to always filter out these events for the property that you are looking for! Components often have many properties that change. |
The following examples may need to be modifed to match the table and column names in your database.
# Inserts a note using the three default tables: notes, users, and itemNotes.
# Also stores only the file name in the database instead of the full path to the file
# Assumes a User ID is used in the notes table
# determine the ID for the logged in user
user
=
system.db.runScalarPrepQuery(
"SELECT id from users where username = ?"
, [system.security.getUsername()])
# determine if a file is being attached
if
filename
is
None
:
# a file was not attached, provide a blank for the bytes
attachmentBytes
=
None
else
:
# get the bytes of the file at the path the user selects
attachmentBytes
=
system.
file
.readFileAsBytes(filename)
# splits the file name from the file path. This way we can show just the file name on the component
# Using '\' as a delimiter, but python requires 2 since it's an escape character
pathAndFile
=
filename.rsplit(
'\\'
,
1
)
filename
=
pathAndFile[
1
]
# build the query and the arguments
query
=
"INSERT INTO Notes (note, whoid, tstamp, attachment, filename, sticky) VALUES (?, ?, CURRENT_TIMESTAMP, ?, ?, ?)"
arguments
=
[note, user, attachmentBytes, filename, sticky]
# insert the note
insertId
=
system.db.runPrepUpdate(query, arguments)
# insert a row onto the itemNotes table
# replace 'MYID' with the proper code to fetch your id
myId
=
'MYID'
system.db.runPrepUpdate(
"INSERT INTO ItemNotes (AccountId, NoteId) VALUES (?, ?)"
, [myId, insertId])
# Similar to the above example, but only a single database table is required.
# Assumes a User Name is used in the notes table
# determine the name for the logged in user
user
=
system.security.getUsername()
# determine if a file is being attached
if
filename
is
None
:
# a file was not attached, provide a blank for the bytes
attachmentBytes
=
None
else
:
# get the bytes of the file at the path the user selects
attachmentBytes
=
system.
file
.readFileAsBytes(filename)
# splits the file name from the file path. This way we can show just the file name on the component
# Using '\' as a delimiter, but python requires 2 since it's an escape character
pathAndFile
=
filename.rsplit(
'\\'
,
1
)
filename
=
pathAndFile[
1
]
# insert the note
query
=
"INSERT INTO Notes (note, whoid, tstamp, attachment, filename, sticky) VALUES (?, ?, CURRENT_TIMESTAMP, ?, ?, ?)"
#if the note will be stored in SQL Server, then the attachment will need to be converted into a VarBinary. Use the following query instead
#query = = "INSERT INTO Notes (note, whoid, tstamp, attachment, filename, sticky) VALUES (?, ?, CURRENT_TIMESTAMP, ?, CONVERT(VARBINARY(MAX),?), ?)"
arguments
=
[note, user, attachmentBytes, filename, sticky]
system.db.runPrepUpdate(query, arguments)