This is an internal documentation. There is a good chance you’re looking for something else. See Disclaimer.
Legacy Actions¶
Warning
No longer create new legacy actions if anyhow possible. Many use cases can be represented by Simple Actions now.
Actions may be used to add custom functionality to list or detail pages. Actions are created in Java-Script by extending the AbstractEntityExplorerAction
.
Most legacy actions communicate with the backend using DWR Java Services.
Actions are usually part of the “Actions” menu of the respective form.
The following components are commonly used in ExtJs actions.
JavaScript that will be executed when the action is executed
DWR-Communication that is used to interact with the java backend
Standard-Integration amendment to define where the action is displayed
ACL to define who may use this action
contributions to register javascript and dwr serivces
Dependencies¶
build.gradle¶
Support for DWR services is contained by the netui
module. This dependency can be declared like this:
dependencies {
implementation project(":core:netui")
}
Note
This dependency might already be transitively included by another dependency. An api
dependency might be necessary
if the service is in an exported package.
JavaScript¶
Actions are written in ExtJs javascript. The action javascript will usually be stored in the resources/resources/webapp/js
folder
of the module.
Every javascript file needs to be registered in the @Configuration
class of the module, so that it can be loaded by the JavaScriptServlet
:
@Bean
public JavaScriptContribution tasksJavaScriptContribution() {
return new JavaScriptContribution("nice2.tasks", currentModule(), findModelResources(
"resources/webapp/js/nice2/tasks/TriggerBatchjobAction.js",
"resources/webapp/js/nice2/tasks/UpdateDueTimeAction.js",
"resources/webapp/js/nice2/tasks/CancelTaskAction.js",
"resources/webapp/js/nice2/tasks/DeleteTaskAction.js",
"resources/webapp/js/nice2/tasks/progress/TaskProgressGuiManager.js",
"resources/webapp/js/nice2/tasks/progress/TaskProgressContextMenu.js",
"resources/webapp/js/nice2/tasks/progress/TaskProgressReceiver.js",
"resources/webapp/js/nice2/tasks/progress/UpdateProgressRemoteClientAction.js"
));
}
If a custom javascript package is used, this needs to be registered as well (only once):
@Bean
public JavaScriptModuleContribution tasksJavaScriptModuleContribution() {
return new JavaScriptModuleContribution("nice2-admin", currentModule(), List.of("nice2.tasks"));
}
Please find below an example of a simple JavaScript action that calls a dwr service to process the selection.
Ext.ns('nice2.optional.test');
nice2.optional.test.TestAction = Ext.extend(nice2.modules.entityExplorer2.actions.AbstractEntityExplorerAction, {
_doPerform: function() {
this.requireSingleSelection();
nice2.netui.dwr.RemoteService.call({
remoteService: 'nice2_optional_test_TestService',
method: 'testMethod',
args: [ this.getSelection() ],
mask: false,
scope: this,
successMessage: getText('actions.test.TestAction.success'),
success: Ext.emptyFn
});
}
});
NetuiActionRegistry.register('nice2.optional.test.TestAction', nice2.optional.test.TestAction);
AbstractEntityExplorerAction
contains various utility functions that may be used when creating an action:
getSelection()
returns the current selection. This selection may directly be sent to a DWR servicerequireSingleSelection()
this method may be called to evaluate the selection. If not exactly one entity is selected, an error will be shown.requireSingleOrNoSelection()
requires the selection to be 0 or 1 entityrequireSelectionWithMaximum(max)
requires the selection to be between0
andmax
entitiesrequireSingleOrUpToMaxSelection(max)
requires the selection to be between1
andmax
entitiescreateAutoHeightWindow()
can be used to create an extjs windowcreateFormWindow()
can be used to create an extjs window that renders a form
DWR-Communication¶
DWR is used to connect the ExtJs frontend with our java backend. If an action requires any backend interaction, i.e. if any data should be checked or amended, it will be done using DWR.
Java-Script¶
Accessing DWR services in javascript may be done using the nice2.netui.dwr.RemoteService.call
method as followed.
nice2.netui.dwr.RemoteService.call({
remoteService: 'nice2_optional_test_TestService',
method: 'testMethod',
args: [ this.getSelection() ],
mask: false,
scope: this,
successMessage: getText('actions.test.TestAction.success'),
success: function(response) {
}
});
In this example the method testMethod
of the DWR Service nice2_optional_test_TestService
is called with the current
selection as parameter.
Java¶
If a DWR service is created exclusively to be called from an action, it usually will be created in an actions
package. To
create a DWR service the methods to be called must be defined in an interface and its implementation is usually in a corresponding
Impl
class right next to it (unfortunately a DWR service always requires an interface even if it’s only referenced by a JS file):
Please find below a minimal example with a method that takes a selection and returns a string
Interface:
public interface TestActionService {
String testDwrMethod(EntityExplorerActionSelection selection);
}
Implementation:
@DwrService(name = "nice2_optional_test_TestActionService", interfaceClass = TestActionService.class)
public class TestActionServiceImpl implements TestActionService {
private final EntityExplorerActionSelectionService selectionService;
public TestActionServiceImpl(EntityExplorerActionSelectionService selectionService) {
this.selectionService = selectionService;
}
@Override
String testDwrMethod(EntityExplorerActionSelection selection) {
EntityList entities = selectionService.getSelectionEntityList(selection);
//method content
return "Test";
}
}
A DWR service must be annotated with @DwrService
. The parameter name
defines the name that needs to be used in the
javascript to reference the service. In addition the interface class needs to be specified as well.
Data-Types¶
DWR by default only supports basic Data-Types e.g String
, int
or long
and simple Collections of those. This means
that for example Entity
objects cannot be passed as a return value to the ExtJs frontend. If a method needs to return multiple
values (e.g. multiple fields of an entity) Maps
are commonly used.
Warning
An outbound java List
will be converted to a javascript array. An inbound javascript array will be converted to a java array.
Return values of dwr methods can be Collections
, parameters can only be simple types, custom beans or arrays of those.
@Override
Map<String, String> testDwrMethod(String[] params) {
//method content
return null
}
Custom Beans¶
If an object of multiple values needs to be passed to the frontend or from the frontend to a DWR service this can be achieved by creating a custom bean.
Java Bean
public class MembershipBean {
private String key;
private String email;
private String currentUserKey;
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getCurrentUserKey() {
return currentUserKey;
}
public void setCurrentUserKey(String currentUserKey) {
this.currentUserKey = currentUserKey;
}
}
A converter needs to be configured in the @Configuration
class for each custom DWR bean:
@Bean
public ConverterContribution membershipBeanConverter() {
BeanConverter converter = new BeanConverter();
converter.setJavascript("nice2.optional.membership.MembershipBean");
return new ConverterContribution(converter, MembershipBean.class.getName());
}
Bean in JavaScript
var bean = new nice2.customer.sps.MembershipBean();
Ext.apply(bean, {
key: recipient.key,
email: recipient.email,
currentUserKey: recipient.currentUserKey
});
Standard-Integration¶
Adding an action to a XML form is as easy defining a new <action>
in the corresponding action group (usually actiongroup_actions
)
<action name="actiongroup_actions" label="actiongroup.actions" icon="cog_go">
<action path="nice2.optional.test.TestAction" label="actions.test_action.title" icon="email_open"/>
</action>
path the path of the action. corresponds with the service point.
label The label of this action (textresource key).
icon the icon of the action.
group the action group to put this action in.
default a default action can be defined for a group. If defined, the group will be displayed as split button where this action is directly executable and the other actions in the group will be displayed in the dropdown menu.
showConfirmMessage if
true
, a confirmation box will be displayed before the action is executed.confirmationText the text resource key for the text of the confirmation box. Only will be considered if the
showConfirmationMessage
attribute is set totrue
.
ACL¶
To grant access to a created action, a netuiactions
acl rule has to be created in the action.acl
file. If no
action.acl
exists it may be created and added to the module.acl
(include 'action.acl';
).
netuiactions("nice2.optional.test.TestAction"):
grant netuiPerform to configurator;
ActionFactories¶
If an action needs to be dynamically added to multiple forms, if it needs to be added to each row or if you need to overwrite
a standard action (e.g. New
) custom ActionFactories
may be created as shown below.
@Component
public class SingleMarkActionFactory extends AbstractActionFactory {
private final Context ctx;
public SingleMarkActionFactory(ActionsBuilder actionBuilder,
Context ctx) {
super(actionBuilder);
this.ctx = ctx;
}
/**
* Create a new {@link ActionGroupModel} for the specified situation.
*
* @return <code>null</code> if this factory provides no actions for this situation.
*/
@Nullable
@Override
public Collection<ActionModel> createActions(Situation situation) {
if(and(instanceOf(EntityNameSituation.class), not(isScreen("explorer-modal"))).apply(situation)) {
EntityManager markEM = ctx.getEntityManager(((EntityNameSituation) situation).getEntityName());
EntityModel markEntityModel = (EntityModel) markEM.getModel();
if (markEntityModel != null && markEntityModel.isMarkable()) {
if (isScope("update").apply(situation) && situation instanceof EntitySituation) {
return createMarkSingleAction((EntitySituation) situation, markEM, markEntityModel);
} else if (isScope("list").apply(situation)) {
return createMarkMultipleAction();
}
}
}
return null;
}
private Collection<ActionModel> createMarkSingleAction(EntitySituation situation,
EntityManager markEM,
EntityModel markEntityModel) {
ActionModel markAction = actionBuilder.newAction("nice2.marking.MarkSingleAction", "nice2.marking.MarkSingleAction");
markAction.setEnabled(true);
markAction.setIcon(getMarkMultipleIcon(situation, markEM, markEntityModel));
markAction.setShortDescription("actions.mark.markBox");
markAction.setName("");
return asCollection(markAction);
}
private String getMarkMultipleIcon(EntitySituation situation,
EntityManager markEM,
EntityModel markEntityModel) {
PrimaryKey key = PrimaryKey.createPrimary(markEntityModel, situation.getPrimaryKey());
Entity entity = markEM.get(key);
if(!entity.resolve("relMark").execute().isEmpty()) {
return "star";
} else {
return "star_grey";
}
}
private Collection<ActionModel> createMarkMultipleAction() {
ActionGroupModel actionGroupModel = actionBuilder.getNiceGroup(ActionsBuilder.NiceGroup.ACTIONS);
ActionModel markMultipleAction = actionBuilder.newAction("nice2.marking.MarkMultipleAction", "actions.marking.MarkMultipleAction");
markMultipleAction.setEnabled(true);
markMultipleAction.setIcon("star_half_grey");
actionGroupModel.addAction(markMultipleAction);
return asCollection(actionGroupModel);
}
}
Action factories are discovered automatically as long as they are annotated with @Component
.
How to enable legacy actions in the new client¶
Legacy actions are not enabled by default in the new client.
Set the application property nice2.forms.legacyActionsEnabled=true
to enable them.
If legacy actions are enabled, disable specific legacy actions by adding them to the configuration point
nice2.model.form.DisabledLegacyActions
(e.g. once they’ve been reimplemented with React).
Customer legacy actions aren’t enabled automatically even if nice2.forms.legacyActionsEnabled=true
is set.
You need to whitelist them additionally to the application property via the configuration point
nice2.model.form.EnabledCustomerLegacyActions
.