ในบทช่วยสอนนี้ เราจะทำตาม guideline for developing a plugin เพื่อพัฒนาปลั๊กอิน Hyperlink Options Filter. โปรดอ้างอิงบทช่วยสอน วิธีการพัฒนา Bean Shell Hash Variable สำหรับรายละเอียดเพิ่มเติมขั้นต่อไป
เราต้องการมี filter similar เพื่อการติดตาม

2. วิธีแก้ปัญหา?
เราจะพัฒนา ปลั๊กอินประเภทตัวกรองข้อมูล (Datalist Filter Type Plugin) เพื่อแสดง Filter Hyperlink Options ของเรา
ในการพัฒนาปลั๊กอินตัวกรองไฮเปอร์ลิงก์ตัวเลือกเราจะต้องให้อินพุตบางอย่างดังต่อไปนี้
รายการไฮเปอร์ลิงก์ซึ่งจะแสดงรายการตัวเลือกทั้งหมดพร้อมนับข้อมูล เมื่อคลิกที่การเชื่อมโยงหลายมิติจะกรองข้อมูล

อ้างอิงถึง ปลั๊กอินประเภทตัวกรองข้อมูล (Datalist Filter Type Plugin).
เราจำเป็นต้องให้ซอร์สโค้ด Joget Workflow ของเราพร้อมและสร้างโดยทำตาม this guideline.
บทช่วยสอนนี้จัดทำโดย Macbook Pro และ the Joget Source Code is version 5.0.1. โปรดอ้างอิงถึง แนวทางสำหรับการพัฒนาปลั๊กอิน สำหรับคำสั่งแพลตฟอร์มอื่น ๆ
สมมติว่าไดเรกทอรีโฟลเดอร์ของเรามีดังนี้
- Home
- joget
- plugins
- jw-community
-5.0.1 |
ไดเรกทอรี "ปลั๊กอิน" คือโฟลเดอร์ที่เราจะสร้างและจัดเก็บปลั๊กอินของเราทั้งหมดและไดเรกทอรี "jw-community" เป็นที่เก็บซอร์สโค้ด Joget Workflow
เรียกใช้คำสั่งต่อไปนี้เพื่อสร้างโครงการ 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 script จะขอให้เราใส่หมายเลขเวอร์ชันสำหรับปลั๊กอินและขอให้เรายืนยันก่อนที่จะสร้างโครงการ 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" ที่ปรากฏในเครื่องของเราและโฟลเดอร์ "hyperlink_options_filter" ที่สร้างขึ้นในโฟลเดอร์ "ปลั๊กอิน"
เปิดโครงการ maven ด้วย IDE ที่คุณชื่นชอบ เราแนะนำให้ใช้ NetBeans.
Create a "HyperlinkOptionsFilter" class under "org.joget" package. Then, extend the class with org.joget.apps.datalist.model.DataListFilterTypeDefault abstract class. Please refer to ปลั๊กอินประเภทตัวกรองข้อมูล (Datalist Filter Type Plugin).
เช่นเคย เราจะต้องใช้ abstract methods ทั้งหมด. เราจะใช้ AppPluginUtil.getMessage method เพื่อสนับสนุน i18n และใช้ตัวแปรคงที่ MESSAGE_PATH สำหรับ message resource bundle directory.
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 method, เราได้ระบุไฟล์ ตัวเลือกคุณสมบัติปลั๊กอิน ของเราไว้ที่ "/properties/hyperlinkOptionsFilter.json". ให้สร้าง directory "resources/properties" ภายใต้ "hyperlink_options_filter/src/main" directory. หลังจากสร้าง directory, สร้างไฟล์ชื่อ "hyperlinkOptionsFilter.json" ในโฟลเดอร์ "properties"
ในไฟล์ตัวเลือกคุณสมบัติ เราจะต้องกำหนดตัวเลือกดังต่อไปนี้. โปรดทราบว่าเราสามารถใช้ "@@message.key@@" syntax เพื่อสนับสนุน 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 syntax เพื่อสร้างเทมเพลตของเราดังต่อไปนี้:
<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>
</#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>
</#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> |
เราจะต้องรวม "commons-collections" library ในไฟล์ POM ของเรา
<!-- 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 --> |
เรากำลังใช้ i18n message key ใน getLabel and getDescription method. เราใช้ i18n message key ในไฟล์ตัวเลือกคุณสมบัติของเราเช่นกัน จากนั้นเราจะต้องสร้างไฟล์ตัวเลือก message resource bundle เพื่อปลั๊กอินของเรา
สร้าง directory, "resources/message", ภายใต้ "hyperlink_options_filter/src/main" directory. จากนั้นสร้างไฟล์ "HyperlinkOptionsFilter.properties" ในโฟลเดอร์. ในไฟล์คุณสมบัติให้เพิ่ม message keys และ label ทั้งหมดดัต่อไปนี้.
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? |
ต่อไปเราจะต้องลงทะเบียนคลาสปลั๊กอินของเราในคลาส Activator (สร้างอัตโนมัติในแพ็คเกจคลาสเดียวกัน) เพื่อบอก Felix Framework ว่านี่เป็นปลั๊กอิน
public void start(BundleContext context) {
registrationList = new ArrayList<ServiceRegistration>();
//Register plugin here
registrationList.add(context.registerService(HyperlinkOptionsFilter.class.getName(), new HyperlinkOptionsFilter(), null));
} |
มาสร้างปลั๊กอินของเรากัน เมื่อกระบวนการสร้างเสร็จสิ้นเราจะพบไฟล์ "hyperlink_options_filter-5.0.0.jar" ที่สร้างขึ้นภายใต้ไดเรกทอรี "hyperlink_options_filter / target"
จากนั้นลองอัปโหลด Manage Plugins. หลังจากอัปโหลดไฟล์ jar ตรวจสอบอีกครั้งว่ามีการอัปโหลดและเปิดใช้งานปลั๊กอินอย่างถูกต้อง
จากนั้นตรวจสอบว่าตัวเลือกไฮเปอร์ลิงค์นั้นแสดงเป็นตัวเลือกประเภทตัวกรองใน ตัวสร้างดาตาลิสต์ (Datalist Builder).

กำหนดค่าคุณสมบัติ

บันทึกคุณสมบัติและตรวจสอบว่าตัวกรองนั้นสร้างขึ้นในแคนวาสดังนี้

ตรวจสอบและทดสอบตัวกรองใน datalist

คุณสามารถดาวน์โหลด source code จาก hyperlink_options_filter_src.zip.
หากต้องการดาวน์โหลด jar ปลั๊กอินที่พร้อมใช้งานโปรดค้นหา http://marketplace.joget.org/. (เร็วๆ นี้)