Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

In this tutorial, we will follow the guideline for developing a plugin to develop our Slack Notification plugin. Please also refer to the very first tutorial วิธีการพัฒนา Bean Shell Hash Variable for more details steps.

1. What is the problem?

We want to send message to Slack to notify user when there is an assignment created for them in Joget Workflow.

2. How to solve the problem?

We will develop an Audit Trail Plugin to send message to Slack

3. What is the input needed for your plugin?

To develop a Slack Notification plugin, we will consider providing the properties options similar to User Notification plugin.

4. What is the output and expected outcome of your plugin?

When an assignment is created for an user, a message with assignment link will send to his/her Slack account based on the configuration.

5. Are there any resources/API that can be reused?

We can use slack-webhook library to integrate with Slack. We can also extend the org.joget.apps.app.lib.UserNotificationAuditTrail to save our time on re-implement similar methods.

6. Prepare your development environment

We need to always have our Joget Workflow Source Code ready and builded by following this guideline

...

Open the maven project with your favourite IDE. I will be using NetBeans.  

7. Just code it!

a. Extending the abstract class of a plugin type

Create a "SlackNotification" class under "org.joget" package. Then, extend the class with org.joget.apps.app.lib.UserNotificationAuditTrail class which is extends org.joget.plugin.base.DefaultAuditTrailPlugin abstract class. Please refer to Audit Trail Plugin. We will need to implement org.joget.plugin.base.PluginWebSupport interface class as well to provide a send test message button in plugin properties page. Please refer to Web Service Plugin.

b. Implement all the abstract methods

As usual, we have to implement all the abstract methods. We will using AppPluginUtil.getMessage method to support i18n and using constant variable MESSAGE_PATH for message resource bundle directory.

...

Code Block
languagexml
    public void webService(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        boolean isAdmin = WorkflowUtil.isCurrentUserInRole(WorkflowUserManager.ROLE_ADMIN);
        if (!isAdmin) {
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
            return;
        }
        
        String action = request.getParameter("action");
        if ("sendTestMessage".equals(action)) {
            String message = "";
            try {
                AppDefinition appDef = AppUtil.getCurrentAppDefinition();
                
                String url = AppUtil.processHashVariable(request.getParameter("url"), null, null, null, appDef);
                String testChannel = AppUtil.processHashVariable(request.getParameter("testChannel"), null, null, null, appDef);
                
                setProperty("apiurl", url);
                setProperty("text", AppPluginUtil.getMessage("SlackWebhookTool.testMessage", getClassName(), MESSAGE_PATH));
                
                if (testChannel != null && !testChannel.isEmpty()) {
                    sendMessage(testChannel, null);
                } else {
                    sendMessage(null, null);
                }
                
                message = AppPluginUtil.getMessage("SlackWebhookTool.sendTestMessage.success", getClassName(), MESSAGE_PATH);
            } catch (Exception e) {
                LogUtil.error(this.getClassName(), e, "Fail to send Test Message to Slack");
                message = AppPluginUtil.getMessage("SlackWebhookTool.sendTestMessage.fail", getClassName(), MESSAGE_PATH) + "\n" + StringEscapeUtils.escapeJavaScript(e.getMessage());
            }
            try {
                JSONObject jsonObject = new JSONObject();
                jsonObject.accumulate("message", message);
                jsonObject.write(response.getWriter());
            } catch (Exception e) {
                //ignore
            }
        } else {
            response.setStatus(HttpServletResponse.SC_NO_CONTENT);
        }
    }

c. Manage the dependency libraries of your plugin

We need to include "jsp-api" and "slack-webhook" libraries in our POM file.

Code Block
        <!-- Change plugin specific dependencies here -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.0</version>
        </dependency>
        <dependency>
            <groupId>net.gpedro.integrations.slack</groupId>
            <artifactId>slack-webhook</artifactId>
            <version>1.1.1</version>
        </dependency>
        <!-- End change plugin specific dependencies here -->

d. Make your plugin internationalization (i18n) ready

We are using i18n message key in getLabel and getDescription method. We will use i18n message key in our properties options definition as well. Then, we will need to create a message resource bundle properties file for our plugin.

...

Code Block
org.joget.SlackNotification.pluginLabel=Slack Notification
org.joget.SlackNotification.pluginDesc=Send notification message to Slack user when an assignment is available.
SlackNotification.config=Configure Slack Notification
SlackNotification.url=Webhook URL
SlackNotification.from=From
SlackNotification.fromUsername=Username
SlackNotification.fromUsername.value=Joget Workflow
SlackNotification.customIcon=Custom Icon
SlackNotification.customIcon.none=None
SlackNotification.customIcon.joget=Joget Workflow Logo
SlackNotification.customIcon.url=Image URL
SlackNotification.customIcon.emoji=Emoji Code
SlackNotification.to=To
SlackNotification.usernameTransform=Transform username to Slack username
SlackNotification.usernameTransform.desc=Hash Variable can be used to transform username to Slack username. Eg. @#form.slack.username[{username}]#
SlackNotification.usernameTransform.value=@{username}
SlackNotification.message=Message
SlackNotification.text=Text
SlackNotification.text.desc=Refer to <a href="https://api.slack.com/docs/formatting" target="_blank">Slack Message Formatting</a>.
SlackNotification.unfurl_links=Unfurling Links
SlackNotification.unfurl_links.desc=Automatically find URLs in a message and create attachments based on the content of those URLs
SlackNotification.unfurl_media=Unfurling Media
SlackNotification.unfurl_media.desc=Automatically find Media URLs in a message and create attachments based on the media of those URLs
SlackNotification.sendTestMessage=Send Test Message
SlackNotification.sendTestMessage.testChannel=Test Channel
SlackNotification.sendTestMessage.success=Test message sent.
SlackNotification.sendTestMessage.fail=Fail to sent test message. Error:
SlackNotification.testMessage=Test Message
SlackNotification.viewAssignment=View Assignment

e. Register your plugin to the Felix Framework

Next, we will have to register our plugin class in the Activator class (Auto generated in the same class package) to tell the Felix Framework that this is a plugin.

Code Block
languagejava
    public void start(BundleContext context) {
        registrationList = new ArrayList<ServiceRegistration>();
        //Register plugin here
        registrationList.add(context.registerService(SlackNotification.class.getName(), new SlackNotification(), null));
    }

f. Build it and test

Let's build our plugin. Once the building process is done, we will find a "slack_notification-5.0.0.jar" file created under "slack_notification/target" directory.

...

When test running a process, the message is received in Slack once a new assignment is created.

8. Take a step further, share it or sell it

You can download the source code from slack_notification_src.zip.

...