The Web Console components for the JGrapes framework provide the
diff --git a/javadoc-webconsole/org/jgrapes/webconlet/examples/formtest/FormTestConlet.html b/javadoc-webconsole/org/jgrapes/webconlet/examples/formtest/FormTestConlet.html
index 0f336642db..ecb52d653d 100644
--- a/javadoc-webconsole/org/jgrapes/webconlet/examples/formtest/FormTestConlet.html
+++ b/javadoc-webconsole/org/jgrapes/webconlet/examples/formtest/FormTestConlet.html
@@ -1,11 +1,11 @@
Invokes the functions defined in data-jgwc-on-action
attributes of the tree with root container. If close
is true also executes the "on unload" functions. Must
be invoked by edit or modal dialogs when they are closed.
Sends a JSON RPC notification to the server with method
notifyConletModel and as parameters the given conletId,
the method and the additional arguments.
Parameters
conletId: string
the id of the conlet to send to
method: string
the method to invoke
...args: any[]
additional arguments to send
-
Returns void
openModalDialog
openModalDialog( Â Â Â Â conletType:string, Â Â Â Â conletId:string, Â Â Â Â content:string, Â Â Â Â options:ModalDialogOptions, ):void
Opens a modal dialog related to the conlet with the given
+
Returns void
openModalDialog
openModalDialog( Â Â Â Â conletType:string, Â Â Â Â conletId:string, Â Â Â Â content:string, Â Â Â Â options:ModalDialogOptions, ):void
Opens a modal dialog related to the conlet with the given
type and id.
Removes a conlet preview by invoking the respective methods
+
Returns void
removePreview
removePreview(conletId:string):void
Removes a conlet preview by invoking the respective methods
of the associated renderer. If a view of the conlet is
displayed, it will be removed, too.
After un-displaying the conlet, any "execOnUnload" functions
@@ -94,7 +94,7 @@
notifications for the component conlets have as additional
argument the collected properties.
Parameters
conletId: string
the conlet id
-
Returns void
removeView
removeView(conletId:string):void
Removes a conlet view by invoking the respective methods
+
Returns void
removeView
removeView(conletId:string):void
Removes a conlet view by invoking the respective methods
of the associated renderer.
After un-displaying the conlet, any "execOnUnload" functions
in the conlet tree are invoked (depth first) and JSON RPC
@@ -104,22 +104,22 @@
notifications for the component conlets have as additional
argument the collected properties.
diff --git a/javadoc-webconsole/org/jgrapes/webconsole/base/jsdoc/classes/DefaultConlet.html b/javadoc-webconsole/org/jgrapes/webconsole/base/jsdoc/classes/DefaultConlet.html
index f9f043fe0a..afc0a7cdbb 100644
--- a/javadoc-webconsole/org/jgrapes/webconsole/base/jsdoc/classes/DefaultConlet.html
+++ b/javadoc-webconsole/org/jgrapes/webconsole/base/jsdoc/classes/DefaultConlet.html
@@ -3,17 +3,17 @@
conlet and a dataset attribute conlet-id. Different kinds
of conlet representationa are distinguished by additional classes
conlet-preview, conlet-view and conlet-content.
-
diff --git a/javadoc-webconsole/org/jgrapes/webconsole/base/jsdoc/classes/JGConsole.html b/javadoc-webconsole/org/jgrapes/webconsole/base/jsdoc/classes/JGConsole.html
index bd7f4dc672..f23bb5a276 100644
--- a/javadoc-webconsole/org/jgrapes/webconsole/base/jsdoc/classes/JGConsole.html
+++ b/javadoc-webconsole/org/jgrapes/webconsole/base/jsdoc/classes/JGConsole.html
@@ -4,7 +4,7 @@
may also be imported individually.
The class is also registered as window.JGConsole for non-modular
JavaScript.
Returns the data (data-* Attribute) of the specified
element with the given key. If it does not exist, it
set to the value provided by the supplier function.
forLang( Â Â Â Â items:Map<string,string>|Map<string,Map<string,string>>, Â Â Â Â lang:string, Â Â Â Â fallback?:string, ):null|string|Map<string,string>
Finds the lang specific item in a map of items by language.
+
forLang( Â Â Â Â items:Map<string,string>|Map<string,Map<string,string>>, Â Â Â Â lang:string, Â Â Â Â fallback?:string, ):null|string|Map<string,string>
Finds the lang specific item in a map of items by language.
The function first tests for a property as specified by lang,
then removes any trailing "-..." from lang and tries again.
If not successful, it tests for an entry using "en" and
@@ -51,8 +51,8 @@
localize( Â Â Â Â l10ns:Map<string,Map<string,string>>, Â Â Â Â lang:string, Â Â Â Â key:string, Â Â Â Â fallback?:string, ):string
Localizes the given key, using the provided localizations and language.
First, the implementation looks up a mapping using
forLang(l10ns, lang, fallback). Then it looks for an entry for key.
If none is found, it returns the value of key.
diff --git a/javadoc-webconsole/org/jgrapes/webconsole/base/jsdoc/classes/Renderer.html b/javadoc-webconsole/org/jgrapes/webconsole/base/jsdoc/classes/Renderer.html
index 3a0c5595ce..5f1f267e75 100644
--- a/javadoc-webconsole/org/jgrapes/webconsole/base/jsdoc/classes/Renderer.html
+++ b/javadoc-webconsole/org/jgrapes/webconsole/base/jsdoc/classes/Renderer.html
@@ -2,7 +2,7 @@
creates the DOM for the SPA, usually based on some initial DOM from
the console page. It also provides methods for the management of the
conlets' representations in the DOM tree.
-
addConletType( Â Â Â Â conletType:string, Â Â Â Â displayNames:Map<string,string>, Â Â Â Â renderModes:RenderMode[], Â Â Â Â pageComponents:PageComponentSpecification[], ):void
Called from the Console when a new conlet type is added.
addConletType( Â Â Â Â conletType:string, Â Â Â Â displayNames:Map<string,string>, Â Â Â Â renderModes:RenderMode[], Â Â Â Â pageComponents:PageComponentSpecification[], ):void
Called from the Console when a new conlet type is added.
Called by the Console to close a modal dialog
when a corrsponding request is received from the server.
Parameters
container: HTMLElement
the container for the dialog
-
Returns void
connectionLost
connectionLost():void
Called by the console instance when the connection to the server is
+
Returns void
connectionLost
connectionLost():void
Called by the console instance when the connection to the server is
lost. The default implementation logs a warning message.
Should be overridden by a method that displays a notification.
-
Returns void
connectionRestored
connectionRestored():void
Called from the console when the connection to the server is restored.
+
Returns void
connectionRestored
connectionRestored():void
Called from the console when the connection to the server is restored.
The default implementation logs a warning message.
Should be overridden by a method that displays a notification.
-
Returns void
connectionSuspended
connectionSuspended(resume:()=>void):void
Called from the console when the connection to the server is
+
Returns void
connectionSuspended
connectionSuspended(resume:()=>void):void
Called from the console when the connection to the server is
suspended. The default implementation logs a warning message.
Should be overridden by a method that displays a modal dialog.
The connection can be resumed by invoking the function passed
as argument.
Parameters
resume: ()=>void
called when resuming
-
Returns void
consoleConfigured
consoleConfigured():void
Called from the console when the consoleConfigured notification
+
Returns void
consoleConfigured
consoleConfigured():void
Called from the console when the consoleConfigured notification
is received from the server.
The default implementation logs a warning message.
Should be overridden by a method that displays a modal dialog.
Find the conlet representations that display content.
The default implementation wraps all nodes that match
body .conlet.conlet-content. If a conletId is specified,
the the result set is restricted to conlets with this id.
Find the conlet representation that displays the preview of the
conlet with the given id. The default implementation wraps
the node with .conlet-preview[data-conlet-id='<conletId>'.
Find the conlets that display the preview, view or content of
the conlet with the given id. The default implementation wraps
all nodes with .conlet[data-conlet-id='<conletId>' in
DefaultConlets.
Find the conlet representation that displays the view of the
conlet with the given id. The default implementation returns
all nodes with .conlet-view[data-conlet-id='<conletId>'.
Find conlet representations that display content and
are embedded in the given conlet (which is most likely a view
or preview, but may also be a content representation).
The default implementation wraps all nodes (with the exception
@@ -87,30 +87,30 @@
.conlet.conlet-content and returns them in a depth first order.
Find the HTML element that displays a modal dialog associated
+
findModalDialog
findModalDialog(conletId:string):null|HTMLElement
Find the HTML element that displays a modal dialog associated
with the conlet with the given id. The default implementation returns
all nodes with .conlet-modal-dialog[data-conlet-id='<conletId>'.
The container has a generated dialog id and attributes
data-conlet-type and data-conlet-id. If
ModalDialogOptions.useSubmit is true, the submit button
@@ -121,22 +121,22 @@
Called from the Console to update a conlet that is used
to provide content. The default implementation removes all children
from the container and inserts the new content.
updateConletPreview( Â Â Â Â isNew:boolean, Â Â Â Â conlet:Conlet, Â Â Â Â modes:RenderMode[], Â Â Â Â content:HTMLElement[], Â Â Â Â foreground:boolean, ):void
Called from the Console to update the preview of
+
Returns void
updateConletPreview
updateConletPreview( Â Â Â Â isNew:boolean, Â Â Â Â conlet:Conlet, Â Â Â Â modes:RenderMode[], Â Â Â Â content:HTMLElement[], Â Â Â Â foreground:boolean, ):void
Called from the Console to update the preview of
the given conlet. If the preview is new (no preview with the
given conlet id exists), the console provides DOM for the
container as a convenience. Implementations of this method
@@ -155,17 +155,17 @@
usually inserted into the container
foreground: boolean
true if the preview (i.e. the overview
plane) is to be made the active tab
Called from the Console when a conlet type is removed.
The expected action of the renderer is to remove the
conlet type from e.g. a menu that allows adding conlets.
However, front-ends can also force a reload of the application.
updateConletView( Â Â Â Â isNew:boolean, Â Â Â Â conlet:Conlet, Â Â Â Â modes:RenderMode[], Â Â Â Â content:HTMLElement[], Â Â Â Â foreground:boolean, ):void
Called from the Console to update the view of the given conlet.
+
Returns void
updateConletView
updateConletView( Â Â Â Â isNew:boolean, Â Â Â Â conlet:Conlet, Â Â Â Â modes:RenderMode[], Â Â Â Â content:HTMLElement[], Â Â Â Â foreground:boolean, ):void
Called from the Console to update the view of the given conlet.
If the view is new (no view with the
given conlet id exists), the console provides DOM for the
container as a convenience. Implementations of this method
@@ -182,7 +182,7 @@
content: HTMLElement[]
the view content, usually inserted into the container
diff --git a/javadoc-webconsole/org/jgrapes/webconsole/base/jsdoc/classes/TableController.html b/javadoc-webconsole/org/jgrapes/webconsole/base/jsdoc/classes/TableController.html
index 730ab1389d..464e79def8 100644
--- a/javadoc-webconsole/org/jgrapes/webconsole/base/jsdoc/classes/TableController.html
+++ b/javadoc-webconsole/org/jgrapes/webconsole/base/jsdoc/classes/TableController.html
@@ -2,7 +2,7 @@
the available columns and maintains state regarding their
sort order and direction. In addition, it supports simple
filtering based on cell content.
-
A convenience function that inserts word breaks
(​) before every dot in the given text
and returns the result.
Parameters
text: string
the text
-
Returns string
clearFilter
clearFilter(event:Event):void
A convenience method for clearing an input element
+
Returns string
clearFilter
clearFilter(event:Event):void
A convenience method for clearing an input element
that is used to specify a filter. Searches for
an input element in the event.target's enclosing
elements and sets its value to the empty string.
Parameters
event: Event
the event
-
Returns void
filter
filter(data:any[]):any[]
Sort and filter the given data according to the current state
+
Returns void
filter
filter(data:any[]):any[]
Sort and filter the given data according to the current state
of the controller. Returns the sorted data.
-
Parameters
data: any[]
Returns any[]
filterBy
filterBy(filter:string):void
Sets a filter for the data.
+
Parameters
data: any[]
Returns any[]
filterBy
filterBy(filter:string):void
Sets a filter for the data.
Parameters
filter: string
the string to match
-
Returns void
label
label(key:string):undefined|string
Returns the column label for the given column key.
+
Returns void
label
label(key:string):undefined|string
Returns the column label for the given column key.
Parameters
key: string
the column key
-
Returns undefined|string
sortBy
sortBy(key:string,order?:string):void
This method sets the primary sort key. If the order is
+
Returns undefined|string
sortBy
sortBy(key:string,order?:string):void
This method sets the primary sort key. If the order is
undefined, and the current sort key is the same as the
specified key, the current sort order is inverted.
Parameters
key: string
the column key
Optionalorder: string
the sort order ('up' for ascending
and 'down' for descending) or undefined
-
Returns void
sortedByAsc
sortedByAsc(key:string):boolean
Returns true if given key is the current sort key
+
Returns void
sortedByAsc
sortedByAsc(key:string):boolean
Returns true if given key is the current sort key
and the current sort order for is ascending.
Parameters
key: string
the column key
-
Returns boolean
sortedByDesc
sortedByDesc(key:string):boolean
Returns true if given key is the current sort key
+
Returns boolean
sortedByDesc
sortedByDesc(key:string):boolean
Returns true if given key is the current sort key
and the current sort order for is descending.
Parameters
key: string
the column key
-
Returns boolean
sortOrder
sortOrder(key:string):undefined|number
Returns the sort order of the column with the given key
+
Returns boolean
sortOrder
sortOrder(key:string):undefined|number
Returns the sort order of the column with the given key
(1 for "up" and -1 for "down").
Parameters
key: string
the column key
-
Returns undefined|number
updateFilter
updateFilter(event:Event):void
A convenience method to update the filter from the
+
Returns undefined|number
updateFilter
updateFilter(event:Event):void
A convenience method to update the filter from the
value of the passed in event.
Parameters
event: Event
the event which must have a currentTarget
with a value attribute.
Sets the allowed conlet types based on user roles.
By default, system components (e.g., policies) can add conlets,
but users cannot. This method allows changing that behavior.
The parameter is a Map<String, List<String>> where each role
@@ -320,7 +320,7 @@
If a role is withdrawn from a user, there may still be conlets in
his stored layout that he is no longer allowed to use.
diff --git a/javadoc-webconsole/org/jgrapes/webconsole/rbac/UserLogger.html b/javadoc-webconsole/org/jgrapes/webconsole/rbac/UserLogger.html
index 8e02b8b5c4..1d180d5ad2 100644
--- a/javadoc-webconsole/org/jgrapes/webconsole/rbac/UserLogger.html
+++ b/javadoc-webconsole/org/jgrapes/webconsole/rbac/UserLogger.html
@@ -1,11 +1,11 @@
-
+
UserLogger
-
+
diff --git a/javadoc-webconsole/org/jgrapes/webconsole/rbac/class-use/RoleConfigurator.html b/javadoc-webconsole/org/jgrapes/webconsole/rbac/class-use/RoleConfigurator.html
index ab6c1fc147..9ae76281ae 100644
--- a/javadoc-webconsole/org/jgrapes/webconsole/rbac/class-use/RoleConfigurator.html
+++ b/javadoc-webconsole/org/jgrapes/webconsole/rbac/class-use/RoleConfigurator.html
@@ -1,11 +1,11 @@
-
+
Uses of Class org.jgrapes.webconsole.rbac.RoleConfigurator
-
+
diff --git a/javadoc-webconsole/org/jgrapes/webconsole/rbac/class-use/RoleConletFilter.html b/javadoc-webconsole/org/jgrapes/webconsole/rbac/class-use/RoleConletFilter.html
index 24e243e987..5a70de5604 100644
--- a/javadoc-webconsole/org/jgrapes/webconsole/rbac/class-use/RoleConletFilter.html
+++ b/javadoc-webconsole/org/jgrapes/webconsole/rbac/class-use/RoleConletFilter.html
@@ -1,11 +1,11 @@
-
+
Uses of Class org.jgrapes.webconsole.rbac.RoleConletFilter
-
+
diff --git a/javadoc-webconsole/org/jgrapes/webconsole/rbac/class-use/UserLogger.html b/javadoc-webconsole/org/jgrapes/webconsole/rbac/class-use/UserLogger.html
index 3fd7dab9a7..697543b350 100644
--- a/javadoc-webconsole/org/jgrapes/webconsole/rbac/class-use/UserLogger.html
+++ b/javadoc-webconsole/org/jgrapes/webconsole/rbac/class-use/UserLogger.html
@@ -1,11 +1,11 @@
-
+
Uses of Class org.jgrapes.webconsole.rbac.UserLogger
-
+
diff --git a/javadoc-webconsole/org/jgrapes/webconsole/rbac/package-summary.html b/javadoc-webconsole/org/jgrapes/webconsole/rbac/package-summary.html
index d9a7d5ff2c..18665f5dd1 100644
--- a/javadoc-webconsole/org/jgrapes/webconsole/rbac/package-summary.html
+++ b/javadoc-webconsole/org/jgrapes/webconsole/rbac/package-summary.html
@@ -1,11 +1,11 @@
-
+
org.jgrapes.webconsole.rbac
-
+
diff --git a/javadoc-webconsole/org/jgrapes/webconsole/rbac/package-tree.html b/javadoc-webconsole/org/jgrapes/webconsole/rbac/package-tree.html
index 605bda527e..3b0c1094e6 100644
--- a/javadoc-webconsole/org/jgrapes/webconsole/rbac/package-tree.html
+++ b/javadoc-webconsole/org/jgrapes/webconsole/rbac/package-tree.html
@@ -1,11 +1,11 @@
-
+
org.jgrapes.webconsole.rbac Class Hierarchy
-
+
diff --git a/javadoc-webconsole/org/jgrapes/webconsole/rbac/package-use.html b/javadoc-webconsole/org/jgrapes/webconsole/rbac/package-use.html
index 18bbc03ac7..6050aebc28 100644
--- a/javadoc-webconsole/org/jgrapes/webconsole/rbac/package-use.html
+++ b/javadoc-webconsole/org/jgrapes/webconsole/rbac/package-use.html
@@ -1,11 +1,11 @@
-
+
Uses of Package org.jgrapes.webconsole.rbac
-
+
diff --git a/javadoc-webconsole/org/jgrapes/webconsole/vuejs/VueJsConsoleWeblet.html b/javadoc-webconsole/org/jgrapes/webconsole/vuejs/VueJsConsoleWeblet.html
index afc0013438..316b95b6b6 100644
--- a/javadoc-webconsole/org/jgrapes/webconsole/vuejs/VueJsConsoleWeblet.html
+++ b/javadoc-webconsole/org/jgrapes/webconsole/vuejs/VueJsConsoleWeblet.html
@@ -1,11 +1,11 @@
-
+
VueJsConsoleWeblet
-
+
diff --git a/javadoc-webconsole/org/jgrapes/webconsole/vuejs/class-use/VueJsConsoleWeblet.html b/javadoc-webconsole/org/jgrapes/webconsole/vuejs/class-use/VueJsConsoleWeblet.html
index a260192b7c..d06ad3ac2d 100644
--- a/javadoc-webconsole/org/jgrapes/webconsole/vuejs/class-use/VueJsConsoleWeblet.html
+++ b/javadoc-webconsole/org/jgrapes/webconsole/vuejs/class-use/VueJsConsoleWeblet.html
@@ -1,11 +1,11 @@
-
+
Uses of Class org.jgrapes.webconsole.vuejs.VueJsConsoleWeblet
-
+
diff --git a/javadoc-webconsole/org/jgrapes/webconsole/vuejs/package-summary.html b/javadoc-webconsole/org/jgrapes/webconsole/vuejs/package-summary.html
index 518bb6931e..5073e9a7ad 100644
--- a/javadoc-webconsole/org/jgrapes/webconsole/vuejs/package-summary.html
+++ b/javadoc-webconsole/org/jgrapes/webconsole/vuejs/package-summary.html
@@ -1,11 +1,11 @@
-
+
org.jgrapes.webconsole.vuejs
-
+
diff --git a/javadoc-webconsole/org/jgrapes/webconsole/vuejs/package-tree.html b/javadoc-webconsole/org/jgrapes/webconsole/vuejs/package-tree.html
index ff6f2d1642..a6898f9a70 100644
--- a/javadoc-webconsole/org/jgrapes/webconsole/vuejs/package-tree.html
+++ b/javadoc-webconsole/org/jgrapes/webconsole/vuejs/package-tree.html
@@ -1,11 +1,11 @@
-
+
org.jgrapes.webconsole.vuejs Class Hierarchy
-
+
diff --git a/javadoc-webconsole/org/jgrapes/webconsole/vuejs/package-use.html b/javadoc-webconsole/org/jgrapes/webconsole/vuejs/package-use.html
index 1d895385f6..4aff913c4f 100644
--- a/javadoc-webconsole/org/jgrapes/webconsole/vuejs/package-use.html
+++ b/javadoc-webconsole/org/jgrapes/webconsole/vuejs/package-use.html
@@ -1,11 +1,11 @@
-
+
Uses of Package org.jgrapes.webconsole.vuejs
-
+
diff --git a/javadoc-webconsole/overview-summary.html b/javadoc-webconsole/overview-summary.html
index 09bb688e3b..cd16c75922 100644
--- a/javadoc-webconsole/overview-summary.html
+++ b/javadoc-webconsole/overview-summary.html
@@ -1,11 +1,11 @@
-
+
Generated Documentation (Untitled)
-
+
diff --git a/javadoc-webconsole/overview-tree.html b/javadoc-webconsole/overview-tree.html
index 3b920861ce..5c6e56b7d8 100644
--- a/javadoc-webconsole/overview-tree.html
+++ b/javadoc-webconsole/overview-tree.html
@@ -1,11 +1,11 @@
-
+
Class Hierarchy
-
+
diff --git a/javadoc-webconsole/search.html b/javadoc-webconsole/search.html
index 5dcddcba33..98d1f1bc48 100644
--- a/javadoc-webconsole/search.html
+++ b/javadoc-webconsole/search.html
@@ -1,11 +1,11 @@
-
+
Search
-
+
diff --git a/javadoc-webconsole/serialized-form.html b/javadoc-webconsole/serialized-form.html
index 91b2f5a7fa..a48cebbe98 100644
--- a/javadoc-webconsole/serialized-form.html
+++ b/javadoc-webconsole/serialized-form.html
@@ -1,11 +1,11 @@
-
+
Serialized Form
-
+
diff --git a/javadoc-webconsole/src-html/org/jgrapes/webconlet/logviewer/LogViewerConlet.html b/javadoc-webconsole/src-html/org/jgrapes/webconlet/logviewer/LogViewerConlet.html
index 7ebacfbf5b..03140db1ba 100644
--- a/javadoc-webconsole/src-html/org/jgrapes/webconlet/logviewer/LogViewerConlet.html
+++ b/javadoc-webconsole/src-html/org/jgrapes/webconlet/logviewer/LogViewerConlet.html
@@ -191,25 +191,26 @@
177 * @see org.jgrapes.console.AbstractConlet#doNotifyConletModel178 */179 @Override
-180 @SuppressWarnings({ "PMD.SwitchStmtsShouldHaveDefault",
-181 "PMD.TooFewBranchesForASwitchStatement" })
-182 protected void doUpdateConletState(NotifyConletModel event,
-183 ConsoleConnection channel, Serializable conletState)
-184 throws Exception {
-185 event.stop();
-186 switch (event.method()) {
-187 case "resync":
-188 sendAllEntries(channel, event.conletId());
-189 break;
-190 }
-191 }
-192
-193 @Override
-194 protected boolean doSetLocale(SetLocale event, ConsoleConnection channel,
-195 String conletId) throws Exception {
-196 return true;
-197 }
-198}
+180 @SuppressWarnings({ "PMD.TooFewBranchesForSwitch" })
+181 protected void doUpdateConletState(NotifyConletModel event,
+182 ConsoleConnection channel, Serializable conletState)
+183 throws Exception {
+184 event.stop();
+185 switch (event.method()) {
+186 case "resync":
+187 sendAllEntries(channel, event.conletId());
+188 break;
+189 default:
+190 break;
+191 }
+192 }
+193
+194 @Override
+195 protected boolean doSetLocale(SetLocale event, ConsoleConnection channel,
+196 String conletId) throws Exception {
+197 return true;
+198 }
+199}
diff --git a/javadoc-webconsole/src-html/org/jgrapes/webconsole/base/events/DisplayNotification.html b/javadoc-webconsole/src-html/org/jgrapes/webconsole/base/events/DisplayNotification.html
index 80cadc30bb..929b641c01 100644
--- a/javadoc-webconsole/src-html/org/jgrapes/webconsole/base/events/DisplayNotification.html
+++ b/javadoc-webconsole/src-html/org/jgrapes/webconsole/base/events/DisplayNotification.html
@@ -107,10 +107,10 @@
093094 @Override095 public void emitJson(Writer writer) throws IOException {
-096 options.put("destroyOnClose", true);
-097 emitJson(writer, "displayNotification", content(), options);
-098 }
-099
+096 var extOpts = new HashMap<>(options);
+097 extOpts.put("destroyOnClose", true);
+098 emitJson(writer, "displayNotification", content(), extOpts);
+099 }100}
diff --git a/javadoc-webconsole/src-html/org/jgrapes/webconsole/examples/consoleapp/ConsoleApp.html b/javadoc-webconsole/src-html/org/jgrapes/webconsole/examples/consoleapp/ConsoleApp.html
index 9923f64b8c..e180b2f9cb 100644
--- a/javadoc-webconsole/src-html/org/jgrapes/webconsole/examples/consoleapp/ConsoleApp.html
+++ b/javadoc-webconsole/src-html/org/jgrapes/webconsole/examples/consoleapp/ConsoleApp.html
@@ -174,63 +174,64 @@
160 return sslContext;161 }162
-163 @SuppressWarnings("PMD.TooFewBranchesForASwitchStatement")
-164 private void createVueJsConsole(HttpServer httpServer)
-165 throws URISyntaxException {
-166 ConsoleWeblet consoleWeblet
-167 = httpServer.attach(new VueJsConsoleWeblet(httpServer.channel(),
-168 SELF, new URI("/")))
-169 .prependClassTemplateLoader(this.getClass())
-170 .prependResourceBundleProvider(ConsoleApp.class)
-171 .prependConsoleResourceProvider(ConsoleApp.class);
-172 WebConsole console = consoleWeblet.console();
-173 // consoleWeblet.setConnectionInactivityTimeout(300_000);
-174 console.attach(new BrowserLocalBackedKVStore(
-175 console.channel(), consoleWeblet.prefix().getPath()));
-176 console.attach(new KVStoreBasedConsolePolicy(console.channel()));
-177 // Add all available page resource providers
-178 console.attach(new ComponentCollector<>(
-179 PageResourceProviderFactory.class, console.channel()));
-180 // Add all available conlets
-181 console.attach(new ComponentCollector<>(
-182 ConletComponentFactory.class, console.channel(), type -> {
-183 switch (type) {
-184 case "org.jgrapes.webconlet.examples.login.LoginConlet":
-185 return Collections.emptyList();
-186 default:
-187 return Arrays.asList(Collections.emptyMap());
-188 }
-189 }));
-190 }
-191
-192 /**
-193 * Stop the application.
-194 * @throws Exception
-195 *
-196 * @throws Exception the exception
-197 */
-198 @SuppressWarnings("PMD.SignatureDeclareThrowsException")
-199 public void stop() throws Exception {
-200 app.fire(new Stop(), BROADCAST);
-201 Components.awaitExhaustion();
-202 }
-203
-204 /**
-205 * @param args
-206 * @throws IOException
-207 * @throws InterruptedException
-208 * @throws NoSuchAlgorithmException
-209 * @throws KeyStoreException
-210 * @throws UnrecoverableKeyException
-211 * @throws CertificateException
-212 * @throws KeyManagementException
-213 */
-214 @SuppressWarnings("PMD.SignatureDeclareThrowsException")
-215 public static void main(String[] args) throws Exception {
-216 new ConsoleApp().start();
-217 }
-218
-219}
+163 @SuppressWarnings({ "PMD.TooFewBranchesForASwitchStatement",
+164 "PMD.TooFewBranchesForSwitch" })
+165 private void createVueJsConsole(HttpServer httpServer)
+166 throws URISyntaxException {
+167 ConsoleWeblet consoleWeblet
+168 = httpServer.attach(new VueJsConsoleWeblet(httpServer.channel(),
+169 SELF, new URI("/")))
+170 .prependClassTemplateLoader(this.getClass())
+171 .prependResourceBundleProvider(ConsoleApp.class)
+172 .prependConsoleResourceProvider(ConsoleApp.class);
+173 WebConsole console = consoleWeblet.console();
+174 // consoleWeblet.setConnectionInactivityTimeout(300_000);
+175 console.attach(new BrowserLocalBackedKVStore(
+176 console.channel(), consoleWeblet.prefix().getPath()));
+177 console.attach(new KVStoreBasedConsolePolicy(console.channel()));
+178 // Add all available page resource providers
+179 console.attach(new ComponentCollector<>(
+180 PageResourceProviderFactory.class, console.channel()));
+181 // Add all available conlets
+182 console.attach(new ComponentCollector<>(
+183 ConletComponentFactory.class, console.channel(), type -> {
+184 switch (type) {
+185 case "org.jgrapes.webconlet.examples.login.LoginConlet":
+186 return Collections.emptyList();
+187 default:
+188 return Arrays.asList(Collections.emptyMap());
+189 }
+190 }));
+191 }
+192
+193 /**
+194 * Stop the application.
+195 * @throws Exception
+196 *
+197 * @throws Exception the exception
+198 */
+199 @SuppressWarnings("PMD.SignatureDeclareThrowsException")
+200 public void stop() throws Exception {
+201 app.fire(new Stop(), BROADCAST);
+202 Components.awaitExhaustion();
+203 }
+204
+205 /**
+206 * @param args
+207 * @throws IOException
+208 * @throws InterruptedException
+209 * @throws NoSuchAlgorithmException
+210 * @throws KeyStoreException
+211 * @throws UnrecoverableKeyException
+212 * @throws CertificateException
+213 * @throws KeyManagementException
+214 */
+215 @SuppressWarnings("PMD.SignatureDeclareThrowsException")
+216 public static void main(String[] args) throws Exception {
+217 new ConsoleApp().start();
+218 }
+219
+220}
diff --git a/javadoc-webconsole/src-html/org/jgrapes/webconsole/rbac/RoleConletFilter.html b/javadoc-webconsole/src-html/org/jgrapes/webconsole/rbac/RoleConletFilter.html
index 2f430f8465..84360be913 100644
--- a/javadoc-webconsole/src-html/org/jgrapes/webconsole/rbac/RoleConletFilter.html
+++ b/javadoc-webconsole/src-html/org/jgrapes/webconsole/rbac/RoleConletFilter.html
@@ -33,290 +33,291 @@
019package org.jgrapes.webconsole.rbac;020021import java.util.Collections;
-022import java.util.HashMap;
-023import java.util.HashSet;
-024import java.util.List;
-025import java.util.Map;
-026import java.util.Set;
-027import java.util.stream.Collectors;
-028import org.jgrapes.core.Channel;
-029import org.jgrapes.core.Component;
-030import org.jgrapes.core.Event;
-031import org.jgrapes.core.Manager;
-032import org.jgrapes.core.annotation.Handler;
-033import org.jgrapes.util.events.ConfigurationUpdate;
-034import org.jgrapes.webconsole.base.ConsoleConnection;
-035import org.jgrapes.webconsole.base.ConsoleRole;
-036import org.jgrapes.webconsole.base.WebConsoleUtils;
-037import org.jgrapes.webconsole.base.events.AddConletRequest;
-038import org.jgrapes.webconsole.base.events.AddConletType;
-039import org.jgrapes.webconsole.base.events.ConsolePrepared;
-040import org.jgrapes.webconsole.base.events.DeleteConlet;
-041import org.jgrapes.webconsole.base.events.RenderConletRequest;
-042import org.jgrapes.webconsole.base.events.UpdateConletType;
-043
-044/**
-045 * Configures the conlets available based on the user currently logged in.
-046 */
-047@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
-048public class RoleConletFilter extends Component {
-049
-050 /**
-051 * The possible permissions.
-052 */
-053 private enum Permission {
-054 ADD, RENDER
-055 }
-056
-057 @SuppressWarnings("PMD.UseConcurrentHashMap")
-058 private final Map<String, List<String>> acl = new HashMap<>();
-059 private final Set<String> knownTypes = new HashSet<>();
-060
-061 /**
-062 * Creates a new component with its channel set to the given
-063 * channel.
-064 *
-065 * @param componentChannel the channel that the component's
-066 * handlers listen on by default and that
-067 * {@link Manager#fire(Event, Channel...)} sends the event to
-068 */
-069 public RoleConletFilter(Channel componentChannel) {
-070 super(componentChannel);
-071 }
-072
-073 /**
-074 * Creates a new component with its channel set to the given
-075 * channel.
-076 *
-077 * Supported properties are:
-078 *
-079 * * *conletTypesByRole*: see {@link #setConletTypesByRole(Map)}.
-080 *
-081 * @param componentChannel the channel that the component's
-082 * handlers listen on by default and that
-083 * {@link Manager#fire(Event, Channel...)} sends the event to
-084 * @param properties the properties used to configure the component
-085 */
-086 @SuppressWarnings({ "PMD.ConstructorCallsOverridableMethod", "unchecked",
-087 "PMD.AvoidDuplicateLiterals" })
-088 public RoleConletFilter(Channel componentChannel,
-089 Map<?, ?> properties) {
-090 super(componentChannel);
-091 setConletTypesByRole((Map<String, List<String>>) properties
-092 .get("conletTypesByRole"));
-093 }
-094
-095 /**
-096 * Sets the allowed conlet types based on user roles.
-097 *
-098 * By default, system components (e.g., policies) can add conlets,
-099 * but users cannot. This method allows changing that behavior.
-100 * The parameter is a `Map<String, List<String>>` where each role
-101 * maps to a list of conlet types that authorized users with that
-102 * role can add.
-103 *
-104 * If a conlet type is prefixed with double minus ("`--`"), it
-105 * is also excluded from adding by system components, meaning
-106 * it will never be displayed. Note that this exclusion must be
-107 * specified for all roles a user has, as permissions from
-108 * different roles are combined.
-109 *
-110 * Instead of listing specific conlet types, users can be allowed
-111 * to add any type of conlet by including "`*`" in the list.
-112 * Specific conlet types can be excluded from the wildcard match
-113 * by placing them before the "`*`" in the list and prefixing
-114 * them with a minus ("`-`"), double minus ("`--`"), or an
-115 * exclamation mark ("`!`") (the use of "`!`" is deprecated).
-116 *
-117 * @param acl the acl
-118 * @return the user role conlet filter
-119 */
-120 @SuppressWarnings({ "PMD.LinguisticNaming",
-121 "PMD.AvoidInstantiatingObjectsInLoops" })
-122 public RoleConletFilter
-123 setConletTypesByRole(Map<String, List<String>> acl) {
-124 // Deep copy (and cleanup)
-125 this.acl.clear();
-126 this.acl.putAll(acl);
-127 for (var e : this.acl.entrySet()) {
-128 e.setValue(e.getValue().stream().map(String::trim)
-129 .collect(Collectors.toList()));
-130 }
-131 return this;
-132 }
-133
-134 /**
-135 * The component can be configured with events that include
-136 * a path (see @link {@link ConfigurationUpdate#paths()})
-137 * that matches this components path (see {@link Manager#componentPath()}).
-138 *
-139 * The following properties are recognized:
-140 *
-141 * `conletTypesByRole`
-142 * : Invokes {@link #setConletTypesByRole(Map)} with the
-143 * given values.
-144 *
-145 * @param event the event
-146 */
-147 @SuppressWarnings("unchecked")
-148 @Handler
-149 public void onConfigUpdate(ConfigurationUpdate event) {
-150 event.structured(componentPath())
-151 .map(c -> (Map<String, List<String>>) c
-152 .get("conletTypesByRole"))
-153 .ifPresent(this::setConletTypesByRole);
-154 }
-155
-156 /**
-157 * Collect known types for wildcard handling
-158 *
-159 * @param event the event
-160 */
-161 @Handler
-162 public void onAddConletType(AddConletType event) {
-163 knownTypes.add(event.conletType());
-164 }
-165
-166 /**
-167 * Disable all conlets that the user is not allowed to use
-168 * by firing {@link UpdateConletType} events with no render modes.
-169 * The current user is obtained from
-170 * {@link WebConsoleUtils#userFromSession(org.jgrapes.http.Session)}.
-171 *
-172 * @param event the event
-173 * @param channel the channel
-174 */
-175 @Handler(priority = 800)
-176 @SuppressWarnings({ "PMD.AvoidInstantiatingObjectsInLoops",
-177 "PMD.AvoidLiteralsInIfCondition", "PMD.CognitiveComplexity" })
-178 public void onConsolePrepared(ConsolePrepared event,
-179 ConsoleConnection channel) {
-180 var permissions = new HashMap<String, Set<Permission>>();
-181 for (var conletType : knownTypes) {
-182 var conletPerms = new HashSet<Permission>();
-183 permissions.put(conletType, conletPerms);
-184 for (var role : WebConsoleUtils
-185 .rolesFromSession(channel.session())) {
-186 var perms = permissionsFromRole(conletType, role);
-187 if (perms.isEmpty()) {
-188 continue;
-189 }
-190 logger.fine(() -> "Role " + role.getName() + " allows user "
-191 + WebConsoleUtils.userFromSession(channel.session())
-192 .get().getName()
-193 + " to " + perms + " " + conletType);
-194 conletPerms.addAll(perms);
-195 if (conletPerms.size() == Permission.values().length) {
-196 logger.fine(() -> "User " + WebConsoleUtils
-197 .userFromSession(channel.session()).get().getName()
-198 + " has all possible permissions for " + conletType);
-199 break;
-200 }
-201 }
-202 }
-203
-204 // Disable non-addable conlet types in GUI
-205 for (var e : permissions.entrySet()) {
-206 if (!e.getValue().contains(Permission.ADD)) {
-207 channel.respond(new UpdateConletType(e.getKey()));
-208 }
-209 }
-210 channel.setAssociated(this, permissions);
-211 }
-212
-213 /**
-214 * Evaluate the permissions contributed by the given role for the
-215 * given conlet type.
-216 *
-217 * @param conletType the conlet type
-218 * @param role the role
-219 * @return the sets the
-220 */
-221 @SuppressWarnings("PMD.AvoidBranchingStatementAsLastInLoop")
-222 private Set<Permission> permissionsFromRole(String conletType,
-223 ConsoleRole role) {
-224 var rules = acl.get(role.getName());
-225 if (rules == null) {
-226 // No rules for this role.
-227 return Collections.emptySet();
-228 }
-229 for (var rule : rules) {
-230 // Extract conlet type
-231 int pos = 0;
-232 while (rule.charAt(pos) == '!' || rule.charAt(pos) == '-') {
-233 pos++;
-234 }
-235 if (rule.startsWith("*")) {
-236 return Set.of(Permission.ADD, Permission.RENDER);
-237 }
-238 if (!conletType.equals(rule.substring(pos).trim())) {
-239 // Rule does not apply to this type
-240 continue;
-241 }
-242 if (rule.startsWith("--")) {
-243 return Collections.emptySet();
-244 }
-245 if (rule.startsWith("!") || rule.startsWith("-")) {
-246 return Set.of(Permission.RENDER);
-247 }
-248 // Rule is type name and thus allows everything
-249 return Set.of(Permission.ADD, Permission.RENDER);
-250 }
-251 // Default permissions
-252 return Set.of(Permission.RENDER);
-253 }
-254
-255 /**
-256 * If the request originates from a client
-257 * (see {@link AddConletRequest#isFrontendRequest()}, verifies that
-258 * the user is allowed to create a conlet of the given type.
-259 *
-260 * As the conlets that he user is not allowed to use are disabled,
-261 * it should be impossible to create such requests in the first place.
-262 * However, the frontend code is open to manipulation and therefore
-263 * this additional check is introduced to increase security.
-264 *
-265 * @param event the event
-266 * @param channel the channel
-267 */
-268 @Handler(priority = 1000)
-269 public void onAddConlet(AddConletRequest event, ConsoleConnection channel) {
-270 channel.associated(this, Map.class).ifPresent(aP -> {
-271 @SuppressWarnings("unchecked")
-272 var allPerms = (Map<String, Set<Permission>>) aP;
-273 var perms = allPerms.getOrDefault(event.conletType(),
-274 Collections.emptySet());
-275 if (event.isFrontendRequest() ? !perms.contains(Permission.ADD)
-276 : !perms.contains(Permission.RENDER)) {
-277 event.cancel(true);
-278 }
-279 });
-280 }
-281
-282 /**
-283 * If a role is withdrawn from a user, there may still be conlets in
-284 * his stored layout that he is no longer allowed to use.
-285 *
-286 * @param event the event
-287 * @param channel the channel
-288 */
-289 @Handler(priority = 1000)
-290 public void onRenderConletRequest(RenderConletRequest event,
-291 ConsoleConnection channel) {
-292 channel.associated(this, Map.class).ifPresent(aP -> {
-293 @SuppressWarnings("unchecked")
-294 var allPerms = (Map<String, Set<Permission>>) aP;
-295 var perms = allPerms.getOrDefault(event.conletType(),
-296 Collections.emptySet());
-297 if (perms.isEmpty()) {
-298 event.cancel(true);
-299 // Avoid future rendering of this conlet
-300 fire(new DeleteConlet(event.conletId(),
-301 Collections.emptySet()));
-302 }
-303 });
-304 }
-305}
+022import java.util.EnumSet;
+023import java.util.HashMap;
+024import java.util.HashSet;
+025import java.util.List;
+026import java.util.Map;
+027import java.util.Set;
+028import java.util.stream.Collectors;
+029import org.jgrapes.core.Channel;
+030import org.jgrapes.core.Component;
+031import org.jgrapes.core.Event;
+032import org.jgrapes.core.Manager;
+033import org.jgrapes.core.annotation.Handler;
+034import org.jgrapes.util.events.ConfigurationUpdate;
+035import org.jgrapes.webconsole.base.ConsoleConnection;
+036import org.jgrapes.webconsole.base.ConsoleRole;
+037import org.jgrapes.webconsole.base.WebConsoleUtils;
+038import org.jgrapes.webconsole.base.events.AddConletRequest;
+039import org.jgrapes.webconsole.base.events.AddConletType;
+040import org.jgrapes.webconsole.base.events.ConsolePrepared;
+041import org.jgrapes.webconsole.base.events.DeleteConlet;
+042import org.jgrapes.webconsole.base.events.RenderConletRequest;
+043import org.jgrapes.webconsole.base.events.UpdateConletType;
+044
+045/**
+046 * Configures the conlets available based on the user currently logged in.
+047 */
+048@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
+049public class RoleConletFilter extends Component {
+050
+051 /**
+052 * The possible permissions.
+053 */
+054 private enum Permission {
+055 ADD, RENDER
+056 }
+057
+058 @SuppressWarnings("PMD.UseConcurrentHashMap")
+059 private final Map<String, List<String>> acl = new HashMap<>();
+060 private final Set<String> knownTypes = new HashSet<>();
+061
+062 /**
+063 * Creates a new component with its channel set to the given
+064 * channel.
+065 *
+066 * @param componentChannel the channel that the component's
+067 * handlers listen on by default and that
+068 * {@link Manager#fire(Event, Channel...)} sends the event to
+069 */
+070 public RoleConletFilter(Channel componentChannel) {
+071 super(componentChannel);
+072 }
+073
+074 /**
+075 * Creates a new component with its channel set to the given
+076 * channel.
+077 *
+078 * Supported properties are:
+079 *
+080 * * *conletTypesByRole*: see {@link #setConletTypesByRole(Map)}.
+081 *
+082 * @param componentChannel the channel that the component's
+083 * handlers listen on by default and that
+084 * {@link Manager#fire(Event, Channel...)} sends the event to
+085 * @param properties the properties used to configure the component
+086 */
+087 @SuppressWarnings({ "PMD.ConstructorCallsOverridableMethod", "unchecked",
+088 "PMD.AvoidDuplicateLiterals" })
+089 public RoleConletFilter(Channel componentChannel,
+090 Map<?, ?> properties) {
+091 super(componentChannel);
+092 setConletTypesByRole((Map<String, List<String>>) properties
+093 .get("conletTypesByRole"));
+094 }
+095
+096 /**
+097 * Sets the allowed conlet types based on user roles.
+098 *
+099 * By default, system components (e.g., policies) can add conlets,
+100 * but users cannot. This method allows changing that behavior.
+101 * The parameter is a `Map<String, List<String>>` where each role
+102 * maps to a list of conlet types that authorized users with that
+103 * role can add.
+104 *
+105 * If a conlet type is prefixed with double minus ("`--`"), it
+106 * is also excluded from adding by system components, meaning
+107 * it will never be displayed. Note that this exclusion must be
+108 * specified for all roles a user has, as permissions from
+109 * different roles are combined.
+110 *
+111 * Instead of listing specific conlet types, users can be allowed
+112 * to add any type of conlet by including "`*`" in the list.
+113 * Specific conlet types can be excluded from the wildcard match
+114 * by placing them before the "`*`" in the list and prefixing
+115 * them with a minus ("`-`"), double minus ("`--`"), or an
+116 * exclamation mark ("`!`") (the use of "`!`" is deprecated).
+117 *
+118 * @param acl the acl
+119 * @return the user role conlet filter
+120 */
+121 @SuppressWarnings({ "PMD.LinguisticNaming",
+122 "PMD.AvoidInstantiatingObjectsInLoops" })
+123 public RoleConletFilter
+124 setConletTypesByRole(Map<String, List<String>> acl) {
+125 // Deep copy (and cleanup)
+126 this.acl.clear();
+127 this.acl.putAll(acl);
+128 for (var e : this.acl.entrySet()) {
+129 e.setValue(e.getValue().stream().map(String::trim)
+130 .collect(Collectors.toList()));
+131 }
+132 return this;
+133 }
+134
+135 /**
+136 * The component can be configured with events that include
+137 * a path (see @link {@link ConfigurationUpdate#paths()})
+138 * that matches this components path (see {@link Manager#componentPath()}).
+139 *
+140 * The following properties are recognized:
+141 *
+142 * `conletTypesByRole`
+143 * : Invokes {@link #setConletTypesByRole(Map)} with the
+144 * given values.
+145 *
+146 * @param event the event
+147 */
+148 @SuppressWarnings("unchecked")
+149 @Handler
+150 public void onConfigUpdate(ConfigurationUpdate event) {
+151 event.structured(componentPath())
+152 .map(c -> (Map<String, List<String>>) c
+153 .get("conletTypesByRole"))
+154 .ifPresent(this::setConletTypesByRole);
+155 }
+156
+157 /**
+158 * Collect known types for wildcard handling
+159 *
+160 * @param event the event
+161 */
+162 @Handler
+163 public void onAddConletType(AddConletType event) {
+164 knownTypes.add(event.conletType());
+165 }
+166
+167 /**
+168 * Disable all conlets that the user is not allowed to use
+169 * by firing {@link UpdateConletType} events with no render modes.
+170 * The current user is obtained from
+171 * {@link WebConsoleUtils#userFromSession(org.jgrapes.http.Session)}.
+172 *
+173 * @param event the event
+174 * @param channel the channel
+175 */
+176 @Handler(priority = 800)
+177 @SuppressWarnings({ "PMD.AvoidInstantiatingObjectsInLoops",
+178 "PMD.AvoidLiteralsInIfCondition", "PMD.CognitiveComplexity" })
+179 public void onConsolePrepared(ConsolePrepared event,
+180 ConsoleConnection channel) {
+181 var permissions = new HashMap<String, Set<Permission>>();
+182 for (var conletType : knownTypes) {
+183 var conletPerms = EnumSet.noneOf(Permission.class);
+184 permissions.put(conletType, conletPerms);
+185 for (var role : WebConsoleUtils
+186 .rolesFromSession(channel.session())) {
+187 var perms = permissionsFromRole(conletType, role);
+188 if (perms.isEmpty()) {
+189 continue;
+190 }
+191 logger.fine(() -> "Role " + role.getName() + " allows user "
+192 + WebConsoleUtils.userFromSession(channel.session())
+193 .get().getName()
+194 + " to " + perms + " " + conletType);
+195 conletPerms.addAll(perms);
+196 if (conletPerms.size() == Permission.values().length) {
+197 logger.fine(() -> "User " + WebConsoleUtils
+198 .userFromSession(channel.session()).get().getName()
+199 + " has all possible permissions for " + conletType);
+200 break;
+201 }
+202 }
+203 }
+204
+205 // Disable non-addable conlet types in GUI
+206 for (var e : permissions.entrySet()) {
+207 if (!e.getValue().contains(Permission.ADD)) {
+208 channel.respond(new UpdateConletType(e.getKey()));
+209 }
+210 }
+211 channel.setAssociated(this, permissions);
+212 }
+213
+214 /**
+215 * Evaluate the permissions contributed by the given role for the
+216 * given conlet type.
+217 *
+218 * @param conletType the conlet type
+219 * @param role the role
+220 * @return the sets the
+221 */
+222 @SuppressWarnings("PMD.AvoidBranchingStatementAsLastInLoop")
+223 private Set<Permission> permissionsFromRole(String conletType,
+224 ConsoleRole role) {
+225 var rules = acl.get(role.getName());
+226 if (rules == null) {
+227 // No rules for this role.
+228 return Collections.emptySet();
+229 }
+230 for (var rule : rules) {
+231 // Extract conlet type
+232 int pos = 0;
+233 while (rule.charAt(pos) == '!' || rule.charAt(pos) == '-') {
+234 pos++;
+235 }
+236 if (rule.startsWith("*")) {
+237 return Set.of(Permission.ADD, Permission.RENDER);
+238 }
+239 if (!conletType.equals(rule.substring(pos).trim())) {
+240 // Rule does not apply to this type
+241 continue;
+242 }
+243 if (rule.startsWith("--")) {
+244 return Collections.emptySet();
+245 }
+246 if (rule.startsWith("!") || rule.startsWith("-")) {
+247 return Set.of(Permission.RENDER);
+248 }
+249 // Rule is type name and thus allows everything
+250 return Set.of(Permission.ADD, Permission.RENDER);
+251 }
+252 // Default permissions
+253 return Set.of(Permission.RENDER);
+254 }
+255
+256 /**
+257 * If the request originates from a client
+258 * (see {@link AddConletRequest#isFrontendRequest()}, verifies that
+259 * the user is allowed to create a conlet of the given type.
+260 *
+261 * As the conlets that he user is not allowed to use are disabled,
+262 * it should be impossible to create such requests in the first place.
+263 * However, the frontend code is open to manipulation and therefore
+264 * this additional check is introduced to increase security.
+265 *
+266 * @param event the event
+267 * @param channel the channel
+268 */
+269 @Handler(priority = 1000)
+270 public void onAddConlet(AddConletRequest event, ConsoleConnection channel) {
+271 channel.associated(this, Map.class).ifPresent(aP -> {
+272 @SuppressWarnings("unchecked")
+273 var allPerms = (Map<String, Set<Permission>>) aP;
+274 var perms = allPerms.getOrDefault(event.conletType(),
+275 Collections.emptySet());
+276 if (event.isFrontendRequest() ? !perms.contains(Permission.ADD)
+277 : !perms.contains(Permission.RENDER)) {
+278 event.cancel(true);
+279 }
+280 });
+281 }
+282
+283 /**
+284 * If a role is withdrawn from a user, there may still be conlets in
+285 * his stored layout that he is no longer allowed to use.
+286 *
+287 * @param event the event
+288 * @param channel the channel
+289 */
+290 @Handler(priority = 1000)
+291 public void onRenderConletRequest(RenderConletRequest event,
+292 ConsoleConnection channel) {
+293 channel.associated(this, Map.class).ifPresent(aP -> {
+294 @SuppressWarnings("unchecked")
+295 var allPerms = (Map<String, Set<Permission>>) aP;
+296 var perms = allPerms.getOrDefault(event.conletType(),
+297 Collections.emptySet());
+298 if (perms.isEmpty()) {
+299 event.cancel(true);
+300 // Avoid future rendering of this conlet
+301 fire(new DeleteConlet(event.conletId(),
+302 Collections.emptySet()));
+303 }
+304 });
+305 }
+306}
The root element of the conlet representation.