在本教程中,我们将遵循开发插件的  指导方针  来开发我们的超链接选项过滤器插件。 有关更多详细信息步骤,请参阅第一个教程  如何开发一个Bean Shell哈希变量插件

1.什么问题?

我们希望有一个类似于以下的过滤器。

2.如何解决问题?

我们将开发一个  Datalist Filter Type插件  来呈现我们的超链接选项过滤器。 

3.你的插件需要什么输入?

要开发一个超链接选项过滤器插件,我们将需要提供一些输入如下。

  1. 要作为链接填充的选项
  2. 是否显示每个选项的数据数量。

4.你的插件的输出和预期结果是什么?

超链接列表将列出所有选项及其数据计数。当点击超链接时将过滤数据列表。

5.是否有任何资源/ API可以重复使用?

请参阅数据  滤波器类型插件

6.准备你的开发环境

我们需要始终准备好Joget Workflow Source Code,并按照这个指导方针建立起来  。 

下面的教程是用Macbook Pro编写的,Joget源代码是5.0.1版本。 其他平台命令请参阅  如何开发插件文章。

假设我们的文件夹目录如下所示。 

- Home
  - joget
    - plugins
    - jw-community
      -5.0.1

“插件”目录是我们将创建和存储我们所有插件的文件夹,“jw-community”目录是Joget Workflow源代码的存储位置。

运行以下命令在“plugins”目录下创建一个maven项目。

 

cd joget/plugins/
~/joget/jw-community/5.0.1/wflow-plugin-archetype/create-plugin.sh org.joget hyperlink_options_filter 5.0.1

 

然后,shell脚本会要求我们输入插件的版本号,并在生成maven项目之前要求我们确认。

 

Define value for property 'version':  1.0-SNAPSHOT: : 5.0.0
[INFO] Using property: package = org.joget
Confirm properties configuration:
groupId: org.joget
artifactId: hyperlink_options_filter
version: 5.0.0
package: org.joget
Y: : y



我们应该在终端上显示“BUILD SUCCESS”消息,并在“plugins”文件夹中创建一个“hyperlink_options_filter”文件夹。

用你最喜欢的IDE打开maven项目。我将使用  NetBeans。  

7. 开始编码吧!

a. 扩展插件类型的抽象类

在“org.joget”包下创建一个“HyperlinkOptionsFilter”类。然后,使用org.joget.apps.datalist.model.DataListFilterTypeDefault 抽象类来扩展  该类。请参阅  Datalist Filter Type插件

b. 实现所有的抽象方法

像往常一样,我们必须执行所有的抽象方法。我们将使用AppPluginUtil.getMessage方法来支持i18n,并使用常量变量MESSAGE_PATH作为消息资源包目录。

package org.joget;

import org.joget.apps.app.service.AppPluginUtil;
import org.joget.apps.app.service.AppUtil;
import org.joget.apps.datalist.model.DataListFilterTypeDefault;

public class HyperlinkOptionsFilter extends DataListFilterTypeDefault {
    private final static String MESSAGE_PATH = "message/HyperlinkOptionsFilter";
    
    public String getName() {
        return "Hyperlink Options Filter Type";
    }
 
    public String getVersion() {
        return "5.0.0";
    }
 
    @Override
    public String getLabel() {
        //support i18n
        return AppPluginUtil.getMessage("org.joget.HyperlinkOptionsFilter.pluginLabel", getClassName(), MESSAGE_PATH);
    }
 
    @Override
    public String getDescription() {
        //support i18n
        return AppPluginUtil.getMessage("org.joget.HyperlinkOptionsFilter.pluginDesc", getClassName(), MESSAGE_PATH);
    }
 
    public String getClassName() {
        return this.getClass().getName();
    }
 
    public String getPropertyOptions() {
        return AppUtil.readPluginResource(getClass().getName(), "/properties/hyperlinkOptionsFilter.json", null, true, MESSAGE_PATH);
    }
}

现在,我们必须为管理员用户创建一个UI,为我们的插件提供输入。在getPropertyOptions方法中,我们已经指定了我们的  插件属性选项和配置  定义文件位于“/properties/hyperlinkOptionsFilter.json”。让我们在“hyperlink_options_filter / src / main”目录下创建一个目录“resources / properties”。创建目录后,在“properties”文件夹中创建一个名为“hyperlinkOptionsFilter.json”的文件。

在属性定义选项文件中,我们需要提供如下的选项。请注意,我们可以在我们的属性选项中使用“@@ message.key @@”语法来支持i18n。

[{
    title : '@@HyperlinkOptionsFilter.config@@',
    properties : [{
        name:'defaultValue',
        label:'@@HyperlinkOptionsFilter.defaultValue@@',
        type:'textfield'
    },
    {
        name : 'showLabel',
        label : '@@HyperlinkOptionsFilter.showLabel@@',
        type : 'checkbox',
        options : [{
            value : 'true',
            label : ''
        }]
    },
    {
        name : 'displayFull',
        label : '@@HyperlinkOptionsFilter.displayFull@@',
        type : 'checkbox',
        value : 'true',
        options : [{
            value : 'true',
            label : ''
        }]
    },
    {
        name : 'showCount',
        label : '@@HyperlinkOptionsFilter.showCount@@',
        type : 'checkbox',
        value : '',
        options : [{
            value : 'true',
            label : ''
        }]
    },
    {
        name : 'options',
        label : '@@HyperlinkOptionsFilter.options@@',
        type : 'grid',
        columns : [{
            key : 'value',
            label : '@@HyperlinkOptionsFilter.value@@'
        },
        {
            key : 'label',
            label : '@@HyperlinkOptionsFilter.label@@'
        }]
    }]
},
{
    title : '@@HyperlinkOptionsFilter.chooseOptionsBinder@@',
    properties : [{
        name : 'optionsBinder',
        label : '@@HyperlinkOptionsFilter.optionsBinder@@',
        type : 'elementselect',
        options_ajax : '[CONTEXT_PATH]/web/property/json/getElements?classname=org.joget.apps.form.model.FormLoadOptionsBinder',
        url : '[CONTEXT_PATH]/web/property/json[APP_PATH]/getPropertyOptions'
    }]
}]

完成属性选项以收集输入后,我们可以使用getTemplate和getQueryObject方法来处理插件的主要方法。在getTemplate方法中,我们将根据配置的插件属性检索选项和计数。

    public String getTemplate(DataList datalist, String name, String label) {
        PluginManager pluginManager = (PluginManager) AppUtil.getApplicationContext().getBean("pluginManager");
        Map dataModel = new HashMap();
        
        dataModel.put("element", this);
        dataModel.put("name", datalist.getDataListEncodedParamName(DataList.PARAMETER_FILTER_PREFIX+name));
        dataModel.put("label", label);
        
        Map<String, String> options = getOptionMap();
        if ("true".equalsIgnoreCase(getPropertyString("showCount"))) {
            DataListBinder binder = datalist.getBinder();
            for (String key : options.keySet()) {
                DataListFilterQueryObject filter = getQueryObject(datalist, name, key);
                int count = 0;
                if (binder != null) {
                    if (filter != null) {
                        count = binder.getDataTotalRowCount(datalist, binder.getProperties(), new DataListFilterQueryObject[]{filter});
                    } else {
                        count = binder.getDataTotalRowCount(datalist, binder.getProperties(), new DataListFilterQueryObject[]{});
                    }
                }
                
                options.put(key, options.get(key) + " (" + count + ")");
            }
        }
        
        String value = getValue(datalist, name, getPropertyString("defaultValue"));
        dataModel.put("value", value);
        dataModel.put("options", options);
            
        return pluginManager.getPluginFreeMarkerTemplate(dataModel, getClassName(), "/templates/hyperlinkOptionsFilter.ftl", null);
    }
    
    protected Map<String, String> getOptionMap() {
        Map<String, String> optionMap = new ListOrderedMap();
        
        // load from "options" property
        Object[] options = (Object[]) getProperty(FormUtil.PROPERTY_OPTIONS);
        for (Object o : options) {
            Map option = (HashMap) o;
            Object value = option.get(FormUtil.PROPERTY_VALUE);
            Object label = option.get(FormUtil.PROPERTY_LABEL);
            if (value != null && label != null) {
                optionMap.put(value.toString(), label.toString());
            }
        }
        // load from binder if available
        Map optionsBinderProperties = (Map) getProperty("optionsBinder");
        if (optionsBinderProperties != null && optionsBinderProperties.get("className") != null && !optionsBinderProperties.get("className").toString().isEmpty()) {
            PluginManager pluginManager = (PluginManager) AppUtil.getApplicationContext().getBean("pluginManager");
            FormBinder optionBinder = (FormBinder) pluginManager.getPlugin(optionsBinderProperties.get("className").toString());
            if (optionBinder != null) {
                optionBinder.setProperties((Map) optionsBinderProperties.get("properties"));
                FormRowSet rowSet = ((FormLoadBinder) optionBinder).load(null, null, null);
                if (rowSet != null) {
                    optionMap = new ListOrderedMap();
                    for (FormRow row : rowSet) {
                        Iterator<String> it = row.stringPropertyNames().iterator();
                        // get the key based on the "value" property
                        String value = row.getProperty(FormUtil.PROPERTY_VALUE);
                        if (value == null) {
                            // no "value" property, use first property instead
                            String key = it.next();
                            value = row.getProperty(key);
                        }
                        // get the label based on the "label" property
                        String label = row.getProperty(FormUtil.PROPERTY_LABEL);
                        if (label == null) {
                            // no "label" property, use next property instead
                            String key = it.next();
                            label = row.getProperty(key);
                        }
                        optionMap.put(value, label);
                    }
                }
            }
        }
        
        if (!optionMap.containsKey("")) {
            Map<String, String> tempOptionMap = new ListOrderedMap();
            tempOptionMap.put("", AppPluginUtil.getMessage("HyperlinkOptionsFilter.all", getClassName(), MESSAGE_PATH));
            tempOptionMap.putAll(optionMap);
            optionMap = tempOptionMap;
        }
        
        return optionMap;
    }
    
    protected DataListFilterQueryObject getQueryObject(DataList datalist, String name, String value) {
        DataListFilterQueryObject queryObject = new DataListFilterQueryObject();
        if (datalist != null && datalist.getBinder() != null && value != null && !value.isEmpty()) {
            String columnName = datalist.getBinder().getColumnName(name);
            List<String> valuesList = new ArrayList<String>();
            String query = "("+columnName+" = ? or "+columnName+" like ? or "+columnName+" like ? or "+columnName+" like ?)";
            valuesList.add(value);
            valuesList.add(value + ";%");
            valuesList.add("%;" + value + ";%");
            valuesList.add("%;" + value);
            queryObject.setOperator(DataListFilter.OPERATOR_AND);
            queryObject.setQuery(query);
            queryObject.setValues(valuesList.toArray(new String[0]));
            return queryObject;
        }
        return null;
    }
 
    public DataListFilterQueryObject getQueryObject(DataList datalist, String name) {
        String value = getValue(datalist, name, getPropertyString("defaultValue"));
        return getQueryObject(datalist, name, value);
    }

在getTemplate中,我们指定模板文件为“hyperlinkOptionsFilter.ftl”。在“hyperlink_options_filter / src / main / resources / templates”目录下创建这个文件。然后,使用  FreeMaker  语法来构建我们的模板,如下所示:

<div id="${name!}_container" style="display:none;margin:5px 0;">
    <input id="${name!}" name="${name!}" type="hidden" value="${value!?html}" />
    <#if element.properties.showLabel! == "true" >
        <label><strong>${label!?html} :</strong></label>&nbsp;&nbsp;
    </#if>
    <#list options?keys as key>
        <a ref="${key?html}" href="${key?html}" class="<#if value! == key >active</#if>"><span><#if value! == key ><strong></#if>${options[key]!?html}<#if value! == key ></strong></#if></span></a>&nbsp;&nbsp;
    </#list>
    <script type="text/javascript">
        $(document).ready(function(){
            <#if element.properties.displayFull! == "true" >
                $('#${name!}_container').insertBefore($('#${name!}_container').closest(".filters"));
            </#if>
            $('#${name!}_container').show();
            $('#${name!}_container a').click(function(){
                var value = $(this).attr("ref");
                $(this).parent().find("input").val(value);
                $(this).closest("form").submit();
                return false;
            });
        });
    </script>
</div>

c. 管理你的插件的依赖库

我们需要在我们的POM文件中包含“commons-collections”库。

        <!-- Change plugin specific dependencies here -->
        <dependency>
            <groupId>commons-collections</groupId>
            <artifactId>commons-collections</artifactId>
            <version>3.2.1</version>
        </dependency>
        <!-- End change plugin specific dependencies here -->

d. 让你的插件国际化(国际化)准备就绪

我们在getLabel和getDescription方法中使用i18n消息密钥。我们将在我们的属性选项定义中使用i18n消息密钥。然后,我们将需要为我们的插件创建一个消息资源包属性文件。

在“hyperlink_options_filter / src / main”目录下创建一个目录“resources / message”。然后,在该文件夹中创建一个“HyperlinkOptionsFilter.properties”文件。在属性文件中,添加所有消息密钥及其标签,如下所示。

org.joget.HyperlinkOptionsFilter.pluginLabel=Hyperlink Options
org.joget.HyperlinkOptionsFilter.pluginDesc=Show options as Hyperlink to perform filter.
HyperlinkOptionsFilter.all=All
HyperlinkOptionsFilter.config=Configure Hyperlink Options Filter
HyperlinkOptionsFilter.options=Options
HyperlinkOptionsFilter.value=Value
HyperlinkOptionsFilter.label=Label
HyperlinkOptionsFilter.chooseOptionsBinder=Choose Options Binder
HyperlinkOptionsFilter.optionsBinder=Options Binder
HyperlinkOptionsFilter.defaultValue=Default Value
HyperlinkOptionsFilter.showCount=Show Data Count?
HyperlinkOptionsFilter.displayFull=Display in full width (Above other filters)
HyperlinkOptionsFilter.showLabel=Show label?

e. 注册你的插件到Felix框架

接下来,我们将需要在Activator类(在同一个类包中自动生成)中注册我们的插件类,以告诉Felix框架这是一个插件。

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

f. 建立它并测试

让我们建立我们的插件。构建过程完成后,我们将在“hyperlink_options_filter / target”目录下找到一个“hyperlink_options_filter-5.0.0.jar”文件。

然后,让我们上传插件jar到  管理插件。上传jar文件后,再次检查插件是否正确上传并激活。

然后,检查“超链接选项”过滤器在“列表设计器.”中显示为过滤器类型的选择。

配置其属性。

保存属性并检查过滤器是否在画布中呈现,如下所示

检查并测试datalist中的过滤器。

8. 再进一步,分享或出售

您可以从hyperlink_options_filter_src.zip下载源代码  。

要下载现成的插件jar,请在http://marketplace.joget.org/上找到它  。(快来了)